S02_CH14_ EMIO_OLED 实验

S02_CH14_ EMIO_OLED 实验

本章将使用EMIO模拟OLED的时序来驱动OLED,本方案对米联系列Miz702,Miz702N和Miz701N全兼容。

14.1板载OLED硬件原理

Miz系列开发板板载OLED的型号是UG-2832HSWEG04(Miz701N为UG-2864HSWEG04),分辨率为128*32(Miz701N为128*64),接口类型为4线SPI,控制芯片为SSD1306。本小节,首先简要分析开发板OLED相关的硬件电路,然后对SSD1306控制器进行介绍,为后续的驱动开发做好铺垫。

14.1.1 硬件电路简析

Miz702和Miz702N OLED接口电路如下图所示。

wps6F69.tmp

Miz701N OLED接口电路如下图所示。

wps6F7A.tmp

关键引脚具体说明如下表所示。

引脚名称

详细描述

SCLK

串行时钟线。总线上的数据传输是通过时钟驱动的。每个bit的传输都发生在SCLK的上升沿。

SDIN

串行数据线。输入数据(MSB最先传输)在SCLK上升沿被锁存,在最后一个时钟周期将8位串行数据转换为一个byte的并行数据。

D/C

数据/命令控制。高电平表示总线上传输的是数据,低电平表示总线上传输的是命令。

RES

复位信号。该信号被拉低时,芯片执行复位操作。

CS

片选信号。低电平有效。

VCC

面板驱动电压源。

VDD

控制器电压源。

VSS

地线。

VBAT

内部DC/DC电压转换器供电电源。

从原理图中可以看出片选信号CS通过电阻短接到GND,因此该信号是一直有效的;OLED-RES、OLED-DC、OLED-SCLK、OLED-SDIN直接连接到Zynq GPIO,其中RES和DC信号低电平有效;PIN7 VDD和PIN5 VBAT是高电平有效的,但是并非直接连接至Zynq GPIO,而是通过PMOS管进行驱动。根据PMOS管的导通特性可以知道,当OLED_VBAT和OLED-VDD为低电平时,3.3V的电压才会送到VBAT和VDD,换句话说,对于Zynq而言,VBAT和BDD是低电平有效。市面上大多是将VBAT和VDD直接连接到高电平,这样一来不需要额外的控制,但是功耗也相对高一些。Miz702和Miz702N开发板将VBAT和VDD连接到Zynq GPIO,可以通过软件控制OLED的通、断电,可以降低整个板子的功耗。

14.1.2 SSD1306简介

SSD1306是一块内置CMOS OLED/PLED驱动控制器的IC芯片,芯片可以驱动共阴型OLED面板。芯片内部包含晶振、显示RAM、对比度控制模块以及256级亮度控制模块,大大降低了外围元器件数量和功耗。MCU可以通过6800/8000并行接口,I2C接口或者SPI接口实现对SSD1306的控制。

板载OLED接口为4线串行(SPI)方式,工作在模式下,需要注意的地方有以下几点:

- 使用的信号有以下几个:CS,RES,DC,SCLK,SDIN,各信号作用请参照上一小节,此处不再重复。

- 只能往模块写数据而不能读数据。

- 每个数据长度均为8位,在SCLK的上升沿,数据从SDIN移入到SSD1306,并且是高位在前的。

- 写操作的时序如下图所示。

wps6F7B.tmp

4线SPI模式就介绍到这里,时序图是十分重要的,驱动程序和SPI相关的函数就是对这个时序图设计的“翻译”。读者在为自己的项目设计电路时,如果用到其他几种接口方式,请自行阅读SSD1306数据手册。

接下来,我们介绍一下模块的显存,SSD1306的显存总共为128*64bit大小,SSD1306将这些显存分为了8页,其对应关系如下:

wps6F7C.tmp

可以看出,SSD1306的每页包含了128个字节,总共8页,这样刚好是128*64的点阵大小。

14.2 OLED驱动开发思路解析

14.2.1 SPI接口

Zynq和OLED通过SPI总线连接,想要实现对OLED的控制,就必须按照SPI接口规范完成数据的传输,相应的我们在驱动实现时要设计出SPI接口函数。主要接口函数有以下几个:

- 写命令

- 写数据

这部分实现难度不大,在驱动实现基础篇参考源码,再结合18.3.2的时序图,很容易就能够理解。

14.2.2 SSD1306控制

对SSD1306的控制是通过SPI接口实现的,实现了基本的写命令和写数据操作之后,就可以轻松地完成SSD1306的控制,常用的控制函数有:

- SSD1306初始化,初始化流程如下图所示:

