1.14寸ST7789并口彩屏移植实战:基于立创CW32F030C8T6的引脚配置与驱动适配
1.14寸ST7789并口彩屏移植实战基于立创CW32F030C8T6的引脚配置与驱动适配最近在做一个基于立创CW32F030C8T6开发板的小项目需要用到一块1.14寸的彩色屏幕来显示一些参数和图形。市面上很多彩屏用的是SPI接口虽然省引脚但刷新速度慢。我选的这块屏用的是8位并口速度更快显示更流畅但移植起来需要自己动手配置引脚和时序。如果你也在用CW32系列的单片机并且想驱动类似的并口屏跟着我这篇教程一步步来保证你能搞定。咱们的目标很明确把厂家提供的ST7789驱动例程成功移植到立创CW32F030C8T6这块开发板上让屏幕亮起来并能显示文字和图片。整个过程我会拆解成几个清晰的步骤从拿到资料到最终验证手把手教你。1. 准备工作了解屏幕与获取资料工欲善其事必先利其器。在动手写代码之前咱们得先搞清楚要驱动的对象是什么以及手头有哪些资源。1.1 屏幕规格与接口我用的这块屏关键信息如下这些都在厂家提供的资料里写着屏幕尺寸1.14英寸分辨率135 x 240像素 (RGB)驱动芯片ST7789V通信接口8位并口引脚数量16 Pin (2.54mm间距排针)工作电压3.3V这块屏是并口屏这和常见的SPI屏3线或4线不同。并口意味着它有8根数据线DB0-DB7可以一次传输一个字节8位的数据所以数据传输速率比SPI的逐位传输快得多。除了数据线它还有几根必不可少的控制线CS片选低电平有效选中屏幕进行通信。DC数据/命令选择告诉屏幕接下来发送的是命令还是显示数据。WR写使能低电平期间屏幕会从数据线上读取数据。RD读使能本例中我们只写不读通常接高电平或固定电平。RES复位用于硬件复位屏幕。BLK背光控制。1.2 获取驱动资料驱动屏幕离不开厂家提供的底层驱动代码。资料可以通过提供的链接下载。解压后你会找到一个【LCD】文件夹这里面包含了屏幕的初始化、基本画图函数等核心驱动代码。这是我们移植工作的基础。2. 工程搭建与文件导入现在我们开始在立创CW32的开发环境中创建或打开一个工程。这个工程需要有一个基本的框架比如系统时钟初始化、GPIO初始化等最好还有一个可用的毫秒级延时函数delay_ms。你可以直接使用立创官方提供的工程模板这样最省事。复制文件将资料包里的整个【LCD】文件夹复制到你自己的工程目录下。导入工程在你的IDE比如Keil MDK中打开工程将刚才复制过来的lcd.c、lcd_init.c以及它们对应的头文件lcd.h、lcd_init.h添加到工程项目中。修改头文件引用厂家例程可能基于别的硬件平台。我们需要做两处小修改打开lcd_init.h和lcd.h文件找到#include “sys.h”这一行把它改成#include “board.h”。因为立创的开发板工程通常用board.h来管理硬件相关定义。打开lcd_init.c和lcd.c文件暂时将#include “delay.h”这一行注释掉。我们后续会使用自己工程里的延时函数。定义数据类型在lcd_init.h和lcd.h文件中添加以下宏定义确保代码中的u8、u16、u32类型有明确的定义。// 在 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 #endif做完这些编译一下工程可能会报错但先不用管因为我们最关键的引脚配置还没做。3. 核心步骤引脚配置与驱动适配这是移植工作的核心决定了单片机能否正确地“指挥”屏幕。我们需要根据自己开发板的连线情况重新定义每个控制引脚和数据引脚。3.1 规划引脚连接首先你得决定把屏幕的16个引脚接到CW32F030C8T6的哪些GPIO上。为了后续代码清晰和易于修改强烈建议像下面这样在lcd_init.h文件中用宏定义来管理所有引脚。我根据自己的板子资源做了如下连接规划你可以完全根据你的实际接线来修改这些宏定义屏幕引脚功能连接至CW32引脚宏定义端口宏定义引脚1VCC3.3V--2GNDGND--3BLK (背光)PA0PORT_LCD_BLKGPIO_LCD_BLK4RES (复位)PA1PORT_LCD_RESGPIO_LCD_RES5CS (片选)PA2PORT_LCD_CSGPIO_LCD_CS6DC (数据/命令)PA3PORT_LCD_DCGPIO_LCD_DC7WR (写使能)PA4PORT_LCD_WRGPIO_LCD_WR8RD (读使能)PA5PORT_LCD_RDGPIO_LCD_RD9DB0 (数据位0)PA6PORT_LCD_DB0GPIO_LCD_DB010DB1 (数据位1)PA7PORT_LCD_DB1GPIO_LCD_DB111DB2 (数据位2)PB0PORT_LCD_DB2GPIO_LCD_DB212DB3 (数据位3)PB1PORT_LCD_DB3GPIO_LCD_DB313DB4 (数据位4)PB10PORT_LCD_DB4GPIO_LCD_DB414DB5 (数据位5)PB11PORT_LCD_DB5GPIO_LCD_DB515DB6 (数据位6)PB12PORT_LCD_DB6GPIO_LCD_DB616DB7 (数据位7)PB13PORT_LCD_DB7GPIO_LCD_DB7根据上表的规划我们在lcd_init.h中编写宏定义//-----------------LCD端口移植---------------- // 引脚功能与连接定义 #define RCC_LCD1_ENABLE() __RCC_GPIOA_CLK_ENABLE() // 使能GPIOA时钟 #define RCC_LCD2_ENABLE() __RCC_GPIOB_CLK_ENABLE() // 使能GPIOB时钟 //RD #define PORT_LCD_RD CW_GPIOA #define GPIO_LCD_RD GPIO_PIN_5 //WR #define PORT_LCD_WR CW_GPIOA #define GPIO_LCD_WR GPIO_PIN_4 //CS #define PORT_LCD_CS CW_GPIOA #define GPIO_LCD_CS GPIO_PIN_2 //DC #define PORT_LCD_DC CW_GPIOA #define GPIO_LCD_DC GPIO_PIN_3 //RES #define PORT_LCD_RES CW_GPIOA #define GPIO_LCD_RES GPIO_PIN_1 //BLK #define PORT_LCD_BLK CW_GPIOA #define GPIO_LCD_BLK GPIO_PIN_0 //DB0 #define PORT_LCD_DB0 CW_GPIOA #define GPIO_LCD_DB0 GPIO_PIN_6 //DB1 #define PORT_LCD_DB1 CW_GPIOA #define GPIO_LCD_DB1 GPIO_PIN_7 //DB2 #define PORT_LCD_DB2 CW_GPIOB #define GPIO_LCD_DB2 GPIO_PIN_0 //DB3 #define PORT_LCD_DB3 CW_GPIOB #define GPIO_LCD_DB3 GPIO_PIN_1 //DB4 #define PORT_LCD_DB4 CW_GPIOB #define GPIO_LCD_DB4 GPIO_PIN_10 //DB5 #define PORT_LCD_DB5 CW_GPIOB #define GPIO_LCD_DB5 GPIO_PIN_11 //DB6 #define PORT_LCD_DB6 CW_GPIOB #define GPIO_LCD_DB6 GPIO_PIN_12 //DB7 #define PORT_LCD_DB7 CW_GPIOB #define GPIO_LCD_DB7 GPIO_PIN_13注意__RCC_GPIOA_CLK_ENABLE()这类时钟使能宏的名称需要根据你使用的CW32标准外设库的具体版本来确定如果编译报错请检查你的库文件中的实际定义。3.2 编写GPIO初始化函数引脚定义好了接下来就要在lcd_init.c文件的LCD_GPIO_Init(void)函数中将这些引脚初始化为输出模式。因为我们要控制屏幕所有引脚都是输出。void LCD_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; // 定义GPIO初始化结构体 // 1. 使能GPIO端口时钟 RCC_LCD1_ENABLE(); // 使能GPIOA时钟 RCC_LCD2_ENABLE(); // 使能GPIOB时钟 // 2. 配置控制引脚 (RD, WR, CS, DC, RES, BLK, DB0, DB1) - 它们都在GPIOA上 GPIO_InitStruct.Pins GPIO_LCD_RD | GPIO_LCD_WR | GPIO_LCD_CS | GPIO_LCD_DC | GPIO_LCD_RES | GPIO_LCD_BLK | GPIO_LCD_DB0 | GPIO_LCD_DB1; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; // 推挽输出模式 GPIO_InitStruct.Speed GPIO_SPEED_HIGH; // 高速输出 GPIO_Init(PORT_LCD_RD, GPIO_InitStruct); // 初始化这一组引脚 // 3. 配置数据引脚 DB2-DB7 - 它们都在GPIOB上 GPIO_InitStruct.Pins GPIO_LCD_DB2 | GPIO_LCD_DB3 | GPIO_LCD_DB4 | GPIO_LCD_DB5 | GPIO_LCD_DB6 | GPIO_LCD_DB7; // Mode和Speed沿用上面的设置 GPIO_Init(PORT_LCD_DB2, GPIO_InitStruct); // 初始化这一组引脚 }3.3 实现引脚电平控制宏为了方便地设置某个引脚为高电平或低电平我们需要在lcd_init.h中定义一系列宏。这些宏会直接调用CW32库的GPIO_WritePin函数。//-----------------LCD端口定义---------------- // 设置引脚为高电平 #define LCD_RES_Set() GPIO_WritePin(PORT_LCD_RES, GPIO_LCD_RES, GPIO_Pin_SET) //RES #define LCD_CS_Set() GPIO_WritePin(PORT_LCD_CS, GPIO_LCD_CS, GPIO_Pin_SET) //CS #define LCD_DC_Set() GPIO_WritePin(PORT_LCD_DC, GPIO_LCD_DC, GPIO_Pin_SET) //DC #define LCD_WR_Set() GPIO_WritePin(PORT_LCD_WR, GPIO_LCD_WR, GPIO_Pin_SET) //WR #define LCD_RD_Set() GPIO_WritePin(PORT_LCD_RD, GPIO_LCD_RD, GPIO_Pin_SET) //RD #define LCD_BLK_Set() GPIO_WritePin(PORT_LCD_BLK, GPIO_LCD_BLK, GPIO_Pin_SET) //BLK // 设置引脚为低电平 #define LCD_RES_Clr() GPIO_WritePin(PORT_LCD_RES, GPIO_LCD_RES, GPIO_Pin_RESET) //RES #define LCD_CS_Clr() GPIO_WritePin(PORT_LCD_CS, GPIO_LCD_CS, GPIO_Pin_RESET) //CS #define LCD_DC_Clr() GPIO_WritePin(PORT_LCD_DC, GPIO_LCD_DC, GPIO_Pin_RESET) //DC #define LCD_WR_Clr() GPIO_WritePin(PORT_LCD_WR, GPIO_LCD_WR, GPIO_Pin_RESET) //WR #define LCD_RD_Clr() GPIO_WritePin(PORT_LCD_RD, GPIO_LCD_RD, GPIO_Pin_RESET) //RD #define LCD_BLK_Clr() GPIO_WritePin(PORT_LCD_BLK, GPIO_LCD_BLK, GPIO_Pin_RESET) //BLK3.4 重写并口数据写入函数这是最关键的一步并口屏需要我们把一个8位数据一个字节的每一位分别送到8根数据线DB0-DB7上。我们需要在lcd_init.h中为每个数据位定义一个操作宏然后在lcd_init.c中修改数据写入函数。首先在lcd_init.h中添加数据位操作宏// 数据位操作宏如果x不为0则对应引脚置高电平1否则置低电平0 #define BIT_DB7(x) { GPIO_WritePin( PORT_LCD_DB7, GPIO_LCD_DB7, (x)?GPIO_Pin_SET:GPIO_Pin_RESET); } #define BIT_DB6(x) { GPIO_WritePin( PORT_LCD_DB6, GPIO_LCD_DB6, (x)?GPIO_Pin_SET:GPIO_Pin_RESET); } #define BIT_DB5(x) { GPIO_WritePin( PORT_LCD_DB5, GPIO_LCD_DB5, (x)?GPIO_Pin_SET:GPIO_Pin_RESET); } #define BIT_DB4(x) { GPIO_WritePin( PORT_LCD_DB4, GPIO_LCD_DB4, (x)?GPIO_Pin_SET:GPIO_Pin_RESET); } #define BIT_DB3(x) { GPIO_WritePin( PORT_LCD_DB3, GPIO_LCD_DB3, (x)?GPIO_Pin_SET:GPIO_Pin_RESET); } #define BIT_DB2(x) { GPIO_WritePin( PORT_LCD_DB2, GPIO_LCD_DB2, (x)?GPIO_Pin_SET:GPIO_Pin_RESET); } #define BIT_DB1(x) { GPIO_WritePin( PORT_LCD_DB1, GPIO_LCD_DB1, (x)?GPIO_Pin_SET:GPIO_Pin_RESET); } #define BIT_DB0(x) { GPIO_WritePin( PORT_LCD_DB0, GPIO_LCD_DB0, (x)?GPIO_Pin_SET:GPIO_Pin_RESET); }接着找到lcd_init.c文件中的void LCD_Writ_Bus(u8 dat)函数。这个函数负责将一个字节的数据通过并口发送出去。我们需要用上面定义的宏来重写它。void LCD_Writ_Bus(u8 dat) { LCD_CS_Clr(); // 拉低片选选中屏幕 LCD_WR_Clr(); // 拉低写使能准备写入数据 // 将dat的每一个bit分别设置到对应的数据引脚上 // (dat7)0x01 是取dat的最高位第7位 BIT_DB7( (dat7) 0x01 ); BIT_DB6( (dat6) 0x01 ); BIT_DB5( (dat5) 0x01 ); BIT_DB4( (dat4) 0x01 ); BIT_DB3( (dat3) 0x01 ); BIT_DB2( (dat2) 0x01 ); BIT_DB1( (dat1) 0x01 ); BIT_DB0( (dat0) 0x01 ); // 取最低位 LCD_WR_Set(); // 拉高写使能完成一个字节的写入 LCD_CS_Set(); // 拉高片选结束本次通信 }这个函数的逻辑模拟了并口通信的时序先选中设备CS拉低然后发出写信号WR拉低在WR为低电平期间数据线上的数据必须保持稳定屏幕会在此时采样。所以我们先设置好所有数据线的电平最后再将WR拉高表示数据有效屏幕完成采样。4. 功能验证与测试所有代码修改完成后编译工程应该没有错误了。接下来写一个简单的main函数来测试屏幕是否正常工作。在你的main.c中可以这样写#include board.h #include lcd_init.h #include lcd.h // 假设你有一个包含图片数组的头文件 pic.h #include pic.h int32_t main(void) { board_init(); // 开发板初始化系统时钟、延时函数等 LCD_Init(); // 初始化LCD屏幕 LCD_Fill(0, 0, LCD_W, LCD_H, WHITE); // 全屏填充白色清屏 float t 0; while(1) { // 显示屏幕宽度和高度 LCD_ShowString(24, 30, (uint8_t *)LCD_W:, RED, WHITE, 16, 0); LCD_ShowIntNum(72, 30, LCD_W, 3, RED, WHITE, 16); LCD_ShowString(24, 50, (uint8_t *)LCD_H:, RED, WHITE, 16, 0); LCD_ShowIntNum(72, 50, LCD_H, 3, RED, WHITE, 16); // 显示一个递增的浮点数 LCD_ShowFloatNum1(20, 80, t, 4, RED, WHITE, 16); t 0.11; // 显示一张图片需要提前准备好图片数组 gImage_1 LCD_ShowPicture(65, 80, 40, 40, gImage_1); delay_ms(1000); // 延时1秒 } }上电后如果一切顺利你应该能看到屏幕点亮背景为白色上面显示着屏幕的宽度、高度信息一个不断变化的小数以及一张小图片。5. 常见问题与排查如果屏幕没反应别着急按以下步骤排查检查硬件连接这是最常见的问题务必确认屏幕的VCC和GND是否正确连接到3.3V和地以及所有16根信号线是否与代码中定义的引脚一一对应、接触良好。检查背光BLK引脚是背光控制。如果屏幕完全黑屏但有隐约内容尝试在初始化后执行LCD_BLK_Set()拉高或LCD_BLK_Clr()拉低看看哪个能点亮背光。有些屏幕是低电平点亮背光。检查复位时序在LCD_Init()函数内部通常会有一个硬件复位过程拉低RES一段时间再拉高。确保这个延时足够长比如100ms。检查时序如果显示乱码或花屏重点检查LCD_Writ_Bus函数。确保在WR拉低之前数据线DB0-DB7的电平已经设置好。可以尝试在BIT_DB0等操作和LCD_WR_Clr()之间加一个极短的延时几个NOP指令。使用逻辑分析仪如果有条件用逻辑分析仪抓取CS、DC、WR和DB0这几根线的波形对照ST7789数据手册的时序图检查是最直接的调试方法。移植并口屏的关键在于耐心和细心只要引脚配置正确时序模拟得当就一定能成功。希望这篇教程能帮你顺利点亮屏幕