完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
1)实验平台:alientek 阿波罗 STM32F767 开发板
2)摘自《STM32F7 开发指南(HAL 库版)》关注官方微信号公众号,获取更多资料:正点原子 第十七章 OLED 显示实验 前面几章的实例,均没涉及到液晶显示,这一章,我们将向大家介绍 OLED 的使用。在本 章中,我们将使用探索者 STM32F4 开发板上的 OLED 模块接口,来点亮 OLED,并实现 ASCII 字符的显示。本章分为如下几个部分: 17.1 OLED 简介 17.2 硬件设计 17.3 软件设计 17.4 下载验证 17.1 OLED 简介 OLED,即有机发光二极管(Organic Light-Emitting Diode),又称为有机电激光显示(Organic Electroluminesence Display, OELD)。OLED 由于同时具备自发光,不需背光源、对比度高、 厚度薄、视角广、反应速度快、可用于挠曲性面板、使用温度范围广、构造及制程较简单等优 异之特性,被认为是下一代的平面显示器新兴应用技术。 LCD 都需要背光,而 OLED 不需要,因为它是自发光的。这样同样的显示,OLED 效果要 来得好一些。以目前的技术,OLED 的尺寸还难以大型化,但是分辨率确可以做到很高。在本 章中,我们使用的是 ALINETEK 的 OLED 显示模块,该模块有以下特点: 1)模块有单色和双色两种可选,单色为纯蓝色,而双色则为黄蓝双色。 2)尺寸小,显示尺寸为 0.96 寸,而模块的尺寸仅为 27mm*26mm 大小。 3)高分辨率,该模块的分辨率为 128*64。 4)多种接口方式,该模块提供了总共 4 种接口包括:6800、8080 两种并行接口方式、4 线 SPI 接口方式以及 IIC 接口方式(只需要 2 根线就可以控制 OLED 了!)。 5)不需要高压,直接接 3.3V 就可以工作了。 这里要提醒大家的是,该模块不和 5.0V 接口兼容,所以请大家在使用的时候一定要小心, 别直接接到 5V 的系统上去,否则可能烧坏模块。以上 4 种模式通过模块的 BS1 和 BS2 设置, BS1 和 BS2 的设置与模块接口模式的关系如表 17.1.1 所示: 表 17.1.1 中:“1”代表接 VCC,而“0”代表接 GND。 该模块的外观图如图 17.1.1 所示 图 17.1.1 ALIENTEK OLED 模块外观图 ALIENTEK OLED 模块默认设置是:BS1 和 BS2 接 VCC ,即使用 8080 并口方式,如果 你想要设置为其他模式,则需要在 OLED 的背面,用烙铁修改 BS1 和 BS2 的设置。 模块的原理图如图 17.1.2 所示: 图 17.1.2 ALIENTEK OLED 模块原理图 该模块采用 8*2 的 2.54 排针与外部连接,总共有 16 个管脚,在 16 条线中,我们只用了 15 条,有一个是悬空的。15 条线中,电源和地线占了 2 条,还剩下 13 条信号线。在不同模式下, 我们需要的信号线数量是不同的,在 8080 模式下,需要全部 13 条,而在 IIC 模式下,仅需要 2 条线就够了!这其中有一条是共同的,那就是复位线 RST(RES),RST 上的低电平,将导致 OLED 复位,在每次初始化之前,都应该复位一下 OLED 模块。 ALIENTEK OLED 模块的控制器是 SSD1306,本章,我们将学习如何通过 STM32F4 来控 制该模块显示字符和数字,本章的实例代码将可以支持两种方式与 OLED 模块连接,一种是 8080 的并口方式,另外一种是 4 线 SPI 方式。 首先我们介绍一下模块的 8080 并行接口,8080 并行接口的发明者是 INTEL,该总线也被 广泛应用于各类液晶显示器,ALIENTEK OLED 模块也提供了这种接口,使得 MCU 可以快速 的访问 OLED。ALIENTEK OLED 模块的 8080 接口方式需要如下一些信号线: CS:OLED 片选信号。 WR:向 OLED 写入数据。 RD:从 OLED 读取数据。 D[7:0]:8 位双向数据线。 RST(RES):硬复位 OLED。 DC:命令/数据标志(0,读写命令;1,读写数据)。 模块的 8080 并口读/写的过程为:先根据要写入/读取的数据的类型,设置 DC 为高(数据) /低(命令),然后拉低片选,选中 SSD1306,接着我们根据是读数据,还是要写数据置 RD/WR 为低,然后: 在 RD 的上升沿, 使数据锁存到数据线(D[7:0])上; 在 WR 的上升沿,使数据写入到 SSD1306 里面; SSD1306 的 8080 并口写时序图如图 17.1.3 所示: 图 17.1.3 8080 并口写时序图 SSD1306 的 8080 并口读时序图如图 17.1.4 所示: 图 17.1.4 8080 并口读时序图 SSD1306 的 8080 接口方式下,控制脚的信号状态所对应的功能如表 17.1.2: 图 17.1.5 读显存时序图 可以看到,在发送了列地址之后,开始读数据,第一个是 Dummy Read,也就是假读,我 们从第二个开始,才算是真正有效的数据。 并行接口模式就介绍到这里,我们接下来介绍一下 4 线串行(SPI)方式,4 先串口模式使 用的信号线有如下几条: CS:OLED 片选信号。 RST(RES):硬复位 OLED。 DC:命令/数据标志(0,读写命令;1,读写数据)。 SCLK:串行时钟线。在 4 线串行模式下,D0 信号线作为串行时钟线 SCLK。 SDIN:串行数据线。在 4 线串行模式下,D1 信号线作为串行数据线 SDIN。 模块的 D2 需要悬空,其他引脚可以接到 GND。在 4 线串行模式下,只能往模块写数据而 不能读数据。 在 4 线 SPI 模式下,每个数据长度均为 8 位,在 SCLK 的上升沿,数据从 SDIN 移入到 SSD1306,并且是高位在前的。DC 线还是用作命令/数据的标志线。在 4 线 SPI 模式下,写操 作的时序如图 17.1.6 所示: 图 17.1.6 4 线 SPI 写操作时序图 4 线串行模式就为大家介绍到这里。其他还有几种模式,在 SSD1306 的数据手册上都有详 细的介绍,如果要使用这些方式,请大家参考该手册。 接下来,我们介绍一下模块的显存,SSD1306 的显存总共为 128*64bit 大小,SSD1306 将 这些显存分为了 8 页,其对应关系如表 17.1.3 所示: 表 17.1.3 SSD1306 显存与屏幕对应关系表 可以看出,SSD1306 的每页包含了 128 个字节,总共 8 页,这样刚好是 128*64 的点阵大 小。因为每次写入都是按字节写入的,这就存在一个问题,如果我们使用只写方式操作模块, 那么,每次要写 8 个点,这样,我们在画点的时候,就必须把要设置的点所在的字节的每个位 都搞清楚当前的状态(0/1?),否则写入的数据就会覆盖掉之前的状态,结果就是有些不需要 显示的点,显示出来了,或者该显示的没有显示了。这个问题在能读的模式下,我们可以先读 出来要写入的那个字节,得到当前状况,在修改了要改写的位之后再写进 GRAM,这样就不会 影响到之前的状况了。但是这样需要能读 GRAM,对于 4 线 SPI 模式/IIC 模式,模块是不支持 读的,而且读->改->写的方式速度也比较慢。 所以我们采用的办法是在 STM32F4 的内部建立一个 OLED 的 GRAM(共 128*8 个字节), 在每次修改的时候,只是修改 STM32F4 上的 GRAM(实际上就是 SRAM),在修改完了之后, 一次性把 STM32F4 上的 GRAM 写入到 OLED 的 GRAM。当然这个方法也有坏处,就是对于 那些 SRAM 很小的单片机(比如 51 系列)就比较麻烦了。 SSD1306 的命令比较多,这里我们仅介绍几个比较常用的命令,这些命令如表 17.1.4 所示: 表 17.1.4 SSD1306 常用命令表 第一个命令为 0X81,用于设置对比度的,这个命令包含了两个字节,第一个 0X81 为命令, 随后发送的一个字节为要设置的对比度的值。这个值设置得越大屏幕就越亮。 第二个命令为 0XAE/0XAF。0XAE 为关闭显示命令;0XAF 为开启显示命令。 第三个命令为 0X8D,该指令也包含 2 个字节,第一个为命令字,第二个为设置值,第二 个字节的 BIT2 表示电荷泵的开关状态,该位为 1,则开启电荷泵,为 0 则关闭。在模块初始化 的时候,这个必须要开启,否则是看不到屏幕显示的。 第四个命令为 0XB0~B7,该命令用于设置页地址,其低三位的值对应着 GRAM 的页地址。 第五个指令为 0X00~0X0F,该指令用于设置显示时的起始列地址低四位。 第六个指令为 0X10~0X1F,该指令用于设置显示时的起始列地址高四位。 其他命令,我们就不在这里一一介绍了,大家可以参考 SSD1306 datasheet 的第 28 页。从 这页开始,对 SSD1306 的指令有详细的介绍。 最后,我们再来介绍一下 OLED 模块的初始化过程,SSD1306 的典型初始化框图如图 17.1.7 所示: 图 17.1.7 SSD1306 初始化框图 驱动 IC 的初始化代码,我们直接使用厂家推荐的设置就可以了,只要对细节部分进行一些 修改,使其满足我们自己的要求即可,其他不需要变动。 OLED 的介绍就到此为止,我们重点向大家介绍了 ALIENTEK OLED 模块的相关知识,接 下来我们将使用这个模块来显示字符和数字。通过以上介绍,我们可以得出 OLED 显示需要的 相关设置步骤如下: 1)设置 STM32F4 与 OLED 模块相连接的 IO。 这一步,先将我们与 OLED 模块相连的 IO 口设置为输出,具体使用哪些 IO 口,这里需要 根据连接电路以及 OLED 模块所设置的通讯模式来确定。这些将在硬件设计部分向大家介绍。 2)初始化 OLED 模块。 其实这里就是上面的初始化框图的内容,通过对 OLED 相关寄存器的初始化,来启动 OLED 的显示。为后续显示字符和数字做准备。 3)通过函数将字符和数字显示到 OLED 模块上。 这里就是通过我们设计的程序,将要显示的字符送到 OLED 模块就可以了,这些函数将在 软件设计部分向大家介绍。 通过以上三步,我们就可以使用 ALIENTEK OLED 模块来显示字符和数字了,在后面我们 还将会给大家介绍显示汉字的方法。这一部分就先介绍到这里。 17.2 硬件设计 本实验用到的硬件资源有: 1) 指示灯 DS0 2) OLED 模块 OLED 模块的电路在前面已有详细说明了,这里我们介绍 OLED 模块与探索者 STM32F4 开发板的连接,开发板底板的 OLED/CAMERA 接口(P8 接口)可以和 ALIENTEK OLED 模块 直接对插(靠左插!),连接如图 17.2.1 所示: 图 17.2.1 OLED 模块与开发板连接示意图 图中圈出来的部分就是连接 OLED 的接口,这里在硬件上,OLED 与探索者 STM32F4 开 发板的 IO 口对应关系如下: OLED_CS 对应 DCMI_VSYNC,即:PB7; OLED_RS 对应 DCMI_SCL,即:PD6; OLED_WR 对应 DCMI_HREF,即:PA4; OLED_RD 对应 DCMI_SDA,即:PD7; OLED_RST 对应 DCMI_RESET,即:PG15; OLED_D[7:0]对应 DCMI_D[7:0],即: PE6/PE5/PB6/PC11/PC9/PC8/PC7/PC6; 这些线的连接,开发板的内部已经连接好了,我们只需要将 OLED 模块插上去就好了,注 意,这里的 OLED_D[7:0]因为不是接的连续的 IO,所以得用拼凑的方式去组合一下,后续会介 绍。实物连接如图 17.2.2 所示: 图 17.2.2 OLED 模块与开发板连接实物图 17.3 软件设计 本实验,我们新建了 oled.c 和 oled.h 文件。这两个文件用来存放 OLED 相关的驱动函数以 及文件申明等。 oled.c 的代码,由于比较长,这里我们就不贴出来了,仅介绍几个比较重要的函数。首先 是 OLED_Init 函数,该函数的结构比较简单,开始是对 IO 口的初始化,这里我们用了宏定义 OLED_MODE 来决定要设置的 IO 口,其他就是一些初始化序列了,我们按照厂家提供的资料 来做就可以。最后要说明一点的是,因为 OLED 是无背光的,在初始化之后,我们把显存都清 空了,所以我们在屏幕上是看不到任何内容的,跟没通电一个样,不要以为这就是初始化失败, 要写入数据模块才会显示的。OLED_Init 函数代码如下: //初始化 SSD1306 void OLED_Init(void) { GPIO_InitTypeDef GPIO_Initure; __HAL_RCC_GPIOA_CLK_ENABLE(); //使能 GPIOA 时钟 __HAL_RCC_GPIOB_CLK_ENABLE(); //使能 GPIOB 时钟 __HAL_RCC_GPIOC_CLK_ENABLE(); //使能 GPIOC 时钟 __HAL_RCC_GPIOD_CLK_ENABLE(); //使能 GPIOD 时钟 __HAL_RCC_GPIOE_CLK_ENABLE(); //使能 GPIOE 时钟 __HAL_RCC_GPIOG_CLK_ENABLE(); //使能 GPIOG 时钟 #if OLED_MODE==1 //使用 8080 并口模式 //GPIO 初始化设置 GPIO_Initure.Pin=GPIO_PIN_4; //PA4 GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出 GPIO_Initure.Pull=GPIO_PULLUP; //上拉 GPIO_Initure.Speed=GPIO_SPEED_FAST; //快速 HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化 //PB6,7 GPIO_Initure.Pin=GPIO_PIN_6|GPIO_PIN_7; HAL_GPIO_Init(GPIOB,&GPIO_Initure);//初始化 //PC6,7,8,9,11 GPIO_Initure.Pin=GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_11; HAL_GPIO_Init(GPIOC,&GPIO_Initure);//初始化 //PD6,7 GPIO_Initure.Pin=GPIO_PIN_6|GPIO_PIN_7; HAL_GPIO_Init(GPIOD,&GPIO_Initure);//初始化 //PE5,6 GPIO_Initure.Pin=GPIO_PIN_5|GPIO_PIN_6; HAL_GPIO_Init(GPIOE,&GPIO_Initure);//初始化 //PG15 GPIO_Initure.Pin=GPIO_PIN_15; HAL_GPIO_Init(GPIOG,&GPIO_Initure);//初始化 OLED_WR=1; OLED_RD=1; #else //使用 4 线 SPI 串口模式 //GPIO 初始化设置 GPIO_Initure.Pin=GPIO_PIN_7; //PB7 GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出 GPIO_Initure.Pull=GPIO_PULLUP; //上拉 GPIO_Initure.Speed=GPIO_SPEED_FAST; //高速 HAL_GPIO_Init(GPIOB,&GPIO_Initure); //初始化 //PC6,7 GPIO_Initure.Pin=GPIO_PIN_6|GPIO_PIN_7; HAL_GPIO_Init(GPIOC,&GPIO_Initure);//初始化 //PD6 GPIO_Initure.Pin=GPIO_PIN_6; HAL_GPIO_Init(GPIOD,&GPIO_Initure);//初始化 //PG15 GPIO_Initure.Pin=GPIO_PIN_15; HAL_GPIO_Init(GPIOG,&GPIO_Initure);//初始化 OLED_SDIN=1; OLED_SCLK=1; #endif OLED_CS=1; OLED_RS=1; OLED_RST=0; delay_ms(100); OLED_RST=1; OLED_WR_Byte(0xAE,OLED_CMD); //关闭显示 OLED_WR_Byte(0xD5,OLED_CMD); //设置时钟分频因子,震荡频率 OLED_WR_Byte(80,OLED_CMD); //[3:0],分频因子;[7:4],震荡频率 OLED_WR_Byte(0xA8,OLED_CMD); //设置驱动路数 OLED_WR_Byte(0X3F,OLED_CMD); //默认 0X3F(1/64) OLED_WR_Byte(0xD3,OLED_CMD); //设置显示偏移 OLED_WR_Byte(0X00,OLED_CMD); //默认为 0 OLED_WR_Byte(0x40,OLED_CMD); //设置显示开始行 [5:0],行数. OLED_WR_Byte(0x8D,OLED_CMD); //电荷泵设置 OLED_WR_Byte(0x14,OLED_CMD); //bit2,开启/关闭 OLED_WR_Byte(0x20,OLED_CMD); //设置内存地址模式 OLED_WR_Byte(0x02,OLED_CMD); //[1:0],00,列地址模式;01,行地址模式;10,页地址模式;默认 10; OLED_WR_Byte(0xA1,OLED_CMD); //段重定义设置,bit0:0,0->0;1,0->127; OLED_WR_Byte(0xC0,OLED_CMD); //设置 COM 扫描方向;bit3:0,普通模式;1,重定义模式 COM[N-1]->COM0;N:驱动路数 OLED_WR_Byte(0xDA,OLED_CMD); //设置 COM 硬件引脚配置 OLED_WR_Byte(0x12,OLED_CMD); //[5:4]配置 OLED_WR_Byte(0x81,OLED_CMD); //对比度设置 OLED_WR_Byte(0xEF,OLED_CMD); //1~255;默认 0X7F (亮度设置,越大越亮) OLED_WR_Byte(0xD9,OLED_CMD); //设置预充电周期 OLED_WR_Byte(0xf1,OLED_CMD); //[3:0],PHASE 1;[7:4],PHASE 2; OLED_WR_Byte(0xDB,OLED_CMD); //设置 VCOMH 电压倍率 OLED_WR_Byte(0x30,OLED_CMD); //[6:4] 000,0.65*vcc;001,0.77*vcc;011,0.83*vcc; OLED_WR_Byte(0xA4,OLED_CMD); //全局显示开启;bit0:1,开启;0,关闭;(白屏/黑屏) OLED_WR_Byte(0xA6,OLED_CMD); //设置显示方式;bit0:1,反相显示;0,正常显示 OLED_WR_Byte(0xAF,OLED_CMD); //开启显示 OLED_Clear(); }接着,要介绍的是OLED_Refresh_Gram 函数。我们在 STM32F4内部定义了一个块GRAM: u8 OLED_GRAM[128][8];此部分 GRAM 对应 OLED 模块上的 GRAM。在操作的时候,我们只 要修改 STM32F4 内部的 GRAM 就可以了,然后通过 OLED_Refresh_Gram 函数把 GRAM 一次 刷新到 OLED 的 GRAM 上。该函数代码如下: //更新显存到 LCD void OLED_Refresh_Gram(void) { u8 i,n; for(i=0;i<8;i++) { OLED_WR_Byte (0xb0+i,OLED_CMD); //设置页地址(0~7) OLED_WR_Byte (0x00,OLED_CMD); //设置显示位置—列低地址 OLED_WR_Byte (0x10,OLED_CMD); //设置显示位置—列高地址 for(n=0;n<128;n++)OLED_WR_Byte(OLED_GRAM[n],OLED_DATA); } }OLED_Refresh_Gram 函数先设置页地址,然后写入列地址(也就是纵坐标),然后从 0 开 始写入 128 个字节,写满该页,最后循环把 8 页的内容都写入,就实现了整个从 STM32F4 显 存到 OLED 显存的拷贝。 OLED_Refresh_Gram 函数还用到了一个外部函数,也就是我们接着要介绍的函数: OLED_WR_Byte,该函数直接和硬件相关,函数代码如下: #if OLED_MODE==1 //通过拼凑的方法向 OLED 输出一个 8 位数据 //data:要输出的数据 void OLED_Data_Out(u8 data) { u16 dat=data&0X0F; GPIOC->ODR&=~(0XF<<6); //清空 6~9 GPIOC->ODR|=dat<<6; //D[3:0]-->PC[9:6] GPIOC->ODR&=~(0X1<<11); //清空 11 GPIOC->ODR|=((data>>4)&0x01)<<11; GPIOB->ODR&=~(0X1<<6); //清空 6 GPIOB->ODR|=((data>>5)&0x01)<<6; GPIOE->ODR&=~(0X3<<5); //清空 5,6 GPIOE->ODR|=((data>>6)&0x01)<<5; GPIOE->ODR|=((data>>7)&0x01)<<6; } //向 SSD1306 写入一个字节。 //dat:要写入的数据/命令, cmd:数据/命令标志 0,表示命令;1,表示数据; void OLED_WR_Byte(u8 dat,u8 cmd) { OLED_Data_Out(dat); OLED_RS=cmd; OLED_CS=0; OLED_WR=0; OLED_WR=1; OLED_CS=1; OLED_RS=1; } #else //向 SSD1306 写入一个字节。 //dat:要写入的数据/命令 //cmd:数据/命令标志 0,表示命令;1,表示数据; void OLED_WR_Byte(u8 dat,u8 cmd) { u8 i; OLED_RS=cmd; //写命令 OLED_CS=0; for(i=0;i<8;i++) { OLED_SCLK=0; if(dat&0x80)OLED_SDIN=1; else OLED_SDIN=0; OLED_SCLK=1;dat<<=1; } OLED_CS=1; OLED_RS=1; } #endif首先,我们看 OLED_Data_Out 函数,这就是我们前面说的,因为 OLED 的 D0~D7 不是接 的连续 IO,所以必须将数据,拆分到各个 IO,以实现一次完整的数据传输,该函数就是根据 我们 OLED_D[7:0]具体连接的 IO,对数据进行拆分,然后输出给对应位的各个 IO,实现并口 数据输出。这种方式会降低并口速度,但是我们 OLED 模块,是单色的,数据量不是很大,所 以这种方式也不会造成视觉上的影响,大家可以放心使用,但是如果是 TFTLCD,就不推荐了。 然后,看 OLED_WR_Byte 函数,这里有 2 个一样的函数,通过宏定义 OLED_MODE 来决 定使用哪一个。如果 OLED_MODE=1,就定义为并口模式,选择第一个函数,而如果为 0,则 为 4 线串口模式,选择第二个函数。这两个函数输入参数均为 2 个:dat 和 cmd,dat 为要写入 的数据,cmd 则表明该数据是命令还是数据。这两个函数的时序操作就是根据上面我们对 8080 接口以及 4 线 SPI 接口的时序来编写的。 OLED_GRAM[128][8]中的 128 代表列数(x 坐标),而 8 代表的是页,每页又包含 8 行, 总共 64 行(y 坐标)。从高到低对应行数从小到大。比如,我们要在 x=100,y=29 这个点写入 1,则可以用这个句子实现: OLED_GRAM[100][4]|=1<<2; 一个通用的在点(x,y)置 1 表达式为: OLED_GRAM[x][7-y/8]|=1<<(7-y%8); 其中 x 的范围为:0~127;y 的范围为:0~63。 因此,我们可以得出下一个将要介绍的函数:画点函数,void OLED_DrawPoint(u8 x,u8 y, u8 t);函数代码如下: void OLED_DrawPoint(u8 x,u8 y,u8 t) { u8 pos,bx,temp=0; if(x>127||y>63)return;//超出范围了. pos=7-y/8;bx=y%8; temp=1<<(7-bx); if(t)OLED_GRAM[x][pos]|=temp; else OLED_GRAM[x][pos]&=~temp; }该函数有 3 个参数,前两个是坐标,第三个 t 为要写入 1 还是 0。该函数实现了我们在 OLED 模块上任意位置画点的功能。 接下来,我们介绍一下显示字符函数,OLED_ShowChar,在介绍之前,我们来介绍一下字 符(ASCII 字符集)是怎么显示在 OLED 模块上去的。要显示字符,我们先要有字符的点阵数 据,ASCII 常用的字符集总共有 95 个,从空格符开始,分别为: !"#$%&'()*+,-0123456789:;<= >?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~. 我们先要得到这个字符集的点阵数据,这里我们介绍一个款很好的字符提取软件: PCtoLCD2002 完美版。该软件可以提供各种字符,包括汉字(字体和大小都可以自己设置)阵 提取,且取模方式可以设置好几种,常用的取模方式,该软件都支持。该软件还支持图形模式, 也就是用户可以自己定义图片的大小,然后画图,根据所画的图形再生成点阵数据,这功能在 制作图标或图片的时候很有用。 该软件的界面如图 17.3.1 所示: 图 17.3.1 PCtoLCD2002 软件界面 然后我们选择设置,在设置里面设置取模方式如图 17.3.2 所示: 图 17.3.2 设置取模方式 上图设置的取模方式,在右上角的取模说明里面有,即:从第一列开始向下每取 8 个点作 为一个字节,如果最后不足 8 个点就补满 8 位。取模顺序是从高到低,即第一个点作为最高位。 如*-------取为 10000000。其实就是按如图 17.3.3 所示的这种方式: 图 17.3.3 取模方式图解 从上到下,从左到右,高位在前。我们按这样的取模方式,然后把 ASCII 字符集按 12*6 大小、16*8 和 24*12 大小取模出来(对应汉字大小为 12*12、16*16 和 24*24,字符的只有汉字 的一半大!),保存在 oledfont.h 里面,每个 12*6 的字符占用 12 个字节,每个 16*8 的字符占用 16 个字节,每个 24*12 的字符占用 36 个字节。具体见 oledfont.h 部分代码(该部分我们不再这 里列出来了,请大家参考光盘里面的代码)。 在知道了取模方式之后,我们就可以根据取模的方式来编写显示字符的代码了,这里我们 针对以上取模方式的显示字符代码如下: //在指定位置显示一个字符,包括部分字符 //x:0~127 y:0~63 //mode:0,反白显示;1,正常显示 //size:选择字体 12/16/24 void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size,u8 mode) { u8 temp,t,t1,y0=y; u8 csize=(size/8+((size%8)?1:0))*(size/2);//得到字体一个字符对应点阵集所占的字节数 chr=chr-' ';//得到偏移后的值 for(t=0;t { if(size==12)temp=asc2_1206[chr][t]; //调用 1206 字体 else if(size==16)temp=asc2_1608[chr][t]; //调用 1608 字体 else if(size==24)temp=asc2_2412[chr][t]; //调用 2412 字体 else return; //没有的字库 for(t1=0;t1<8;t1++) { if(temp&0x80)OLED_DrawPoint(x,y,mode); else OLED_DrawPoint(x,y,!mode); temp<<=1;y++; if((y-y0)==size) { y=y0; x++;break; } } } }该函数为字符以及字符串显示的核心部分,函数中 chr=chr-' ';这句是要得到在字符点阵数 据里面的实际地址,因为我们的取模是从空格键开始的,例如 oled_asc2_1206[0][0],代表的是 空格符开始的点阵码。在接下来的代码,我们也是按照从上到小(先 y++),从左到右(再 x++) 的取模方式来编写的,先得到最高位,然后判断是写 1 还是 0,画点;接着读第二位,如此循 环,直到一个字符的点阵全部取完为止。这其中涉及到列地址和行地址的自增,根据取模方式 来理解,就不难了。 oled.c 的内容就为大家介绍到这里,接下来我们看看 oled.h 代码: #ifndef __OLED_H #define __OLED_H #include "sys.h" #include "stdlib.h" //OLED 模式设置 //0: 4 线串行模式 (模块的 BS1,BS2 均接 GND) //1: 并行 8080 模式 (模块的 BS1,BS2 均接 VCC) #define OLED_MODE 1 //-----------------OLED 端口定义---------------- #define OLED_CS PBout(7) #define OLED_RST PGout(15) #define OLED_RS PDout(6) #define OLED_WR PAout(4) #define OLED_RD PDout(7) //使用 4 线串行接口时使用 #define OLED_SCLK PCout(6) #define OLED_SDIN PCout(7) #define OLED_CMD 0 //写命令 #define OLED_DATA 1 //写数据 //OLED 控制用函数 void OLED_WR_Byte(u8 dat,u8 cmd); …… //忽略部分函数声明 void OLED_ShowString(u8 x,u8 y,const u8 *p); #endif该部分比较简单,OLED_MODE 的定义也在这个文件里面,我们必须根据自己 OLED 模 块 BS1 和 BS2 的设置(目前代码仅支持 8080 和 4 线 SPI)来确定 OLED_MODE 的值。 最后我们来看看主函数代码: int main(void) { u8 t=0; HAL_Init(); //初始化 HAL 库 Stm32_Clock_Init(336,8,2,7); //设置时钟,168Mhz delay_init(168); //初始化延时函数 uart_init(115200); //初始化串口波特率为 115200 LED_Init(); //初始化 LED OLED_Init(); //初始化 OLED OLED_ShowString(0,0,"ALIENTEK",24); OLED_ShowString(0,24, "0.96' OLED TEST",16); OLED_ShowString(0,40,"ATOM 2014/5/4",12); OLED_ShowString(0,52,"ASCII:",12); OLED_ShowString(64,52,"CODE:",12); OLED_Refresh_Gram();//更新显示到 OLED t=' '; while(1) { OLED_ShowChar(36,52,t,12,1);//显示 ASCII 字符 OLED_ShowNum(94,52,t,3,12); //显示 ASCII 字符的码值 OLED_Refresh_Gram();//更新显示到 OLED t++; if(t>'~')t=' '; delay_ms(500); LED0=!LED0; } }该部分代码用于在 OLED 上显示一些字符,然后从空格键开始不停的循环显示 ASCII 字符 集,并显示该字符的 ASCII 值。然后我们编译此工程,直到编译成功为止。 17.4 下载验证 将代码下载到开发板后,可以看到 DS0 不停的闪烁,提示程序已经在运行了。同时可以看 到 OLED 模块显示如图 17.4.1 所示: 图 17.4.1 OLED 显示效果 图中 OLED 显示了三种尺寸的字符:24*12(ALIENTEK)、16*8(0.96’ OLED TEST)和 12*6(剩下的内容)。说明我们的实验是成功的,实现了三种不同尺寸 ASCII 字符的显示,在 最后一行不停的显示 ASCII 字符以及其码值。 通过这一章的学习,我们学会了 ALIENTEK OLED 模块的使用,在调试代码的时候,又多 了一种显示信息的途径,在以后的程序编写中,大家可以好好利用。 |
|
相关推荐
|
|
1085 浏览 0 评论
AD7686芯片不传输数据给STM32,但是手按住就会有数据。
1041 浏览 2 评论
2145 浏览 0 评论
如何解决MPU-9250与STM32通讯时,出现HAL_ERROR = 0x01U
1240 浏览 1 评论
hal库中i2c卡死在HAL_I2C_Master_Transmit
1660 浏览 1 评论
浏览过的版块 |
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-25 01:18 , Processed in 0.553096 second(s), Total 67, Slave 47 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号