1.3寸单色OLED显示屏(SH1106驱动)在TI MSPM0L1306开发板上的SPI移植实战
1.3寸单色OLED显示屏SH1106驱动在TI MSPM0L1306开发板上的SPI移植实战最近在准备电赛项目需要给TI的MSPM0L1306开发板加个小屏幕来显示数据。我选了中景园那款1.3寸的OLED屏用的是SH1106驱动芯片通信方式是SPI。网上找的例程大多是针对STM32的直接拿来用在TI的M0芯片上肯定不行得自己动手移植。今天我就把整个移植过程从找资料、改代码到最终点亮屏幕一步步拆开讲清楚。如果你也在用TI MSPM0系列芯片或者参加电赛需要快速搞定显示模块跟着我做一遍半小时内就能让屏幕亮起来。1. 准备工作拿到屏幕和资料工欲善其事必先利其器。咱们先得把屏幕和对应的资料准备好。我用的屏幕是“黄保凯中景园1.3寸OLED显示屏”驱动芯片是SH1106支持SPI和IIC通信模块上有个电阻焊点可以切换默认是SPI。屏幕尺寸不大分辨率是128x64显示字符和简单图形完全够用。关键参数速览工作电压3.3V ~ 5V和咱们的开发板完美匹配驱动芯片SH1106通信协议SPI四线制像素128 x 64注意购买链接和资料都在原始文章里提供了。资料包特别重要里面包含了屏幕的数据手册、原理图以及最关键的——针对其他单片机比如STM32的示例代码。这份示例代码是我们移植的基础。拿到资料包后你会发现里面有个LCD文件夹这就是我们需要的核心源码。把它先保存好我们待会儿要把它“搬”到我们的TI工程里去。2. 理解屏幕的“接线图”引脚定义在动手写代码之前咱们得先搞清楚这块屏幕怎么和开发板连接。SH1106的SPI接口需要用到6个信号线如果RESET接MCU复位脚则MCU控制线为5根。屏幕模块的引脚通常印在PCB上对照资料里的说明我给大家整理了一下屏幕引脚标号信号名称作用说明是否必须连接MCU GPIOGND电源地接地是VCC电源正接3.3V或5V是D0 / SCL时钟线 (SCK)SPI时钟信号是D1 / SDA数据线 (MOSI)SPI主设备输出数据线是RES复位线低电平复位屏幕可接MCU GPIO或MCU复位引脚DC数据/命令选择高电平数据低电平命令是CS片选线 (NSS)低电平选中屏幕是这里有个小技巧可以节省GPIO口RESET引脚可以不接到MCU的GPIO上而是直接接到开发板的复位引脚。这样当开发板复位时屏幕也跟着一起复位初始化更可靠还省下一个IO口。当然如果你想在程序里单独控制屏幕复位那就接到一个GPIO上。3. 核心移植步骤手把手修改代码好了硬件连接心里有数了现在进入重头戏——软件移植。我们的目标是把那份STM32的例程代码改造得能在TI MSPM0L1306上运行。3.1 第一步把源码“请进”工程在你的TI CCS工程目录下我用的工程模板是empty例程新建一个文件夹比如叫OLED。把资料包里LCD文件夹下的所有.c和.h文件主要是oled.c、oled.h、lcd_init.h等复制到这个OLED文件夹里。回到CCS在项目浏览器里右键点击你的工程选择Add Files...把刚才复制过来的.c文件添加进去。接着配置头文件路径右键工程 -Properties-Build-ARM Compiler-Include Options把OLED文件夹的路径添加进去。3.2 第二步解决编译报错——基础类型定义一编译肯定会报错。别慌这是移植的必经之路。第一个常见的错误是找不到u8、u16这些类型定义。TI的驱动库习惯用uint8_t、uint16_t。我们需要在代码里做一下映射。打开lcd_init.h和lcd.h文件看哪个文件报错就改哪个在文件开头添加以下代码#ifndef u8 #define u8 uint8_t #endif #ifndef u16 #define u16 uint16_t #endif #ifndef u32 #define u32 uint32_t #endif3.3 第三步配置GPIO引脚——使用SYSCONFIG图形化工具TI MSPM0系列开发的一大便利就是SysConfig图形化配置工具配置引脚、外设非常直观。我们用它来生成控制OLED所需的GPIO初始化代码。在CCS工程里找到并双击empty.syscfg文件它会打开SysConfig配置界面。我们需要添加5个GPIO配置分别对应OLED的SCL(时钟)、SDA(数据)、DC、CS和RES如果RES接GPIO的话。在Peripherals或Pins选项卡下找到GPIO然后Add一个配置。每个配置需要设置两项Pin: 选择你想用的具体物理引脚比如PA0, PA1...。这里一定要根据你实际把屏幕插在开发板的哪个位置来选择Direction: 全部选择Output输出模式因为我们是用软件模拟SPI所有引脚都是MCU主动控制输出。为每个配置起一个容易识别的名字例如OLED_SCL、OLED_SDA等。配置完5个GPIO后点击右上角的Save保存。提示保存时如果弹出对话框询问是否要生成代码一定要选择Yes to All。保存后SysConfig会自动在工程里生成一个ti_msp_dl_config.h文件里面就定义了我们刚才配置的引脚宏比如OLED_SCL、OLED_SDA等。3.4 第四步修改OLED底层驱动函数这是移植最关键的一步我们要让OLED的底层IO操作函数调用TI的GPIO驱动库函数。首先修改引脚操作宏定义。打开lcd_init.h文件找到原来用STM32库函数写的#define宏把它们全部替换成下面这样//-----------------OLED端口定义---------------- #define OLED_SCL_Clr() DL_GPIO_clearPins(OLED_PORT,OLED_SCL_PIN) //拉低时钟线 #define OLED_SCL_Set() DL_GPIO_setPins(OLED_PORT,OLED_SCL_PIN) //拉高时钟线 #define OLED_SDA_Clr() DL_GPIO_clearPins(OLED_PORT,OLED_MOSI_PIN)//拉低数据线 #define OLED_SDA_Set() DL_GPIO_setPins(OLED_PORT,OLED_MOSI_PIN) //拉高数据线 #define OLED_RES_Clr() DL_GPIO_clearPins(OLED_PORT,OLED_RES_PIN) //拉低复位 #define OLED_RES_Set() DL_GPIO_setPins(OLED_PORT,OLED_RES_PIN) //拉高复位 #define OLED_DC_Clr() DL_GPIO_clearPins(OLED_PORT,OLED_DC_PIN) //拉低DC(命令) #define OLED_DC_Set() DL_GPIO_setPins(OLED_PORT,OLED_DC_PIN) //拉高DC(数据) #define OLED_CS_Clr() DL_GPIO_clearPins(OLED_PORT,OLED_CS_PIN) //拉低片选 #define OLED_CS_Set() DL_GPIO_setPins(OLED_PORT,OLED_CS_PIN) //拉高片选注意OLED_PORT和OLED_SCL_PIN这些宏已经在ti_msp_dl_config.h里由SysConfig定义好了我们直接使用就行。然后修改字节写入函数OLED_WR_Byte。这个函数是软件模拟SPI的核心它负责把一个字节的数据按照时钟节拍一位一位地发送出去。打开oled.c找到这个函数确保它和下面的代码一致void OLED_WR_Byte(u8 dat, u8 cmd) { u8 i; // 根据cmd参数设置DC引脚电平决定发送的是命令还是数据 if(cmd) OLED_DC_Set(); // 发送数据时DC置高 else OLED_DC_Clr(); // 发送命令时DC置低 delay_us(3); // 短暂延时稳定信号 OLED_CS_Clr(); // 拉低片选开始通信 // 循环8次发送一个字节从最高位MSB开始发送 for(i0; i8; i) { OLED_SCL_Clr(); // 时钟线拉低准备数据 // 判断当前要发送的位是1还是0并设置数据线 if(dat 0x80) OLED_SDA_Set(); // 发送1 else OLED_SDA_Clr(); // 发送0 delay_us(3); // 数据建立时间 OLED_SCL_Set(); // 时钟线拉高数据被采样 delay_us(3); // 时钟高电平保持时间 dat 1; // 数据左移准备发送下一位 } OLED_CS_Set(); // 拉高片选结束本次传输 OLED_DC_Set(); // 将DC恢复为高电平数据状态 delay_us(3); }最后简化初始化函数。因为GPIO的初始化已经由SysConfig在board_init()里自动完成了所以OLED_Init()函数里原来那些配置GPIO模式的代码就可以删掉了。确保你的OLED_Init函数只包含复位序列和发送初始化命令的部分就像原始文章里给出的那样。3.5 第五步编写主程序进行测试所有底层工作都做完了现在该验收成果了。打开你的主函数文件比如empty.c编写一个简单的测试程序。#include board.h #include stdio.h #include oled.h int main(void) { // 开发板初始化包含了SysConfig配置的所有外设初始化 board_init(); OLED_Init(); // 初始化OLED屏幕 OLED_Clear(); // 清屏 uint8_t counter 0; while(1) { // 在屏幕不同位置用不同大小的字体显示“ABC” OLED_ShowString(0, 0, (uint8_t *)ABC, 8, 1); // 6x8 小字体 OLED_ShowString(0, 8, (uint8_t *)ABC, 12, 1); // 6x12 字体 OLED_ShowString(0, 20, (uint8_t *)ABC, 16, 1); // 8x16 字体 OLED_ShowString(0, 36, (uint8_t *)ABC, 24, 1); // 12x24 大字体 // 显示一个递增的数字 OLED_ShowString(50, 8, (uint8_t *)Num:, 16, 1); OLED_ShowNum(90, 8, counter, 3, 16, 1); // 显示3位数字 counter; OLED_Refresh(); // 将显存数据刷新到屏幕 delay_ms(1000); // 延时1秒 OLED_Clear(); // 清屏准备下一次显示实现动态效果 } }4. 上电验证与问题排查代码编译下载后给开发板上电。如果一切顺利你应该能看到屏幕上清晰地显示出不同大小的“ABC”字符并且一个数字在每秒递增。如果屏幕不亮别着急按这个顺序排查检查硬件连接这是最容易出错的地方。务必确认VCC和GND没有接反SPI的几根线是否接对、接牢。用万用表量一下屏幕VCC脚是否有3.3V电压。检查引脚配置回顾SysConfig里你配置的5个GPIO引脚是否和实际插的物理引脚一一对应。一个简单的验证方法是写个测试程序循环置高置低某个引脚用万用表或示波器看看有没有波形。检查软件延时OLED_WR_Byte函数里的delay_us(3)很重要。SH1106对SPI时序有要求如果MCU主频太快这个延时可能不够。如果屏幕显示乱码或者完全不响应可以尝试把这个延时加大到10甚至20微秒试试。检查初始化序列确保OLED_Init()函数里那一长串初始化命令0xAE, 0x02...被正确发送。可以单步调试看看程序是否卡在某个地方。这个移植过程最关键的其实就是两步一是用SysConfig正确配置并生成引脚定义二是把底层IO操作函数替换成TI的GPIO库函数。一旦打通了这两点剩下的显示函数画点、画线、显示字符都是通用的可以直接用。希望这篇实战记录能帮你顺利点亮屏幕。在实际项目里这块小屏幕用来显示传感器数据、系统状态或者简单的菜单界面非常方便。