0.96寸IIC单色OLED屏(SSD1306)驱动移植与显示应用实战
本文详细介绍了0.96寸IIC单色OLED屏(SSD1306)的驱动移植与显示应用实战。以具体开发板为例,从硬件接线、驱动源码获取与修改,到核心显示函数解析与调试,手把手指导完成从零到一的显示应用开发,帮助开发者快速掌握嵌入式小型显示屏的集成与应用。
0.96寸IIC单色OLED屏(SSD1306)驱动移植与显示应用实战
最近在做一个需要小型显示屏的项目,手头正好有一块0.96寸的OLED屏,驱动芯片是SSD1306,接口是IIC。这种小屏功耗低、体积小,显示效果也不错,非常适合用在嵌入式设备上做状态显示或者简单的人机交互。但网上的驱动代码五花八门,移植到自己的开发板上总会出现各种问题,比如引脚不对、时序不对、显示乱码等等。
今天我就以“地奇星”开发板为例,手把手带你把这套OLED驱动移植过去,并且实现字符、汉字、图形甚至图片的显示。整个过程我会把每一步的原理和注意事项讲清楚,让你不仅能“抄作业”,更能理解背后的逻辑,以后换别的开发板也能自己搞定。
1. 认识我们的“小屏幕”:硬件与接线
在开始写代码之前,咱们先得把硬件搞清楚。这块0.96寸OLED屏的核心是SSD1306驱动芯片,它通过IIC总线和我们单片机通信。
1.1 屏幕关键参数
根据资料,这块屏的主要参数如下:
| 参数项 | 规格说明 |
|---|---|
| 工作电压 | 3.3V (非常重要,接5V可能会烧坏!) |
| 工作电流 | 约9mA (非常省电) |
| 模块尺寸 | 27.3mm x 27.8mm (非常小巧) |
| 分辨率 | 128 x 64 像素 (单色,每个像素点只有亮或灭两种状态) |
| 驱动芯片 | SSD1306 |
| 通信协议 | IIC (只需要两根线) |
| 管脚数量 | 4 Pin (2.54mm间距排针) |
四个引脚分别是:GND (地), VCC (电源,接3.3V), SCL (IIC时钟线), SDA (IIC数据线)。
注意:一定要确认你的开发板IO口是3.3V电平的。如果是5V单片机,需要在IIC线上加电平转换电路,或者寻找支持5V的屏幕型号,否则可能通信失败甚至损坏屏幕。
1.2 与开发板连接
接下来就是把屏幕和“地奇星”开发板连起来。连接非常简单,就像接四根导线:
| OLED屏幕引脚 | 连接到开发板 |
|---|---|
| GND | 任意GND引脚 |
| VCC | 3.3V电源引脚 |
| SCL | P407 (这是IIC的时钟线,你可以根据实际情况换到其他IO) |
| SDA | P408 (这是IIC的数据线,需和SCL配对使用) |
这里我选择P407和P408作为IIC引脚,你完全可以根据自己板子的资源分配情况,换成其他任意两个GPIO口,只要在代码里改一下宏定义就行。IIC通信对时序要求严格,但对引脚没有特殊要求,普通IO口模拟即可。
硬件连接好之后,咱们就可以进入软件部分了。
2. 驱动移植:把代码“搬”到你的工程里
移植驱动,说白了就是把别人写好的、能在别的板子上跑的代码,修改成能在你的板子上跑。这个过程一般分三步:拿代码、改引脚、调时序。
2.1 获取驱动源码
首先,你需要拿到OLED的驱动源码。通常包含以下几个关键文件:
oled.h和oled.c:核心驱动文件,包含初始化、画点、显示字符等函数。oledfont.h:字库文件,里面存放了显示ASCII字符和汉字需要的点阵数据。- 可能还有
bmp.h之类的图片数据文件。
你可以从提供的资料链接下载,或者从其他开源项目(如正点原子、野火等)的例程中找到针对SSD1306的驱动。把oled.h, oled.c, oledfont.h这三个文件复制到你自己的工程目录下,比如/User/OLED/文件夹里。
然后在你的IDE(如Keil, IAR等)中,将这些文件添加到工程里。以Keil为例,在项目窗口右键点击你的工程分组,选择“Add Existing Files to Group...”,然后找到并添加这几个.c和.h文件。
2.2 修改引脚配置(最关键的一步)
这是移植的核心。原始驱动代码里,控制SCL和SDA的宏定义是针对原作者板子的,我们必须改成自己板子的引脚。
打开oled.h文件,找到下面这部分的代码:
//-----------------OLED端口定义----------------
#define SCREEN_SCL_PIN BSP_IO_PORT_04_PIN_07 //SCL
#define SCREEN_SDA_PIN BSP_IO_PORT_04_PIN_08 //SDA
#define OLED_SCL_Clr() R_IOPORT_PinWrite(&g_ioport_ctrl, SCREEN_SCL_PIN, 0)//SCL
#define OLED_SCL_Set() R_IOPORT_PinWrite(&g_ioport_ctrl, SCREEN_SCL_PIN, 1)
#define OLED_SDA_Clr() R_IOPORT_PinWrite(&g_ioport_ctrl, SCREEN_SDA_PIN, 0)//DIN
#define OLED_SDA_Set() R_IOPORT_PinWrite(&g_ioport_ctrl, SCREEN_SDA_PIN, 1)
这里的BSP_IO_PORT_04_PIN_07和BSP_IO_PORT_04_PIN_08是“地奇星”开发板对P407和P408引脚的宏定义。R_IOPORT_PinWrite是这个开发板库函数里控制引脚高低电平的函数。
你需要根据自己使用的开发板和单片机型号来修改:
- 如果你用的也是“地奇星”开发板,并且接的正是P407和P408,那么这部分代码可以不用改。
- 如果你用的是STM32,你可能需要改成类似
GPIO_PIN_6这样的定义,并且将R_IOPORT_PinWrite替换成HAL库的HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET),或者标准库的GPIO_SetBits(GPIOB, GPIO_Pin_6)。 - 如果你用的是Arduino,那就更简单了,直接
digitalWrite(SCL_PIN, HIGH)。
原则就是:找到你开发环境下控制某个GPIO输出高电平(1)和低电平(0)的方法,然后替换掉上面的四个宏定义。
2.3 修改延时函数
IIC通信对时序有严格要求,所以驱动里用到了微秒级延时delay_us。在oled.h的开头,我们看到它用开发板的软件延时函数R_BSP_SoftwareDelay来实现了各种延时:
#ifndef delay_ms
#define delay_ms(x) R_BSP_SoftwareDelay(x, BSP_DELAY_UNITS_MILLISECONDS)
#endif
#ifndef delay_1ms
#define delay_1ms(x) R_BSP_SoftwareDelay(x, BSP_DELAY_UNITS_MILLISECONDS)
#endif
#ifndef delay_us
#define delay_us(x) R_BSP_SoftwareDelay(x, BSP_DELAY_UNITS_MICROSECONDS)
#endif
#ifndef delay_1us
#define delay_1us(x) R_BSP_SoftwareDelay(x, BSP_DELAY_UNITS_MICROSECONDS)
#endif
同样,你需要根据你的平台替换这些延时函数。例如在STM32的HAL库中,delay_us通常可以用HAL_Delay()(毫秒级)配合系统滴答定时器自己封装一个。在无操作系统的环境下,经常用空循环来实现微秒延时,但要注意不同主频的CPU循环次数需要调整。
提示:IIC时序对
delay_us的精度要求不是特别高,差一点通常也能工作。如果屏幕初始化失败但接线和引脚确认无误,可以尝试稍微调整IIC_delay函数中的延时时间(比如从3微秒调到5微秒)。
完成以上两步,驱动层的主要修改就完成了。接下来我们看看怎么使用这些驱动函数来显示内容。
3. 驱动解析与应用:让屏幕“活”起来
移植好驱动后,oled.c里提供了丰富的函数。咱们挑几个最常用的,看看它们是怎么工作的,以及怎么用。
3.1 核心机制:显存与刷新
SSD1306芯片内部有一块对应的显示缓存(GRAM)。我们所有画点、画线、写字的操作,实际上都是在修改单片机内存中的一个二维数组OLED_GRAM[144][8](这个数组大小是128x64比特的另一种组织方式)。
当你调用OLED_DrawPoint(10, 20, 1)在坐标(10,20)画一个亮点时,程序会计算这个点对应OLED_GRAM数组中的哪一位,并将其置1。注意,这时候屏幕本身还没有变化!
必须调用OLED_Refresh()函数,这个函数才会把整个OLED_GRAM数组的数据,通过IIC总线,一股脑地发送到SSD1306芯片内部的GRAM中,屏幕才会更新显示。这种“先修改缓存,再统一刷新”的方式效率很高。
所以,一个典型的显示流程是:
OLED_Init(); // 1. 初始化屏幕
OLED_Clear(); // 2. 清空缓存(全黑)
OLED_DrawPoint(...); // 3. 在缓存上画图
OLED_ShowString(...); // 4. 在缓存上写字
OLED_Refresh(); // 5. 将缓存内容刷到屏幕,真正显示出来
3.2 基础显示函数怎么用
oled.h中声明了很多函数,我们来看看几个最重要的:
1. 初始化与清屏
void OLED_Init(void); // 初始化OLED,必须最先调用
void OLED_Clear(void); // 清屏(全黑)
void OLED_DisPlay_On(void); // 开启显示
void OLED_DisPlay_Off(void); // 关闭显示(省电模式)
2. 画图函数
OLED_DrawPoint(u8 x, u8 y, u8 t): 在(x,y)坐标画点,t=1点亮,t=0熄灭。坐标x范围0~127,y范围0~63。OLED_DrawLine(u8 x1,u8 y1,u8 x2,u8 y2,u8 mode): 从(x1,y1)到(x2,y2)画直线。OLED_DrawCircle(u8 x,u8 y,u8 r): 以(x,y)为圆心,画半径为r的圆。OLED_ShowPicture(u8 x,u8 y,u8 sizex,u8 sizey,u8 BMP[],u8 mode): 显示一张位图。BMP[]是一个数组,存放了图片的点阵数据。
3. 显示字符与字符串 这是最常用的功能。
void OLED_ShowChar(u8 x, u8 y, u8 chr, u8 size1, u8 mode);
void OLED_ShowString(u8 x, u8 y, u8 *chr, u8 size1, u8 mode);
void OLED_ShowNum(u8 x, u8 y, u32 num, u8 len, u8 size1, u8 mode);
void OLED_ShowChinese(u8 x, u8 y, u8 num, u8 size1, u8 mode);
x, y: 显示起始的左上角坐标。chr或*chr: 要显示的字符或字符串。size1: 字体大小,支持8(6x8), 12(6x12), 16(8x16), 24(12x24)。注意,这里参数是字符高度,宽度通常是高度的一半。mode:0表示反色显示(黑底白字),1表示正常显示(白底黑字,OLED上是亮底暗字)。num(在ShowChinese中): 不是汉字本身,而是汉字在字库数组Hzk1,Hzk2等中的索引号。你需要事先在oledfont.h里定义好你的字库,并知道每个汉字的编号。
3.3 实战:编写你的显示程序
现在,我们参考提供的例程,在main函数或者你的应用任务里写一个显示流程。下面我写一个简单的例子,演示如何显示一段文字和数字:
#include "oled.h"
int main(void)
{
// 系统初始化...
HAL_Init();
SystemClock_Config();
// ... 其他外设初始化
// 1. 初始化OLED
OLED_Init();
OLED_ColorTurn(0); //0正常显示,1 反色显示
OLED_DisplayTurn(0); //0正常显示 1 屏幕翻转显示
// 2. 清屏
OLED_Clear();
// 3. 在缓存上绘制内容
OLED_ShowString(0, 0, "Hello OLED!", 16, 1); // 在(0,0)显示16像素高的字符串
OLED_ShowString(0, 20, "Voltage:", 16, 1);
OLED_ShowNum(70, 20, 330, 3, 16, 1); // 在(70,20)显示3位数字330
OLED_ShowString(100, 20, "mV", 16, 1);
// 画一条分割线
OLED_DrawLine(0, 40, 127, 40, 1);
// 显示一个汉字(假设“测”字在字库中的索引是10)
OLED_ShowChinese(0, 48, 10, 16, 1);
OLED_ShowString(18, 48, "Test OK!", 16, 1);
// 4. 最关键的一步:刷新到屏幕
OLED_Refresh();
while(1)
{
// 你可以在这里动态更新显示内容,比如刷新传感器数值
// 注意:每次更新后都要调用OLED_Refresh()
}
}
4. 调试与常见问题排查
移植过程中,最容易出问题的地方就几个,咱们来盘点一下:
问题1:屏幕完全不亮,没有任何反应。
- 检查电源:首先用万用表量一下VCC和GND之间是不是3.3V。确认屏幕供电正常。
- 检查接线:确认SCL、SDA、GND、VCC四根线没有接错、没有虚焊。
- 检查初始化:确保
OLED_Init()函数被正确调用。可以在OLED_WR_Byte函数里设置断点,看是否有IIC数据发出。
问题2:屏幕亮但显示乱码、花屏。
- 检查IIC时序:这是最常见的原因。可能是延时
delay_us不准确。尝试增大或减小oled.c文件中IIC_delay函数里的延时值。 - 检查显存刷新:确认在修改显示内容后,调用了
OLED_Refresh()。只画点不刷新,屏幕是不会变的。 - 检查字库:如果只是汉字显示乱码而英文正常,那很可能是
oledfont.h里的汉字点阵数据不对,或者OLED_ShowChinese函数中使用的字库索引num不对。
问题3:显示内容错位或镜像。
- 使用旋转函数:
OLED_DisplayTurn(1)可以让屏幕显示旋转180度。OLED_ColorTurn(1)可以反色显示。如果你发现显示是反的,可以调用这两个函数调整。
问题4:编译通不过,提示引脚函数未定义。
- 检查宏定义替换:回头仔细检查
oled.h里关于OLED_SCL_Set,OLED_SDA_Clr等宏定义,是否替换成了你当前平台正确的GPIO控制语句。确保相关的GPIO时钟和模式初始化在别处已经完成(通常在主函数初始化阶段)。
最后,把工程编译、下载到开发板,上电。如果一切顺利,你应该能看到OLED屏上清晰地显示出你设定的文字和图形。这个过程虽然会遇到一些小麻烦,但成功点亮的那一刻,成就感还是满满的。这套驱动函数功能比较全,掌握了基本显示后,你完全可以尝试用它来画个简单的图形界面,或者实时显示传感器数据,让你的嵌入式项目更加生动。
更多推荐
所有评论(0)