wps6F8C.tmp

- 开启显示

- 关闭显示

在实现SSD1306的控制之前,有必要了解SSD1306常用控制命令,命令分为两种,一种是单字节命令;另一种是非单字节指令,第一个字节是命令字,接下来的一个或多个字节是配置项。现将命令按使用类型分类描述如下:

命令表单(D/C#=0, R/W#(WR#) = 0, E(RD#=1) 特殊状态除外)

1、基本命令

D/C

Hex

D7

D6

D5

D4

D3

D2

D1

D0

命令

描述

0

81

A[7:0]

1

A7

0

A6

0

A5

0

A4

0

A3

0

A2

0

A1

1

A0

设置对比度

双字节命令,1~256级对比度可选,对比度随值增加。

(复位值 = 0x7f)

0

A4/A5

1

0

0

0

0

1

0

X0

全部显示开

A4h,X0 = 0 :恢复内存内容显示(默认),输出内存中的内容

A5h,X0 = 1 :开显示,输出无视内存的内容

0

A6/A7

1

0

0

0

0

1

1

X0

设置正常/逆显示

A6,X[0]= 0:正常显示(默认)

RAM为0:显示面板关

RAM为1:显示面板开

A7 X[0]= 1:逆显示

RAM为0:显示面板开

RAM为1:显示面板关

0

AE/AF

1

0

0

0

1

1

1

X0

设置显示开/关

AE:X[0]= 0:关显示(默认)

AF:X[0]= 1:在正常模式显示

D/C

Hex

D7

D6

D5

D4

D3

D2

D1

D0

命令

描述

0

0

0

0

0

0

0

26/27

A[7:0]

B[2:0]

C[2:0]

D[2:0]

E[7:0]

F[7:0]

0

0

*

*

*

0

1

0

0

*

*

*

0

1

1

0

*

*

*

0

1

0

0

*

*

*

0

1

0

0

*

*

*

0

1

1

0

B2

C2

D2

0

1

1

0

B1

C1

D1

0

1

X0

0

B0

C0

D0

0

1

连续水平滚动设置

26小时,X[0]= 0,右向水平滚动

27 h,X[0]= 1,左向水平滚动

(水平滚动1列)

[7:0]:虚拟字节(设置为00 h)

B(2:0):定义开始页面地址

0~7   PAGE0 ~ PAGE7

C(2:0):设置每个滚动步骤之间的时间间隔的帧频

000 b - 5帧100 b - 3帧

001 b - 64帧101 b - 4帧

010 b - 128帧110 b - 25帧

011 b - 256帧111 b - 2帧

D(2:0):定义最终页面地址

0~7   PAGE0 ~ PAGE7

D(2:0)的值必须大于或等于B(2:0)

E[7:0]:虚拟字节(设置为00 h)

F[7:0]:虚拟字节(设置为FFh)

0

2E

0

0

1

0

1

1

1

0

禁用滚动

 

0

2F

0

0

1

0

1

1

1

1

激活滚动

 

2、寻址设置命令表

D/C

Hex

D7

D6

D5

D4

D3

D2

D1

D0

命令

描述

0

00~0F

0

0

0

0

X3

X2

X1

X0

设置低的列开始地址页面寻址模式

设置列的低咬起始地址注册页面使用X(握)寻址模式数据位。最初的显示行寄存器复位后重置为0000 b。

请注意

(1)该命令只是页面寻址模式

0

10~1F

0

0

0

1

X3

X2

X1

X0

设定更高的列

开始地址页面寻址模式

设置列的高咬起始地址注册页面使用X(握)寻址模式数据位。最初的显示行寄存器复位后重置为0000 b。请注意

1)这个命令只是页面寻址模式

0

0

20

A[1:0]

0

*

0

*

1

*

0

*

0

*

0

*

0

A1

0

A0

设置内存寻址模式

A[1:0]= 00,水平寻址模式

A[1:0]= 01,垂直的寻址模式

A[1:0]= 10,页面寻址模式(重置)

A[1:0]= 11,无效

0

0

0

21

A[6:0]

B[6:0]

0

*

*

0

A6B6

1

A5B5

0

A4B4

0

A3B3

0

A2

B2

0A1

B1

1A0

B0

设置列地址

设置列开始和结束地址

A[6:0]:列起始地址,范围:0 - 127 (默认值 = 0)

B[6:0]:列结束地址范围:0 - 127 (默认值 = 127)

注:(1)该命令只是为水平或垂直寻址模式。

0

0

0

22

A[2:0]

B[2:0]

0

*

*

0

*

*

1

*

*

