zoukankan      html  css  js  c++  java
  • 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方便的现实必要信息的现实,例如开发板的运行信息,时间信息等等。

  • 相关阅读:
    shell特殊符号cut命令 sort_wc_uniq命令 tee_tr_split命令 shell特殊符号
    管道符和作业控制 shell变量 环境变量配置文件
    8.1 shell介绍 8.2 命令历史 8.3 命令补全和别名 8.4 通配符 8.5 输入输出重定向 
    yum更换国内源 yum下载rpm包 源码包安装
    mysql Communication link failure, message from server: "Can't get hostname for your address"
    7.1 安装软件包的三种方法 7.2 rpm包介绍 7.3 rpm工具用法 7.4 yum工具用法 7.5 yum搭建本地仓库
    java 对象数组
    zip压缩工具 tar打包 打包并压缩
    java链接mysql 中文乱码
    压缩打包介绍/gzip压缩工具/bzip2压缩工具/xz压缩工具
  • 原文地址:https://www.cnblogs.com/milinker/p/6474791.html
Copyright © 2011-2022 走看看