第十六章 IIC_OLED实验
本章,我们将继续使用ESP32-S3的硬件IIC接口去驱动正点原子的OLED模块,实现ASCII字符的显示。 本章分为如下几个小节: 16.1 OLED介绍 16.2 硬件设计 16.3 软件设计 16.4 下载验证
16.1 OLED介绍 16.1.1 OLED简介 OLED,即有机发光二极管(Organic Light-Emitting Diode),又称为有机电激光显示(Organic Electroluminesence Display,OLED)。OLED可按发光材料分为两种:小分子OLED和高分子OLED(也可称为PLED)。OLED是一种利用多层有机薄膜结构产生电致发光的器件,它很容易制作,而且只需要低的驱动电压,OLED由于同时具备自发光(不需背光源)、对比度高、厚度薄、视角广、反应速度快、功耗低、柔性好等优异特性,目前主要用于显示领域,OLED在节能照明领域的开发也成为全球趋势。 本章我们将介绍正点原子的OLED显示模块及其使用方法,该模块有以下特点: 1) 模块有单色和双色两种可选,单色为纯蓝色,而双色则为黄蓝双色(分区域的双色,前16行为黄色,后48行为蓝色,且黄蓝色之间有一行不显示的间隔区)。 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的设置与模块接口模式的关系如表16.1.1所示: 表16.1.1 OLED模块接口方式设置表 表15.1.1中:“1”代表接VCC,而“0”代表接GND。该模块的外观图如图15.1.1所示: 图16.1.1 正点原子OLED模块外观图
正点原子OLED模块默认设置是:BS1和BS2接VCC,即使用8080并口方式,如果你想要设置为其他模式,则需要在OLED的背面,用烙铁修改BS1和BS2的设置。 对于本实验而言,是需要将OLED模块的接口方式修改为IIC,所以就需要把BS2接到GND,并把D1和D2连接起来。最终使用IIC接口的OLED模块图如下图所示。 图16.1.2 IIC接口的OLED模块
模块的原理图如图16.1.3所示: 图16.1.3 正点原子OLED模块原理图
该模块采用8*2的2.54排针与外部连接,总共有16个管脚,在16条线中,我们只用了15条,有一个是悬空的。15条线中,电源和地线占了2条,还剩下13条信号线。在不同模式下,我们需要的信号线数量是不同的,在8080模式下,需要全部13条,而在该模块的IIC模式下,需要4条线,分别为OLED_D0(SCL)、OLED_D1和D2(SDA)、OLED_DC、OLED_RST。在IIC模式下,OLED_D0是作为IIC的SCL线,OLED_D1和D2连接一起作为IIC的SDA线,而OLED_DC是作为SA0,用于设置IIC器件地址,OLED_RST是复位线,RST上的低电平,将导致OLED复位,在每次初始化之前,都应该复位一下OLED模块。
要进行IIC通信,首先得知道器件地址,OLED器件地址是7位的,具体格式如下表所示。 表16.1.2 OLED地址格式
OLED模块的主控芯片为SSD1306,从上表可以知道,SSD1306器件地址由两部分组成,一部分就是“固定部分”即“011110”;另一部分就是“可变部分”即SA0引脚,在程序上会让该引脚输出低电平,所以该位为“0”。最终可得到,SSD1306器件地址为“0111100”即0x3C。读操作地址就为0x79,即0111 1001;写操作地址就为0x78,即0111 1000。 16.1.2 SSD1306库介绍 OLED模块驱动,可以参考器件手册中对时序图的描述,基于Wire库函数自行实现。但是在本章中,我们并没有讲解OLED时序,是因为这里我们要用到别人写好的库,站在别人的肩膀上进行开发。Arduino开发有一大优点,就是有很多参考库,基于这些参考库,就可以快速开发项目,并不需要重复造轮子。 接下来,我们就来看一下如何下载OLED库包。 对于OLED模块来说,它的主控芯片是SSD1306,所以我们要下载SSD1306的库包。这个库包的名字为“ESP8266 and ESP32 OLED driver for SSD1306 displays”库,可以在Arduino IDE中库管理搜索到,具体下载操作如下图所示。 图16.1.2.1 安装SSD1306库包步骤
在线下载过程可能会失败,不用担心,我们采用第二种方式:手动安装。在“资料盘à6,软件资料à2,Arduino软件包”下,有“esp8266-oled-ssd1306-master.zip”压缩包,我们就是要安装这个软件包,操作如下: 图16.1.2.2 手动添加软件库
通过操作“项目à导入库à添加.ZIP库”,后面会跳出一个页面,我们找到并选中“esp8266-oled-ssd1306-master.zip”压缩包,这时候IDE就会安装这个库。安装成功如下图所示。 图16.1.2.3 SSD1306库安装成功
当我们下载好“ESP8266 and ESP32 OLED driver for SSD1306 displays”库后,可以从“C:\Users\用户名\ Documents\Arduino\libraries\”路径下找到该文件,该文件夹内容如下图所示。 图16.1.2.4 SSD1306库包文件夹
该文件夹下,examples文件夹下存放的是示例工程,可以快速了解库的使用。打开方式有两种:从文件夹中打开,从IDE进行打开。从IDE打开成功安装的库包示例工程操作如下图所示。 图16.1.2.5 SSD1306库示例工程
resources文件夹存放的是该库实现的照片,而src文件夹存放的是实现代码,大家有兴趣去研究的可以自行查看。 在SSD1306库中,主要分为显示控制、绘制、文本等功能函数,这里就简单介绍一下本例程用到的,其余函数可以自行去看源文件。 本例程用到显示控制功能函数如下: void init(); /* 初始化显示器 */ void clear(void); /* 清屏 */ void display(void); /* 显示 */ void flipScreenVertically(); /* 倒转显示 */ 绘制功能函数如下: void setColor(OLEDDISPLAY_COLOR color); /* 设置当前画笔颜色 */ void fillRect(int16_t x, int16_t y, int16_t width, int16_t height);/* 填充矩阵 */ 文本功能函数如下: void drawString(int16_t x, int16_t y, String text); /* 显示字符串 */ void setFont(const uint8_t* fontData); /* 设置字体大小 */ 在使用前面罗列的函数前,首先实例化一个display对象,因为前面的函数都是display类中的成员函数,操作如下: SSD1306Wire display(OLED_ADDR, OLED_SDA_PIN, OLED_SCL_PIN, GEOMETRY_128_64, I2C_TWO, 800000); 上面函数的原型如下所示: SSD1306Wire(uint8_t _address, int _sda, int _scl, OLEDDISPLAY_GEOMETRY g, HW_I2C _i2cBus, int _frequency); 参数_address为SSD1306的7位器件地址,_sda为IIC通信的数据线,_scl为IIC通信的时钟线,g为屏幕的大小,_i2cBus为IIC总线,_frequency为IIC通信速率。 有了display对象就可以调用前面函数,使用以下格式: display.init(); 16.2 硬件设计 1.例程功能 使用IIC模式驱动OLED模块,不停的显示ASCII码和码值。 2. 硬件资源 1)USART0 U0TXD-IO43 U0RXD-IO44 2)XL9555 IIC_SDA-IO41 IIC_SCL-IO42 3)OLED IIC_SCL-IO4 IIC_SDA-IO5 D2-IO6 DC-IO38 RST-IO_05(XL9555) 3. 原理图 OLED相关原理图,如下图所示。 图16.2.1 OLED模块原理图
从上图可以看到,OV2640座子即CAMERA是可以接OLED模块进行使用的,由于这部分原理图本就是为摄像头设计的,所以那些名称看起来跟OLED模块没有关系,实际上可以利用现有硬件条件对IIC接口的OLED模块进行驱动。 在16.1.1小节也有对IIC接口的OLED模块连线情况进行说明,这里罗列一个IO对应关系表,方便大家更清楚去理解。 IIC接口OLED模块 | OV2640端子 | ESP32S3 | | | | | | | | | | | | |
表16.2.1 OLED模块IO表
在图16.2.1中,红框里面的引脚是IIC接口OLED模块用到的,而紫框里面的内容则是IIC接口OLED模块真正连接到ESP32-S3的引脚或连接到XL9555器件的引脚。 特别注意:OLED模块与ESP32-S3开发板的连接,要靠左插!,如下图所示。 图16.2.2 OLED模块与开发板连接示意图
16.3 软件设计 16.3.1 程序流程图 下面看看本实验的程序流程图: 图16.3.1 程序流程图
16.3.2 程序解析 1. OLED驱动代码这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。OLED驱动源码包括两个文件:oled.cpp和oled.h。 下面我们先解析oled.h的程序。对OLED模块IIC引脚和OLED器件地址做了相关定义。 #define OLED_SCL_PIN 4 #define OLED_SDA_PIN 5 #define OLED_D2_PIN 6 #define OLED_DC_PIN 38
#define OLED_ADDR 0X3C /* 7位器件地址 */ 我们选择使用IO4作为IIC的时钟线,IO5作为IIC的数据线,IO6为OLED_D2_PIN,IO38为OLED_DC_PIN。OLED的器件地址为0x3C。 由于会使用到RST引脚,而RST引脚是XL9555器件上的IO,这里便有OLED_RST宏用来控制RST引脚输出高低电平,实现OLED的硬件复位。 #define OLED_RST(x) xl9555_pin_set(OV_RESET, x ? IO_SET_HIGH : IO_SET_LOW) 接下来,我们来解析一下oled.cpp的程序,首先先来看一下实例化一个display对象操作,在前面也有提及到了,代码如下: SSD1306Wire display(OLED_ADDR, OLED_SDA_PIN, OLED_SCL_PIN, GEOMETRY_128_64, I2C_TWO, 800000); /* 定义OLED对象(显示异常,把800000降低) */ 上面涉及到的参数简单做一个说明,OLED_ADDR为器件地址0x3C,OLED_SDA_PIN为IIC通信数据线,OLED_SCL_PIN为IIC通信时钟线,GEOMETRY_128_64为显示器尺寸,I2C_TWO为硬件IIC1,800000为IIC通信频率。 我们继续看一下OLED模块的初始化函数oled_init,代码如下: /** * @param 无 * @retval 无 */ void oled_init(void) { /* 初始化OLED需要用到的引脚 */ pinMode(OLED_D2_PIN, INPUT_PULLUP); /* OLED模块的D2和D1引脚需要连接在一起,所以为了不干扰D1,设置D2为输入模式 */ pinMode(OLED_DC_PIN, OUTPUT); /* DC引脚跟地址有关 */ digitalWrite(OLED_DC_PIN, 0); /* DC引脚接GND,8位地址为0x78 */
xl9555_io_config(OV_RESET, IO_SET_OUTPUT); /* OLED模块的复位引脚连接到XL9555器件的P05引脚 */ xl9555_pin_set(OV_RESET, IO_SET_HIGH); /* 默认拉高复位引脚 */
/* 硬件复位OLED模块 */ OLED_RST(0); delay(100); OLED_RST(1); delay(100);
display.init(); /* 初始化OLED模块 */ display.flipScreenVertically(); /* 垂直翻转屏幕设置 */ display.setFont(ArialMT_Plain_24); /* 设置24号字体 */ display.drawString(0, 0, "ALIENTEK"); /* 24号字体ALIENTEK */ display.setFont(ArialMT_Plain_16); /* 设置16号字体 */ display.drawString(0, 24, "0.96' OLED TEST"); /* 16号字体0.96' OLED TEST */ display.setFont(ArialMT_Plain_10); /* 设置10号字体 */ display.drawString(0, 40, "ATOM 2023/12/1"); /* 10号字体ATOM 2023/12/1 */ display.drawString(0, 50, "ASCII:"); /* 10号字体ASCII: */ display.drawString(64, 50, "CODE:"); /* 10号字体CODE: */ display.display(); /* 将缓存数据写入到显示器 */ }
/** * @param 无 * @retval 无 */ void oled_show_demo(void) { char t = ' ';
while(1) { display.setColor(BLACK); /* 颜色设置黑色 */ display.fillRect(35, 50, 10, 15); /* 清除ASCII显示区域 */ display.fillRect(104, 50, 20, 15); /* 清除CODE显示区域 */ display.setColor(WHITE); /* 颜色设置白色 */ display.drawString(35, 50, String(t)); /* 显示ASCII字符 */ display.drawString(104, 50, String(t - 0)); /* 显示ASCII字符的码值 */ display.display(); /* 更新显示到OLED */
t++; if (t > '~') { t = ' '; }
delay(500); } } oled_init函数主要做了四件事情: 1、对涉及的IO口进行配置,比如OLED_D2_PIN,在硬件上人为将其OLED_SDA焊接上了,所以为了避免影响SDA线,所以要设置为上拉输入模式。OLED模块初始化时,通常需要硬件复位,这里也提供有RST引脚,只不过是连接在XL9555器件上。在这里就需要首先通过xl9555_io_config接口对RST引脚配置成输出模式,然后通过xl9555_pin_set函数接口设置默认输出高电平。随后,就可以通过调用在oled.h文件编写的OLED_RST宏产生复位信号,硬件复位OLED模块。 2、调用ssd1306库的init函数初始化OLED模块,主要就是发送OLED初始化序列完成其寄存器的配置并开启显示。 3、调用ssd1306库的功能函数去显示实验信息,setFont函数设置当前字体大小,drawString函数设置显示内容。注意:这些数据内容都会被存放到一个缓冲区,还未能显示在OLED上。 4、调用ssd1306库的display函数去更新显示。该函数的功能就是要把缓冲区的数据发送到OLED模块,这样才能更新数据的显示。 oled_show_demo函数主要就是调用drawString函数对ASCII码“ ”到“~”的字符以及码值进行显示,最后一定要调用display函数才能更新到OLED屏上。 2. 10_iic_oled.ino代码在10_iic_oled.ino里面编写如下代码: #include "oled.h" #include "uart.h" #include "xl9555.h"
/** * @brief 当程序开始执行时,将调用setup()函数,通常用来初始化变量、函数等 * @param 无 * @retval 无 */ void setup() { uart_init(0, 115200); /* 串口0初始化 */ xl9555_init(); /* IO扩展芯片初始化 */ oled_init(); /* OLED模块初始化 */ }
/** * @brief 循环函数,通常放程序的主体或者需要不断刷新的语句 * @param 无 * @retval 无 */ void loop() { oled_show_demo(); } 在setup函数中,调用uart_init函数完成串口初始化,调用xl9555_init函数完成XL9555初始化,然后调用oled_init函数完成OLED模块初始化。 在loop函数中,调用oled_show_demo函数实现ASCII字符更新显示。 16.4 下载验证 下载代码后,OLED模块显示ASCII字符集等信息,如下图所示。 图16.4.1 OLED显示效果
OLED显示了三种尺寸的字符:24*12(ALIENTEK)、16*8(0.96’ OLED TEST)和10*5(剩下的内容)。说明我们的实验是成功的,实现了三种不同尺寸ASCII字符的显示,在最后一行不停的显示ASCII字符以及其码值。
|