0

*

*

0

*

*

0

A2

B2

1A1

B1

0A0

B0

设置页面地址

页面设置开始和结束地址

A[2:0]:页面起始地址,范围:0-7

(默认值= 0 )

B[2:0]:页面结束地址,范围:0-7

(默认值= 7 )

注:(1)该命令只是为水平或垂直寻址模式。

0

B0~B7

1

0

1

1

0

X2

X1

X0

设置页面开始

页面地址寻址模式

设置GDDRAM页面的起始地址

(PAGE0 ~ PAGE7)页面寻址模式,使用X[2:0]。

请注意

(1)该命令只是页面寻址模式

3、硬件配置表(面板分辨率&设计相关)命令

0

40~7F

0

1

X5

X4

X3

X2

X1

X0

设置显示开始行

设置显示RAM的显示起始行地址0 -> 63,使用X5X4X3X2X1X0 。

在复位后起始行地址为0。

0

A0/A1

1

0

1

0

0

0

0

X0

设置段重映射

A0,X[0]= 0:列地址0映射到

SEG0(默认值)

A1 X[0]= 1:列地址127映射到SEG0

0

0

A8

A[5:0]

1

*

0

*

1

A5

0

A4

1

A3

0

A2

0

A1

0

A0

设置多种比列

MUX比率设置为N + 1 MUX

N =A[5:0]:从16MUX到64MUX ,复位值= 111111 b(即63 d、64 mux)

A[5:0]:值0到14是无效的。

0

C0/C8

1

1

0

0

X3

0

0

0

设置COM输出扫描方向

C0:X[3]= 0:正常模式(默认值)扫描 COM0->COM(N - 1)

C8:X[3]= 1:重映射模式。扫描

COM0(N - 1)->COM0

其中N是MUX比率值

0

0

D3

A[5:0]

1

*

1 *

0

A5

1

A4

0

A3

0

A2

1

A1

1

A0

设置显示补偿

设置COM垂直移动 0->63

复位后的值为0。

0

0

DA

A[5:4]

1

*

1 *

0

A5

1

A4

0

0

0

0

1

0

1

0

设置COM脚

A[4]= 0,连续COM脚配置

A[4]= 1,(默认),可选择COM脚配置

A[5]= 0,(默认),禁用COM左/右重映射

A[5]= 1,COM左/右可重映射

4、电荷泵命令表

0

0

8D

A[7:0]

1

*

0

*

0

0

0

1

1

0

1

A2

0

0

1

0

电荷泵

设置

A[2]= 0,禁用电荷泵(复位)

A[2]= 1,在显示时使能电荷泵

请注意:在下列的命令序列之前电荷泵必须启用:

0x8d;电荷泵设置

0x14,使能电荷泵

0xAF;开显示

所以的详细指令可以查阅《SSD1306说明书》。

14.2.2 Frame Buffer显示机制

SSD1306显存是按字节方式写入的,如果我们使用只写方式操作模块,每次要写8个点,因此在显示过程中,必须把要点亮的点所在的字节的每个位的状态都搞清楚,否则写入的数据就会覆盖掉之前的状态,造成显示错误。在可读的模式下,在写入之前,可以对待写入字节进行读取,修改需要操作的位之后再写入显存,虽然1读2改3写的操作方式耗时较多,但是不会出现显示错误。

在介绍SSD1306时已经说过,对于3线或4线SPI模式,模块是不支持读的。为了解决上述问题,采用的办法是在利用软件创建一个显示缓冲区frame_buffer[128][4],共512个字节,也就是128*32个位,对应了OLED整个显示区域。在每次修改的时候,只是修改软件内的frame_buffer,修改完成之后,一次性把frame_buffer内的数据写入到SSD1306内部显存。当然这个方法也有坏处,就是对于那些SRAM很小的单片机(比如51系列)就比较麻烦了。

14.2.3 像素操作函数

建立起frame buffer的显示机制后,只需要能够绘制和擦除像素的函数,就可以点亮和熄灭OLED面板上任意一个LED了。

像素操作函数并不是必须的,但是可以大大提高驱动的灵活性。比如我们要显示的不是中英文字符这种规律简单的图形,而是用某种算法描绘出来的图形,例如椭圆、正弦波等,采用和字符显示类似的查表操作就不见得是明智的选择了,所以像素操作函数就有了一定的必要性。

此外,像素操作函数为顶层API函数提供了一个唯一的OLED绘图接口函数,在移植OLED驱动时,只要不改动该函数的接口,就不会影响和绘图相关功能,从而便于驱动程序的移植和维护。

14.2.4 其他API的实现

