1.3寸单色OLED显示屏驱动移植与显示应用实战最近在做一个基于TI MSPM0G3507的小型环境监测项目需要一块屏幕来实时显示温湿度数据。考虑到成本、功耗和显示效果我最终选择了1.3寸的单色OLED显示屏。这种屏幕功耗低、对比度高非常适合嵌入式设备。但网上找到的驱动代码往往不能直接用在MSPM0上需要自己动手移植。今天我就把整个驱动移植和显示温湿度的过程整理出来手把手教你怎么做。即使你是第一次接触OLED屏跟着步骤走也能搞定。1. 准备工作了解你的OLED屏在开始写代码之前咱们得先搞清楚要驱动的对象是什么。我用的这块1.3寸OLED屏核心驱动芯片是SSD1306。这是一款非常常见的OLED驱动芯片支持128x64的分辨率。这种屏幕通常有两种通信方式I2C和SPI。我手头这块是SPI接口的接线更简单速度也更快。SPI接口一般需要4根线SCLK时钟线由主设备MCU产生。SDA数据线用于发送数据。DC数据/命令选择线。这条线很关键告诉屏幕接下来发送的是命令配置参数还是数据要显示的像素点。CS片选线低电平有效选中这块屏幕。RES复位线可选有些模块没有靠上电复位。注意不同厂家生产的模块引脚命名可能略有不同比如SDA可能叫MOSIDC可能叫D/C但功能都一样。拿到屏幕第一件事就是看卖家提供的资料确认引脚定义。2. 硬件连接把屏幕和开发板连起来硬件连接是第一步千万不能接错。我使用的是TI MSPM0G3507开发板连接关系如下OLED屏引脚MSPM0G3507引脚功能说明VCC3.3V电源正极务必接3.3V接5V会烧屏GNDGND电源地SCLKPA2 (可配置为SPI CLK)SPI时钟SDAPA4 (可配置为SPI MOSI)SPI数据输出DCPA1 (任意GPIO)数据/命令选择CSPA3 (任意GPIO)片选低电平选中RESPA0 (任意GPIO)复位引脚如果模块有连接好之后建议用万用表测一下VCC和GND之间没有短路再上电。3. 软件驱动移植让屏幕亮起来驱动移植的核心就是根据SSD1306芯片的数据手册用SPI发送正确的命令和数据。我们分几步走。3.1 底层GPIO与SPI初始化首先我们需要配置MSPM0的引脚功能和SPI外设。这里我使用TI的SysConfig图形化工具来配置非常直观。在CCS或你的IDE工程中打开.syscfg文件。添加一个SPI外设配置选择为主机Master模式。将PA2分配给CLKPA4分配给MOSI。注意OLED屏是只写的我们不需要MISO引脚。配置SPI的时钟极性CPOL和相位CPHA。对于SSD1306通常模式0CPOL0 CPHA0或模式3CPOL1 CPHA1都可以。我习惯用模式0。时钟频率不用太高先设置成1MHz左右就够用了。另外将PA1、PA3、PA0配置为普通的GPIO输出引脚分别对应DC、CS和RES。保存配置工具会自动生成初始化代码到ti_msp_dl_config.c/h文件中。生成的代码会帮我们初始化好SPI和GPIO。接下来我们需要封装几个最基本的底层函数用于控制引脚和发送数据。// oled_gpio.c #include board.h // 此头文件包含了由SysConfig生成的引脚定义 // 假设SysConfig为我们的引脚生成了以下宏定义 // #define OLED_DC_PORT GPIOA // #define OLED_DC_PIN GPIO_PIN_1 // #define OLED_CS_PORT GPIOA // #define OLED_CS_PIN GPIO_PIN_3 // #define OLED_RES_PORT GPIOA // #define OLED_RES_PIN GPIO_PIN_0 // SPI实例定义为SPI_0 // 控制引脚电平的宏简洁明了 #define OLED_DC_HIGH() DL_GPIO_setPins(OLED_DC_PORT, OLED_DC_PIN) #define OLED_DC_LOW() DL_GPIO_clearPins(OLED_DC_PORT, OLED_DC_PIN) #define OLED_CS_HIGH() DL_GPIO_setPins(OLED_CS_PORT, OLED_CS_PIN) #define OLED_CS_LOW() DL_GPIO_clearPins(OLED_CS_PORT, OLED_CS_PIN) #define OLED_RES_HIGH() DL_GPIO_setPins(OLED_RES_PORT, OLED_RES_PIN) #define OLED_RES_LOW() DL_GPIO_clearPins(OLED_RES_PORT, OLED_RES_PIN) // 复位OLED void OLED_Reset(void) { OLED_RES_LOW(); delay_ms(100); // 保持低电平至少一段时间 OLED_RES_HIGH(); delay_ms(100); // 等待复位完成 } // 写命令DC引脚置低然后通过SPI发送 void OLED_Write_Cmd(uint8_t cmd) { OLED_DC_LOW(); // 表示接下来是命令 OLED_CS_LOW(); // 选中OLED DL_SPI_transmitDataBlocking(SPI_0_INST, cmd, 1); // 发送一个字节的命令 OLED_CS_HIGH(); // 取消选中 } // 写数据DC引脚置高然后通过SPI发送 void OLED_Write_Data(uint8_t data) { OLED_DC_HIGH(); // 表示接下来是数据 OLED_CS_LOW(); // 选中OLED DL_SPI_transmitDataBlocking(SPI_0_INST, data, 1); // 发送一个字节的数据 OLED_CS_HIGH(); // 取消选中 }3.2 SSD1306初始化序列屏幕要正常工作需要一系列初始化命令来设置对比度、扫描方式、显示开关等。这些命令序列在SSD1306的数据手册里可以找到网上也有很多现成的代码。我们把它封装成一个初始化函数。// oled.c void OLED_Init(void) { // 1. 硬件复位 OLED_Reset(); // 2. 发送初始化命令序列 OLED_Write_Cmd(0xAE); // 关闭显示 OLED_Write_Cmd(0xD5); // 设置显示时钟分频比/振荡器频率 OLED_Write_Cmd(0x80); OLED_Write_Cmd(0xA8); // 设置多路复用率 (1 to 64) OLED_Write_Cmd(0x3F); // 对应64行 OLED_Write_Cmd(0xD3); // 设置显示偏移 OLED_Write_Cmd(0x00); // 无偏移 OLED_Write_Cmd(0x40); // 设置显示起始行 OLED_Write_Cmd(0x8D); // 电荷泵设置 OLED_Write_Cmd(0x14); // 使能电荷泵必须否则屏幕不亮 OLED_Write_Cmd(0x20); // 设置内存地址模式 OLED_Write_Cmd(0x00); // 水平地址模式方便我们后续画图 OLED_Write_Cmd(0xA1); // 段重映射设置 (0xA1 左右反置0xA0 正常) OLED_Write_Cmd(0xC8); // 扫描方向设置 (0xC8 上下反置0xC0 正常) // 根据你的屏幕显示方向可能需要调整这两条命令 OLED_Write_Cmd(0xDA); // 设置COM引脚硬件配置 OLED_Write_Cmd(0x12); OLED_Write_Cmd(0x81); // 设置对比度控制 OLED_Write_Cmd(0xCF); // 对比度值 (0x00 ~ 0xFF) OLED_Write_Cmd(0xD9); // 设置预充电周期 OLED_Write_Cmd(0xF1); OLED_Write_Cmd(0xDB); // 设置VCOMH电压倍率 OLED_Write_Cmd(0x40); OLED_Write_Cmd(0xA4); // 全部像素点正常显示 OLED_Write_Cmd(0xA6); // 设置非反相显示 OLED_Write_Cmd(0xAF); // 开启显示 // 3. 清屏 OLED_Clear(); } // 清屏函数将整个显存填充为0黑色 void OLED_Clear(void) { uint8_t i, j; for(j0; j8; j) // 共8页Page每页8行共64行 { OLED_Write_Cmd(0xB0 j); // 设置页地址Page0~Page7 OLED_Write_Cmd(0x00); // 设置列地址低4位 OLED_Write_Cmd(0x10); // 设置列地址高4位 for(i0; i128; i) // 每页有128列 { OLED_Write_Data(0x00); // 写入0熄灭所有像素 } } }调用OLED_Init()函数后如果硬件连接和代码正确屏幕应该会亮起但可能是全黑或全白取决于初始化设置并且执行OLED_Clear()后会变成全黑。3.3 实现基础绘图函数屏幕能亮了接下来就要能在指定位置显示内容。我们需要实现设置光标和画点的函数。SSD1306的显存可以看作一个128列 x 8页的矩阵。每一页管理8行像素一个字节的8个bit。所以坐标 (X, Y) 对应的显存位置需要换算。// oled.c // 全局显存用于实现局部刷新避免全屏刷新闪烁 static uint8_t OLED_GRAM[128][8]; // 设置光标位置 (x: 0~127, y: 0~63) void OLED_Set_Pos(uint8_t x, uint8_t y) { OLED_Write_Cmd(0xB0 (y / 8)); // 设置页地址 OLED_Write_Cmd(((x 0xF0) 4) | 0x10); // 设置列地址高4位 OLED_Write_Cmd(x 0x0F); // 设置列地址低4位 } // 在指定坐标画点 (color: 1-点亮0-熄灭) void OLED_DrawPoint(uint8_t x, uint8_t y, uint8_t color) { uint8_t page, bit_pos; if(x 128 || y 64) return; // 超出范围 page y / 8; bit_pos y % 8; if(color) OLED_GRAM[x][page] | (1 bit_pos); // 对应位置1 else OLED_GRAM[x][page] ~(1 bit_pos); // 对应位置0 // 更新到屏幕局部刷新只更新一个字节 OLED_Set_Pos(x, page * 8); OLED_Write_Data(OLED_GRAM[x][page]); } // 刷新整个显存到屏幕 void OLED_Refresh(void) { uint8_t i, j; for(j0; j8; j) { OLED_Set_Pos(0, j*8); for(i0; i128; i) { OLED_Write_Data(OLED_GRAM[i][j]); } } }有了画点函数我们就可以在此基础上构建显示字符、字符串、数字、直线、矩形等高级函数。这里以显示一个6x8的小字符为例// 字模数据取模软件生成这里以字符‘A’为例 static const uint8_t Font6x8[][6] { {0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C}, // A // ... 其他字符 }; // 在指定位置显示一个字符 (size: 1-6x8, 2-8x16这里实现6x8) void OLED_ShowChar(uint8_t x, uint8_t y, char chr, uint8_t size, uint8_t color) { uint8_t i, j, temp; if(size 1) { if(chr || chr ~) return; // 只支持可见ASCII字符 chr chr - ; // 找到字模数组中的索引 for(i0; i6; i) // 字符宽度6像素 { temp Font6x8[chr][i]; // 获取一列像素数据 for(j0; j8; j) // 字符高度8像素 { if(temp 0x01) OLED_DrawPoint(xi, yj, color); else OLED_DrawPoint(xi, yj, !color); temp 1; } } } } // 显示字符串 void OLED_ShowString(uint8_t x, uint8_t y, char *str, uint8_t size, uint8_t color) { while(*str ! \0) { OLED_ShowChar(x, y, *str, size, color); x 6; // 6x8字体字符间距1像素所以x7 if(x 122) // 换行判断简单处理 { x 0; y 8; } str; } } // 显示数字整数 void OLED_ShowNum(uint8_t x, uint8_t y, uint32_t num, uint8_t len, uint8_t size, uint8_t color) { char str[12]; sprintf(str, %d, num); // 简单转换实际项目建议用更安全的函数 OLED_ShowString(x, y, str, size, color); }4. 实战应用显示温湿度数据驱动层搞定后应用层就简单了。假设我们已经有了一个能读取DHT11温湿度传感器的模块就像原始文章里那样我们只需要在主循环里读取数据然后调用OLED显示函数即可。// main.c #include board.h #include dht11.h #include oled.h #include stdio.h int main(void) { // 开发板初始化 board_init(); delay_ms(1000); // 等待系统稳定 // OLED初始化 OLED_Init(); OLED_ShowString(0, 0, OLED DHT11 Test, 1, 1); OLED_ShowString(0, 16, Temp:, 1, 1); OLED_ShowString(0, 32, Humi:, 1, 1); OLED_Refresh(); // DHT11初始化如果需要 // DHT11_Init(); printf(System Start...\r\n); while(1) { // 1. 读取DHT11数据 if(DHT11_Read_Data() ! 0) // 读取成功 { float temp Get_temperature(); float humi Get_humidity(); // 2. 通过串口打印调试用 printf(Temp: %.1f C, Humi: %.1f %%\r\n, temp, humi); // 3. 在OLED上显示 // 先清除之前显示数字的区域简单做法画白色矩形覆盖 // 更优做法创建字符串缓冲区只更新变化的字符 char str_buf[16]; // 显示温度 sprintf(str_buf, %.1f C, temp); // 假设温度显示在 (40, 16) 位置 // 先清除旧内容这里简单用空白字符串覆盖固定宽度区域 OLED_ShowString(40, 16, , 1, 0); // 用背景色0写空白 OLED_ShowString(40, 16, str_buf, 1, 1); // 用前景色1写新数据 // 显示湿度 sprintf(str_buf, %.1f %%, humi); OLED_ShowString(40, 32, , 1, 0); OLED_ShowString(40, 32, str_buf, 1, 1); // 刷新显存到屏幕 OLED_Refresh(); } else { printf(DHT11 Read Error!\r\n); OLED_ShowString(0, 48, Read Error!, 1, 1); OLED_Refresh(); } // 4. 延时一段时间再读取DHT11两次读取间隔建议大于1秒 delay_ms(2000); } }5. 调试心得与常见问题屏幕不亮/全黑/全白检查电源首要怀疑对象确认VCC接的是3.3V不是5V。检查复位如果模块有RES引脚确保上电后有一个正确的复位脉冲低电平-高电平。检查初始化序列特别是电荷泵使能命令0x8D, 0x14和开启显示命令0xAF是否发送。检查SPI时序用逻辑分析仪或示波器抓一下SCLK、SDA、DC、CS的波形看数据是否正常发出。DC电平在发命令和数据时是否正确切换。显示乱码/错位检查扫描方向初始化命令中的0xA1段重映射和0xC8COM扫描方向可能需要调整组合以匹配你的屏幕物理安装方向。检查坐标计算确认画点函数里坐标到页和位的换算是否正确。y/8得到页y%8得到位。显示内容闪烁避免全屏刷新这就是我上面使用OLED_GRAM显存的原因。每次更新只修改变化的部分局部刷新最后再一次性OLED_Refresh()。如果频繁全屏清屏再重绘就会闪烁。SPI通信失败确认CPOL和CPHASSD1306通常支持模式0和模式3如果一种不行就换另一种试试。检查片选CS确保在发送数据前拉低发送后拉高。降低时钟速度刚开始调试时把SPI时钟降到100kHz排除时序问题。移植成功后你就可以在这个驱动框架上扩展显示图片、绘制图形、制作简单UI等更多功能了。整个代码工程包括完整的OLED驱动、字库和DHT11应用示例我已经整理好你可以基于它快速开始你的项目。
1.3寸单色OLED显示屏驱动移植与显示应用实战
1.3寸单色OLED显示屏驱动移植与显示应用实战最近在做一个基于TI MSPM0G3507的小型环境监测项目需要一块屏幕来实时显示温湿度数据。考虑到成本、功耗和显示效果我最终选择了1.3寸的单色OLED显示屏。这种屏幕功耗低、对比度高非常适合嵌入式设备。但网上找到的驱动代码往往不能直接用在MSPM0上需要自己动手移植。今天我就把整个驱动移植和显示温湿度的过程整理出来手把手教你怎么做。即使你是第一次接触OLED屏跟着步骤走也能搞定。1. 准备工作了解你的OLED屏在开始写代码之前咱们得先搞清楚要驱动的对象是什么。我用的这块1.3寸OLED屏核心驱动芯片是SSD1306。这是一款非常常见的OLED驱动芯片支持128x64的分辨率。这种屏幕通常有两种通信方式I2C和SPI。我手头这块是SPI接口的接线更简单速度也更快。SPI接口一般需要4根线SCLK时钟线由主设备MCU产生。SDA数据线用于发送数据。DC数据/命令选择线。这条线很关键告诉屏幕接下来发送的是命令配置参数还是数据要显示的像素点。CS片选线低电平有效选中这块屏幕。RES复位线可选有些模块没有靠上电复位。注意不同厂家生产的模块引脚命名可能略有不同比如SDA可能叫MOSIDC可能叫D/C但功能都一样。拿到屏幕第一件事就是看卖家提供的资料确认引脚定义。2. 硬件连接把屏幕和开发板连起来硬件连接是第一步千万不能接错。我使用的是TI MSPM0G3507开发板连接关系如下OLED屏引脚MSPM0G3507引脚功能说明VCC3.3V电源正极务必接3.3V接5V会烧屏GNDGND电源地SCLKPA2 (可配置为SPI CLK)SPI时钟SDAPA4 (可配置为SPI MOSI)SPI数据输出DCPA1 (任意GPIO)数据/命令选择CSPA3 (任意GPIO)片选低电平选中RESPA0 (任意GPIO)复位引脚如果模块有连接好之后建议用万用表测一下VCC和GND之间没有短路再上电。3. 软件驱动移植让屏幕亮起来驱动移植的核心就是根据SSD1306芯片的数据手册用SPI发送正确的命令和数据。我们分几步走。3.1 底层GPIO与SPI初始化首先我们需要配置MSPM0的引脚功能和SPI外设。这里我使用TI的SysConfig图形化工具来配置非常直观。在CCS或你的IDE工程中打开.syscfg文件。添加一个SPI外设配置选择为主机Master模式。将PA2分配给CLKPA4分配给MOSI。注意OLED屏是只写的我们不需要MISO引脚。配置SPI的时钟极性CPOL和相位CPHA。对于SSD1306通常模式0CPOL0 CPHA0或模式3CPOL1 CPHA1都可以。我习惯用模式0。时钟频率不用太高先设置成1MHz左右就够用了。另外将PA1、PA3、PA0配置为普通的GPIO输出引脚分别对应DC、CS和RES。保存配置工具会自动生成初始化代码到ti_msp_dl_config.c/h文件中。生成的代码会帮我们初始化好SPI和GPIO。接下来我们需要封装几个最基本的底层函数用于控制引脚和发送数据。// oled_gpio.c #include board.h // 此头文件包含了由SysConfig生成的引脚定义 // 假设SysConfig为我们的引脚生成了以下宏定义 // #define OLED_DC_PORT GPIOA // #define OLED_DC_PIN GPIO_PIN_1 // #define OLED_CS_PORT GPIOA // #define OLED_CS_PIN GPIO_PIN_3 // #define OLED_RES_PORT GPIOA // #define OLED_RES_PIN GPIO_PIN_0 // SPI实例定义为SPI_0 // 控制引脚电平的宏简洁明了 #define OLED_DC_HIGH() DL_GPIO_setPins(OLED_DC_PORT, OLED_DC_PIN) #define OLED_DC_LOW() DL_GPIO_clearPins(OLED_DC_PORT, OLED_DC_PIN) #define OLED_CS_HIGH() DL_GPIO_setPins(OLED_CS_PORT, OLED_CS_PIN) #define OLED_CS_LOW() DL_GPIO_clearPins(OLED_CS_PORT, OLED_CS_PIN) #define OLED_RES_HIGH() DL_GPIO_setPins(OLED_RES_PORT, OLED_RES_PIN) #define OLED_RES_LOW() DL_GPIO_clearPins(OLED_RES_PORT, OLED_RES_PIN) // 复位OLED void OLED_Reset(void) { OLED_RES_LOW(); delay_ms(100); // 保持低电平至少一段时间 OLED_RES_HIGH(); delay_ms(100); // 等待复位完成 } // 写命令DC引脚置低然后通过SPI发送 void OLED_Write_Cmd(uint8_t cmd) { OLED_DC_LOW(); // 表示接下来是命令 OLED_CS_LOW(); // 选中OLED DL_SPI_transmitDataBlocking(SPI_0_INST, cmd, 1); // 发送一个字节的命令 OLED_CS_HIGH(); // 取消选中 } // 写数据DC引脚置高然后通过SPI发送 void OLED_Write_Data(uint8_t data) { OLED_DC_HIGH(); // 表示接下来是数据 OLED_CS_LOW(); // 选中OLED DL_SPI_transmitDataBlocking(SPI_0_INST, data, 1); // 发送一个字节的数据 OLED_CS_HIGH(); // 取消选中 }3.2 SSD1306初始化序列屏幕要正常工作需要一系列初始化命令来设置对比度、扫描方式、显示开关等。这些命令序列在SSD1306的数据手册里可以找到网上也有很多现成的代码。我们把它封装成一个初始化函数。// oled.c void OLED_Init(void) { // 1. 硬件复位 OLED_Reset(); // 2. 发送初始化命令序列 OLED_Write_Cmd(0xAE); // 关闭显示 OLED_Write_Cmd(0xD5); // 设置显示时钟分频比/振荡器频率 OLED_Write_Cmd(0x80); OLED_Write_Cmd(0xA8); // 设置多路复用率 (1 to 64) OLED_Write_Cmd(0x3F); // 对应64行 OLED_Write_Cmd(0xD3); // 设置显示偏移 OLED_Write_Cmd(0x00); // 无偏移 OLED_Write_Cmd(0x40); // 设置显示起始行 OLED_Write_Cmd(0x8D); // 电荷泵设置 OLED_Write_Cmd(0x14); // 使能电荷泵必须否则屏幕不亮 OLED_Write_Cmd(0x20); // 设置内存地址模式 OLED_Write_Cmd(0x00); // 水平地址模式方便我们后续画图 OLED_Write_Cmd(0xA1); // 段重映射设置 (0xA1 左右反置0xA0 正常) OLED_Write_Cmd(0xC8); // 扫描方向设置 (0xC8 上下反置0xC0 正常) // 根据你的屏幕显示方向可能需要调整这两条命令 OLED_Write_Cmd(0xDA); // 设置COM引脚硬件配置 OLED_Write_Cmd(0x12); OLED_Write_Cmd(0x81); // 设置对比度控制 OLED_Write_Cmd(0xCF); // 对比度值 (0x00 ~ 0xFF) OLED_Write_Cmd(0xD9); // 设置预充电周期 OLED_Write_Cmd(0xF1); OLED_Write_Cmd(0xDB); // 设置VCOMH电压倍率 OLED_Write_Cmd(0x40); OLED_Write_Cmd(0xA4); // 全部像素点正常显示 OLED_Write_Cmd(0xA6); // 设置非反相显示 OLED_Write_Cmd(0xAF); // 开启显示 // 3. 清屏 OLED_Clear(); } // 清屏函数将整个显存填充为0黑色 void OLED_Clear(void) { uint8_t i, j; for(j0; j8; j) // 共8页Page每页8行共64行 { OLED_Write_Cmd(0xB0 j); // 设置页地址Page0~Page7 OLED_Write_Cmd(0x00); // 设置列地址低4位 OLED_Write_Cmd(0x10); // 设置列地址高4位 for(i0; i128; i) // 每页有128列 { OLED_Write_Data(0x00); // 写入0熄灭所有像素 } } }调用OLED_Init()函数后如果硬件连接和代码正确屏幕应该会亮起但可能是全黑或全白取决于初始化设置并且执行OLED_Clear()后会变成全黑。3.3 实现基础绘图函数屏幕能亮了接下来就要能在指定位置显示内容。我们需要实现设置光标和画点的函数。SSD1306的显存可以看作一个128列 x 8页的矩阵。每一页管理8行像素一个字节的8个bit。所以坐标 (X, Y) 对应的显存位置需要换算。// oled.c // 全局显存用于实现局部刷新避免全屏刷新闪烁 static uint8_t OLED_GRAM[128][8]; // 设置光标位置 (x: 0~127, y: 0~63) void OLED_Set_Pos(uint8_t x, uint8_t y) { OLED_Write_Cmd(0xB0 (y / 8)); // 设置页地址 OLED_Write_Cmd(((x 0xF0) 4) | 0x10); // 设置列地址高4位 OLED_Write_Cmd(x 0x0F); // 设置列地址低4位 } // 在指定坐标画点 (color: 1-点亮0-熄灭) void OLED_DrawPoint(uint8_t x, uint8_t y, uint8_t color) { uint8_t page, bit_pos; if(x 128 || y 64) return; // 超出范围 page y / 8; bit_pos y % 8; if(color) OLED_GRAM[x][page] | (1 bit_pos); // 对应位置1 else OLED_GRAM[x][page] ~(1 bit_pos); // 对应位置0 // 更新到屏幕局部刷新只更新一个字节 OLED_Set_Pos(x, page * 8); OLED_Write_Data(OLED_GRAM[x][page]); } // 刷新整个显存到屏幕 void OLED_Refresh(void) { uint8_t i, j; for(j0; j8; j) { OLED_Set_Pos(0, j*8); for(i0; i128; i) { OLED_Write_Data(OLED_GRAM[i][j]); } } }有了画点函数我们就可以在此基础上构建显示字符、字符串、数字、直线、矩形等高级函数。这里以显示一个6x8的小字符为例// 字模数据取模软件生成这里以字符‘A’为例 static const uint8_t Font6x8[][6] { {0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C}, // A // ... 其他字符 }; // 在指定位置显示一个字符 (size: 1-6x8, 2-8x16这里实现6x8) void OLED_ShowChar(uint8_t x, uint8_t y, char chr, uint8_t size, uint8_t color) { uint8_t i, j, temp; if(size 1) { if(chr || chr ~) return; // 只支持可见ASCII字符 chr chr - ; // 找到字模数组中的索引 for(i0; i6; i) // 字符宽度6像素 { temp Font6x8[chr][i]; // 获取一列像素数据 for(j0; j8; j) // 字符高度8像素 { if(temp 0x01) OLED_DrawPoint(xi, yj, color); else OLED_DrawPoint(xi, yj, !color); temp 1; } } } } // 显示字符串 void OLED_ShowString(uint8_t x, uint8_t y, char *str, uint8_t size, uint8_t color) { while(*str ! \0) { OLED_ShowChar(x, y, *str, size, color); x 6; // 6x8字体字符间距1像素所以x7 if(x 122) // 换行判断简单处理 { x 0; y 8; } str; } } // 显示数字整数 void OLED_ShowNum(uint8_t x, uint8_t y, uint32_t num, uint8_t len, uint8_t size, uint8_t color) { char str[12]; sprintf(str, %d, num); // 简单转换实际项目建议用更安全的函数 OLED_ShowString(x, y, str, size, color); }4. 实战应用显示温湿度数据驱动层搞定后应用层就简单了。假设我们已经有了一个能读取DHT11温湿度传感器的模块就像原始文章里那样我们只需要在主循环里读取数据然后调用OLED显示函数即可。// main.c #include board.h #include dht11.h #include oled.h #include stdio.h int main(void) { // 开发板初始化 board_init(); delay_ms(1000); // 等待系统稳定 // OLED初始化 OLED_Init(); OLED_ShowString(0, 0, OLED DHT11 Test, 1, 1); OLED_ShowString(0, 16, Temp:, 1, 1); OLED_ShowString(0, 32, Humi:, 1, 1); OLED_Refresh(); // DHT11初始化如果需要 // DHT11_Init(); printf(System Start...\r\n); while(1) { // 1. 读取DHT11数据 if(DHT11_Read_Data() ! 0) // 读取成功 { float temp Get_temperature(); float humi Get_humidity(); // 2. 通过串口打印调试用 printf(Temp: %.1f C, Humi: %.1f %%\r\n, temp, humi); // 3. 在OLED上显示 // 先清除之前显示数字的区域简单做法画白色矩形覆盖 // 更优做法创建字符串缓冲区只更新变化的字符 char str_buf[16]; // 显示温度 sprintf(str_buf, %.1f C, temp); // 假设温度显示在 (40, 16) 位置 // 先清除旧内容这里简单用空白字符串覆盖固定宽度区域 OLED_ShowString(40, 16, , 1, 0); // 用背景色0写空白 OLED_ShowString(40, 16, str_buf, 1, 1); // 用前景色1写新数据 // 显示湿度 sprintf(str_buf, %.1f %%, humi); OLED_ShowString(40, 32, , 1, 0); OLED_ShowString(40, 32, str_buf, 1, 1); // 刷新显存到屏幕 OLED_Refresh(); } else { printf(DHT11 Read Error!\r\n); OLED_ShowString(0, 48, Read Error!, 1, 1); OLED_Refresh(); } // 4. 延时一段时间再读取DHT11两次读取间隔建议大于1秒 delay_ms(2000); } }5. 调试心得与常见问题屏幕不亮/全黑/全白检查电源首要怀疑对象确认VCC接的是3.3V不是5V。检查复位如果模块有RES引脚确保上电后有一个正确的复位脉冲低电平-高电平。检查初始化序列特别是电荷泵使能命令0x8D, 0x14和开启显示命令0xAF是否发送。检查SPI时序用逻辑分析仪或示波器抓一下SCLK、SDA、DC、CS的波形看数据是否正常发出。DC电平在发命令和数据时是否正确切换。显示乱码/错位检查扫描方向初始化命令中的0xA1段重映射和0xC8COM扫描方向可能需要调整组合以匹配你的屏幕物理安装方向。检查坐标计算确认画点函数里坐标到页和位的换算是否正确。y/8得到页y%8得到位。显示内容闪烁避免全屏刷新这就是我上面使用OLED_GRAM显存的原因。每次更新只修改变化的部分局部刷新最后再一次性OLED_Refresh()。如果频繁全屏清屏再重绘就会闪烁。SPI通信失败确认CPOL和CPHASSD1306通常支持模式0和模式3如果一种不行就换另一种试试。检查片选CS确保在发送数据前拉低发送后拉高。降低时钟速度刚开始调试时把SPI时钟降到100kHz排除时序问题。移植成功后你就可以在这个驱动框架上扩展显示图片、绘制图形、制作简单UI等更多功能了。整个代码工程包括完整的OLED驱动、字库和DHT11应用示例我已经整理好你可以基于它快速开始你的项目。