天空星HC32F4A0开发板驱动0.96寸IIC OLED屏(SSD1306)移植指南最近在天空星HC32F4A0开发板上做个小项目需要接个屏幕显示点信息就选了最常用的0.96寸OLED屏。这种小屏功耗低、显示清晰用IIC接口接线也简单但网上的例程大多是针对STM32的直接拿来用肯定不行。折腾了一下午总算把驱动移植成功了这里把完整的移植过程分享给大家手把手教你搞定。1. 准备工作了解你的OLED屏在开始移植之前咱们先搞清楚要驱动的对象是什么。这次用的是一块0.96英寸的单色OLED显示屏核心驱动芯片是SSD1306。这块屏有几个关键特点通信接口IIC也叫I2C只需要两根线SCL时钟线和SDA数据线就能通信非常节省IO口。供电电压3.3V正好和天空星开发板的IO电平匹配可以直接连接不需要电平转换。分辨率128x64像素虽然不大但显示文字和简单图形完全够用。物理接口4个2.54mm间距的排针分别是VCC电源、GND地、SCL时钟、SDA数据。注意不同厂家生产的0.96寸OLED屏引脚顺序可能不一样一定要确认你手上的屏幕引脚定义。最常见的顺序是1脚GND2脚VCC3脚SCL4脚SDA。接线前最好用万用表量一下。2. 获取原始驱动代码移植的第一步得有“原材料”。我用的驱动代码是从卖家提供的资料里找到的如果你手头没有可以按照原始文章里提到的链接去下载。拿到资料后你会发现里面有个【OLED】文件夹这就是我们要移植的核心。文件夹里通常包含这几个文件oled.c- OLED屏的驱动函数实现oled.h- 函数声明和宏定义oledfont.h- 字库数据ASCII字符的点阵可能还有sys.h、delay.h等依赖文件把这些文件先保存好接下来我们要把它们“改造”成适合HC32F4A0的样子。3. 创建工程并导入文件首先你得有一个能编译的天空星HC32F4A0基础工程。如果还没有可以去官方或者立创开发板LCKFB的网站下载一个空白工程或者点灯例程确保工程里至少有一个能用的毫秒级延时函数比如delay_ms。然后在你的工程目录下新建一个文件夹比如叫OLED把刚才那几个源文件oled.c、oled.h、oledfont.h复制进去。接着在你的IDE比如Keil MDK里把oled.c添加到工程的源文件组把oled.h和oledfont.h所在的路径添加到头文件包含路径里。这一步是告诉编译器“我这里有新的代码要编译头文件在这个地方找”。4. 修改头文件依赖解决第一个编译错误直接编译肯定会报错因为原来的代码是为别的芯片写的。咱们按照报错信息一个个来改。第一步替换系统头文件打开oled.h文件找到类似#include sys.h的语句。在HC32的工程里我们通常用官方的底层库头文件。把它改成#include hc32_ll.h // 替换原来的 sys.h这个头文件包含了HC32芯片的GPIO、时钟等底层寄存器的定义。第二步替换延时头文件打开oled.c文件找到#include delay.h。在我们的工程里延时函数可能定义在board.h或bsp_delay.h里。根据你的工程实际情况修改比如#include board.h // 替换原来的 delay.h假设延时函数在board.h中声明第三步定义数据类型原来的代码可能用了u8、u16、u32这样的简写来定义变量类型。我们需要在oled.h里确保这些类型有定义。通常在HC32的工程里这些类型已经在stdint.h里定义了uint8_t、uint16_t、uint32_t。为了兼容我们可以在oled.h文件的开头#include语句之后加上#ifndef u8 #define u8 uint8_t #endif #ifndef u16 #define u16 uint16_t #endif #ifndef u32 #define u32 uint32_t #endif这样如果其他地方没定义u8我们就把它指向uint8_t确保代码能认。5. 硬件连接与引脚配置这是移植最关键的一步告诉单片机OLED屏接在哪两个引脚上。硬件连接把OLED屏的4个引脚接到天空星开发板上VCC- 开发板的3.3V引脚GND- 开发板的GND引脚SCL- 我们选择的GPIO引脚例如PB10SDA- 我们选择的GPIO引脚例如PB11我选择了PB10和PB11你可以根据自己板子的空闲情况选择任意两个GPIO但最好避开已经用于特殊功能如串口、JTAG的引脚。软件配置我们需要修改代码把引脚定义改成我们实际连接的。找到并修改引脚定义文件。在原始驱动代码里可能有一个lcd_init.h或者直接在oled.h里有引脚定义。我们需要修改它。将定义修改为HC32的格式。在oled.h或新建一个oled_conf.h文件中添加或修改如下代码//-----------------GPIO端口定义---------------- #define OLED_GPIO_PORT GPIO_PORT_B // 使用B端口 #define OLED_SCL_PIN GPIO_PIN_10 // SCL时钟线接PB10 #define OLED_SDA_PIN GPIO_PIN_11 // SDA数据线接PB11 //-----------------OLED端口操作宏定义---------------- // 这些宏定义了如何控制引脚输出高低电平 #define OLED_SCL_Clr() GPIO_ResetPins(OLED_GPIO_PORT, OLED_SCL_PIN) // SCL拉低 #define OLED_SCL_Set() GPIO_SetPins(OLED_GPIO_PORT, OLED_SCL_PIN) // SCL拉高 #define OLED_SDA_Clr() GPIO_ResetPins(OLED_GPIO_PORT, OLED_SDA_PIN) // SDA拉低 #define OLED_SDA_Set() GPIO_SetPins(OLED_GPIO_PORT, OLED_SDA_PIN) // SDA拉高这里用到的GPIO_SetPins和GPIO_ResetPins是HC32官方驱动库提供的函数用于设置引脚输出高或低电平。6. 重写OLED初始化函数核心步骤原来的OLED_Init()函数里包含了GPIO初始化和SSD1306芯片的初始化序列。我们需要把GPIO初始化的部分改成HC32库函数的写法。打开oled.c找到void OLED_Init(void)函数将其内容替换为如下代码void OLED_Init(void) { stc_gpio_init_t stcGpioInit; // 定义GPIO初始化结构体 // 解除GPIO外设的写保护允许配置 LL_PERIPH_WE(LL_PERIPH_GPIO); // 将结构体变量初始化为默认值 (void)GPIO_StructInit(stcGpioInit); // 配置GPIO结构体参数 stcGpioInit.u16PinState PIN_STAT_SET; // 初始输出高电平 stcGpioInit.u16PinDir PIN_DIR_OUT; // 设置为输出模式 stcGpioInit.u16PullUp PIN_PU_ON; // 使能内部上拉电阻IIC总线通常需要上拉 // 初始化SCL引脚 (PB10) (void)GPIO_Init(OLED_GPIO_PORT, OLED_SCL_PIN, stcGpioInit); // 初始化SDA引脚 (PB11) (void)GPIO_Init(OLED_GPIO_PORT, OLED_SDA_PIN, stcGpioInit); // 给OLED一点上电稳定时间 delay_ms(200); // 以下是SSD1306芯片的初始化命令序列这部分是通用的不需要修改 OLED_WR_Byte(0xAE,OLED_CMD);// 关闭显示 OLED_WR_Byte(0x00,OLED_CMD);// 设置列地址低4位 OLED_WR_Byte(0x10,OLED_CMD);// 设置列地址高4位 OLED_WR_Byte(0x40,OLED_CMD);// 设置显示起始行 OLED_WR_Byte(0x81,OLED_CMD);// 对比度设置命令 OLED_WR_Byte(0xCF,OLED_CMD);// 对比度值 (0xCF) OLED_WR_Byte(0xA1,OLED_CMD);// 段重映射设置 (0xA1正常0xA0左右翻转) OLED_WR_Byte(0xC8,OLED_CMD);// 扫描方向设置 (0xC8正常0xC0上下翻转) OLED_WR_Byte(0xA6,OLED_CMD);// 正常显示 (0xA6正常0xA7反色) OLED_WR_Byte(0xA8,OLED_CMD);// 多路复用率设置命令 OLED_WR_Byte(0x3f,OLED_CMD);// 多路复用率值 (0x3F对应64行) OLED_WR_Byte(0xD3,OLED_CMD);// 显示偏移命令 OLED_WR_Byte(0x00,OLED_CMD);// 显示偏移值 (0) OLED_WR_Byte(0xd5,OLED_CMD);// 显示时钟分频/振荡频率命令 OLED_WR_Byte(0x80,OLED_CMD);// 分频比设置时钟为100帧/秒 OLED_WR_Byte(0xD9,OLED_CMD);// 预充电周期命令 OLED_WR_Byte(0xF1,OLED_CMD);// 预充电15个时钟放电1个时钟 OLED_WR_Byte(0xDA,OLED_CMD);// COM引脚硬件配置命令 OLED_WR_Byte(0x12,OLED_CMD);// COM引脚配置值 (0x12) OLED_WR_Byte(0xDB,OLED_CMD);// VCOMH电平命令 OLED_WR_Byte(0x40,OLED_CMD);// VCOMH电平值 (0.77*VCC) OLED_WR_Byte(0x20,OLED_CMD);// 内存地址模式设置命令 OLED_WR_Byte(0x02,OLED_CMD);// 页地址模式 (0x02) OLED_WR_Byte(0x8D,OLED_CMD);// 电荷泵设置命令 OLED_WR_Byte(0x14,OLED_CMD);// 使能电荷泵 (0x14) OLED_WR_Byte(0xA4,OLED_CMD);// 全部点亮关闭 (0xA4) OLED_WR_Byte(0xA6,OLED_CMD);// 非反色显示 (0xA6) OLED_Clear(); // 清屏 OLED_WR_Byte(0xAF,OLED_CMD); // 最后开启显示 }提示初始化序列里那一长串OLED_WR_Byte命令是SSD1306芯片手册规定的用来设置显示参数比如对比度、扫描方向等。这部分代码是通用的无论用什么单片机只要驱动SSD1306这些命令都一样所以千万不要修改这些十六进制数。7. 修改IIC延时函数SSD1306的IIC通信时序对延时有一定要求。原来的驱动里有一个IIC_delay()函数我们需要根据HC32的时钟速度来调整。在oled.c文件里找到void IIC_delay(void)函数它里面可能调用了一个微秒延时函数。我们需要把它改成我们工程里已有的微秒延时函数。假设我们的工程里有一个delay_us()函数void IIC_delay(void) { delay_us(15); // 延时大约15微秒满足IIC时序要求 }这个延时值15us是一个经验值保证了SCL时钟信号的频率在IIC协议允许的范围内标准模式100kHz以内。如果后续通信不稳定可以适当调整这个值。8. 编写主函数进行测试所有底层修改完成后就可以写个简单的程序来测试了。打开你的main.c文件在while(1)主循环之前初始化OLED然后在循环里显示一些内容。#include board.h #include oled.h // 包含OLED驱动头文件 int32_t main(void) { // 硬件初始化时钟、延时等 board_init(); // 初始化OLED OLED_Init(); OLED_Clear(); // 清屏 while(1) { // 在屏幕不同位置显示不同大小的“ABC” OLED_ShowString(0, 0, (uint8_t *)Hello HC32!, 8, 1); // 在(0,0)坐标用8号字体显示 OLED_ShowString(0, 16, (uint8_t *)OLED Test, 16, 1); // 在(0,16)坐标用16号字体显示 // 刷新显示将缓存数据写入OLED OLED_Refresh(); // 延时一段时间 delay_ms(1000); // 可以在这里加个清屏实现闪烁效果 // OLED_Clear(); // delay_ms(500); } }9. 编译、下载与调试编译点击编译按钮确保0错误0警告。连接硬件用杜邦线将OLED屏的SCL、SDA、VCC、GND分别连接到开发板你定义的引脚和3.3V、GND。务必检查连线是否正确电源不要接反下载程序通过调试器将程序下载到天空星HC32F4A0开发板。上电观察给开发板上电你应该能看到OLED屏亮起并显示“Hello HC32!”和“OLED Test”字样。如果屏幕不亮检查电源和地线是否接好用万用表量一下OLED屏的VCC和GND之间是否有3.3V电压。检查SCL和SDA线是否接对是否接触不良。如果屏幕亮但无显示检查OLED_Init()函数中的GPIO初始化部分确认引脚配置是否正确。用逻辑分析仪或示波器抓一下SCL和SDA的波形看是否有IIC通信信号。如果没有检查IIC_delay函数和底层OLED_SCL_Clr/Set、OLED_SDA_Clr/Set宏定义是否正确操作了引脚。按照上面这些步骤走下来驱动移植基本就成功了。整个过程中最关键的其实就是两步一是把GPIO的操作换成HC32的库函数方式二是保证IIC的时序延时正确。以后要在天空星开发板上用其他IIC设备比如温湿度传感器、陀螺仪思路也是一样的。