虽然提供像素操作函数,就可以实现对OLED的操作,但是为了方便用户进行二次开发,有必要设计一些常见的API,常用的有以下几个:

- 英文字符显示

- 英文字符串显示

- 中文字符显示

14.3 OLED驱动方案实现

Zynq与传统FPGA最大的区别是芯片内置了ARM Cortex-A9双核CPU,因此基于Zynq的设计比基于普通SoC或者基于FPGA的设计有更多的选择,本小节给出一种实现方案,给读者提供一些设计思路。

基础方案,主要针对那些对FPGA开发不太熟悉,从传统SOC开发转型Zynq开发的设计人员。方案的主要工作均由PS完成,涉及FPGA的开发很少。

熟悉单片机开发的人都知道,用IO模拟总线时序是开发时常用的手段,当然,这是因为单片机资源有效,没有相应的总线接口控制器。随着MCU的内置资源越来越丰富, IO模拟总线时序的方法就显得没那么有必要了。但是MCU内置接口控制器也有其缺点和限制,例如硬件接口固定等,外设接口一旦不能完全匹配控制器接口,就不能轻松地使用MCU内置接口控制器了。也正是由于这个原因,本方案没有采用Zynq的SPI控制器,而是采用EMIO对总线时序进行模拟。

14.4 点阵式OLED显示原理

14.4.1 OLED简介

OLED,即有机发光二极管(Organic Light-Emitting Diode),又称为有机电激光显示(Organic Electroluminesence Display,OELD)。OLED由于同时具备自发光,不需背光源、对比度高、厚度薄、视角广、反应速度快、可用于挠曲性面板、使用温度范围广、构造及制程较简单等优异之特性,被认为是下一代的平面显示器新兴应用技术。

LCD都需要背光,而OLED不需要,因为它是自发光的。这样同样的显示,OLED效果要来得好一些。OLED的尺寸难以大型化,但是分辨率确可以做到很高。

14.4.2 点阵式显示设备显示原理

在数字世界中,所有数据归根结底都是以0和1的方式存在的。那么点阵式显示设备是如何将字符、汉字等信息显示出来的呢?抛开OLED这种高大上的词不谈,先来看一下最简单的点阵LED。如下图所示,要显示出图形,只要按照一定的方式点亮点阵上的一部分“点”就可以了,LED的亮和灭就对应着1和0。

wps6FAD.tmp

OLED的显示原理在本质上是相同的,只不过是LED间的间隙很小,密度很大,从而显示效果也比上图中的点阵LED好很多。对于字符而言,这种表征了点阵开关状态的数据,被抽象成了一个术语,叫做字模。例如英文字符“A”和中文字符“你”的字模信息,如下面两幅图所示。

wps6FAE.tmpwps6FBE.tmp

14.4.3 字模的获取

网络上有很多字模获取软件,笔者选用的是PCtoLCD2002。

点击选项,进入下图所示的参数设置界面,根据自己的需求进行参数设置。

wps6FBF.tmp

设置好参数后,在字符框中输入字符,然后点击生成字模,就可以获取到字模信息了。如下图所示。

wps6FC0.tmp

为了更透彻地理解显示原理,笔者首先编写了一个简单的测试程序:

font.h

const unsigned char HanZi[4][32]=

{

// 米(0) 联(1) 电(2) 子(3)

{0x01,0x00,0x21,0x08,0x11,0x08,0x09,0x10,0x09,0x20,0xFF,0xFE,0x05,0x80,0x05,0x40,

0x09,0x40,0x09,0x20,0x11,0x20,0x11,0x18,0x21,0x0E,0x41,0x04,0x81,0x00,0x01,0x00},/*"米",0*/

{0x01,0x08,0xFE,0x8C,0x44,0x48,0x44,0x50,0x7F,0xFE,0x44,0x20,0x44,0x20,0x7C,0x20,

0x47,0xFE,0x44,0x20,0x4E,0x20,0xF4,0x20,0x44,0x50,0x04,0x48,0x04,0x86,0x05,0x04},/*"联",1*/

{0x01,0x00,0x01,0x00,0x01,0x00,0x3F,0xF8,0x21,0x08,0x21,0x08,0x3F,0xF8,0x21,0x08,

0x21,0x08,0x21,0x08,0x3F,0xF8,0x21,0x08,0x01,0x02,0x01,0x02,0x00,0xFE,0x00,0x00},/*"电",2*/

{0x00,0x00,0x3F,0xF0,0x00,0x20,0x00,0x40,0x00,0x80,0x01,0x00,0x01,0x00,0x01,0x04,

0xFF,0xFE,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x05,0x00,0x02,0x00},/*"子",3*/

};

// led_matrix_disp_test.cpp : Defines the entry point for the console application.

#include "stdafx.h"

#include "font.h"

int main(int argc, char* argv[])

{

char i = 0;

unsigned char ch_l = 0x0;

unsigned char ch_r = 0x0;

unsigned char row = 0x0; // 行

unsigned char col = 0x0; // 列

for(i=0;i<4;i++) // 四个汉字

{

for(row=0;row<16;row++) // 逐行打印

{

ch_l = HanZi[i][2*row]; // 字符左半边字模

ch_r = HanZi[i][2*row+1]; // 字符右半边字模

// 绘制左半边

for(col=0;col<8;col++)

{

if(ch_l&0x80)

printf("%d",1);

else

printf(" ");

ch_l = ch_l<<1;

}

// 绘制右半边

for(col=0;col<8;col++)

{

if(ch_r&0x80)

printf("%d",1);

else

printf(" ");

ch_r = ch_r<<1;

}

// 换行,开始绘制下一行

printf(" ");

}

}

return 0;

}

测试结果如下图所示:

wps6FD1.tmpwps6FD2.tmp

这个测试程序采用的字模是从左至右、从上到下的方式获取的,这是因为要照顾到打印函数的特性,程序难度不大,此处不再逐句解释。后续我们设计的OLED驱动虽然和本程序有所区别,但思想上是相同的。

14.5 硬件搭建

本章的硬件电路与第三章基本一致,因此做好备份后,我们直接使用第三章的工程,对其进行一些细微的修改即可。

Step1:做好备份后,打开第三章的工程。

Step2:双击ZYNQ  Processing  System图标,对其进行一些修改。

wps6FD3.tmp

Step3:展开MIO configuration-I/O peripherals-GPIO,将EMIO的数量改为6。

wps6FD4.tmp

Step4:右键单击Block文件,文件选择Generate the Output Products。

Step5:右键单击Block文件,选择Create a HDL wrapper,根据Block文件内容产生一个HDL 的顶层文件,并选择让vivado自动完成。

Step6:修改约束文件,打开对应自己硬件的原理图,查看OLED部分引脚连接情况,此处以Miz702约束为例,其他型号用户对端口稍作修改即可。

#DC

set_property PACKAGE_PIN U10 [get_ports {emio_0_tri_io[0]}]

set_property IOSTANDARD LVCMOS33 [get_ports {emio_0_tri_io[0]}]

#RES

set_property PACKAGE_PIN U9 [get_ports {emio_0_tri_io[1]}]

set_property IOSTANDARD LVCMOS33 [get_ports {emio_0_tri_io[1]}]

#SCLK

set_property PACKAGE_PIN AB12 [get_ports {emio_0_tri_io[2]}]

set_property IOSTANDARD LVCMOS33 [get_ports {emio_0_tri_io[2]}]

#SDIN

set_property PACKAGE_PIN AA12 [get_ports {emio_0_tri_io[3]}]

set_property IOSTANDARD LVCMOS33 [get_ports {emio_0_tri_io[3]}]

#VBAT

set_property PACKAGE_PIN U11 [get_ports {emio_0_tri_io[4]}]

set_property IOSTANDARD LVCMOS33 [get_ports {emio_0_tri_io[4]}]

#VDD

set_property PACKAGE_PIN U12 [get_ports {emio_0_tri_io[5]}]

set_property IOSTANDARD LVCMOS33 [get_ports {emio_0_tri_io[5]}]

Miz701N用户因为只有四个OLED接口,但并不影响,只需要对应约束四个端口就可以。

Step7:生成bit文件。

14.6 导入到SDK

Step1:导出硬件。

Step2:选中第三章的main.c文件,右单击,选择Delete删除文件。

Step3:打开我们提供的源程序包,在第二季,第14章的文件夹中,将SDK所有的文件复制过来。

wps6FE4.tmp

Step4:展开EMIO_Test,在Src下按Ctrl+V将所有文件粘贴过来。

wps6FE5.tmp

Step5:右击工程,选择Debug as ->Debug configuration。

Step6:选中system Debugger,双击创建一个系统调试。

Step7:设置系统调试。

Step8:单击运行程序按钮wps6FE6.tmp运行程序,此时可在OLED上观察到滚动显示我们定义的字符。

14.7 本章小结

本次试验搭进行了OLED的驱动,可以用OLED方便的现实必要信息的现实,例如开发板的运行信息,时间信息等等。

原文地址:https://www.cnblogs.com/milinker/p/6474791.html