当前位置: 首页 > news >正文

GD32VW553驱动0.96寸IPS彩屏(ST7735)移植与显示实战

GD32VW553驱动0.96寸IPS彩屏(ST7735)移植与显示实战

最近在做一个智能家居的小项目,需要用到一块小巧的彩色显示屏来显示状态信息。我选用了GD32VW553开发板和一块0.96寸的IPS彩屏,屏幕驱动芯片是ST7735。刚开始移植驱动时,确实遇到了一些小麻烦,比如引脚配置、SPI时序、字库显示等问题。今天我就把整个移植过程整理出来,手把手教大家如何让这块屏幕在GD32VW553上跑起来。

这篇文章适合正在使用GD32VW553进行嵌入式开发的工程师或爱好者,特别是那些需要在物联网设备、智能家居面板等应用中添加小型彩色显示屏的朋友。跟着我的步骤走,你就能在自己的项目里轻松显示文字、图形和图片了。

1. 准备工作:了解你的屏幕

在开始写代码之前,咱们先搞清楚要驱动的这块屏幕是什么来头。我用的这块屏参数如下:

  • 屏幕尺寸:0.96英寸
  • 分辨率:80 x 160 (RGB)
  • 驱动芯片:ST7735
  • 通信协议:SPI (4线制)
  • 工作电压:2.8V ~ 3.3V
  • 接口:8 Pin 2.54mm排针

这块屏用的是SPI接口,接线简单,只需要几根线就能驱动。屏幕的显示效果很不错,IPS屏视角广,色彩也鲜艳,很适合做小型设备的UI。

注意:购买屏幕时,商家通常会提供资料包,里面会有详细的规格书和示例代码。我用的这块屏的资料下载链接和提取码在原始资料里,大家可以按需获取。

2. 硬件连接:把屏幕和开发板连起来

驱动屏幕的第一步,就是把线接对。根据原始资料里的引脚定义,我们需要连接8根线。这里我直接给出GD32VW553开发板与屏幕的接线表,大家照着接就行:

屏幕引脚开发板引脚功能说明
GNDGND电源地
VCC3V3电源正极 (3.3V)
SCLPA11SPI时钟线 (SCK)
SDAPA9SPI数据输出线 (MOSI)
RESPA7复位引脚,低电平有效
DCPA6数据/命令选择引脚
CSPB11SPI片选引脚
BLKPB12背光控制引脚

接线说明

  • SCL (SCK)SDA (MOSI)是SPI通信必需的时钟线和数据线。
  • RES是复位引脚,用来对屏幕进行硬件复位。
  • DC这个引脚很关键,它告诉屏幕接下来发送的是命令(低电平)还是数据(高电平)。
  • CS是SPI的片选引脚,低电平选中设备。
  • BLK控制背光,接高电平背光亮,接低电平背光灭。如果你GPIO引脚紧张,可以直接把它接到3.3V上,这样背光就一直亮着,只是无法调节亮度了。

提示:如果你的项目对GPIO数量要求很苛刻,RES引脚可以接到MCU的复位引脚上,这样MCU复位时屏幕也跟着复位。BLK引脚可以直接接3.3V或悬空(有些模块内部已上拉),代价就是无法用软件控制背光开关了。

3. 软件移植:一步步修改驱动代码

硬件接好后,接下来就是软件部分了。我们的目标是把商家提供的驱动代码移植到GD32VW553的工程中。原始资料里提供了一个完整的例程包,我们需要把它整合到自己的项目里。

3.1 获取并导入源码

首先,从资料包里找到LCD文件夹,里面通常包含lcd_init.clcd_init.hlcd.clcd.hlcdfont.clcdfont.h等文件。把这些文件复制到你自己的GD32VW553工程目录下,比如User文件夹里。

然后,在你的IDE(比如Keil或IAR)中,把这些源文件(.c文件)添加到工程,把头文件路径也设置好。

3.2 修改引脚和端口定义

这是移植的核心步骤,我们需要根据之前的硬件连接表,修改驱动代码中的引脚定义。打开lcd_init.h文件,找到端口定义的部分,修改成下面这样:

#ifndef __LCD_INIT_H #define __LCD_INIT_H #include "gd32vw55x.h" #define USE_HORIZONTAL 2 //设置横屏或者竖屏显示 0或1为竖屏 2或3为横屏 #if USE_HORIZONTAL==0||USE_HORIZONTAL==1 #define LCD_W 80 #define LCD_H 160 #else #define LCD_W 160 #define LCD_H 80 #endif //-----------------LCD端口定义---------------- // 使能相关外设时钟 #define LCD_RCU_ENABLE() rcu_periph_clock_enable(RCU_GPIOA); \ rcu_periph_clock_enable(RCU_GPIOB); \ rcu_periph_clock_enable(RCU_SPI); // 根据硬件连接表修改以下定义 #define LCD_SCL_PORT GPIOA #define LCD_SCL_PIN GPIO_PIN_11 #define LCD_SCL_AF GPIO_AF_0 // 复用功能选择,根据数据手册填写 #define LCD_SDA_PORT GPIOA #define LCD_SDA_PIN GPIO_PIN_9 #define LCD_SDA_AF GPIO_AF_0 #define LCD_RES_PORT GPIOA #define LCD_RES_PIN GPIO_PIN_7 #define LCD_DC_PORT GPIOA #define LCD_DC_PIN GPIO_PIN_6 #define LCD_CS_PORT GPIOB #define LCD_CS_PIN GPIO_PIN_11 #define LCD_BLK_PORT GPIOB #define LCD_BLK_PIN GPIO_PIN_12 /* LCD信号控制宏定义 */ #define LCD_RES_Clr() gpio_bit_write(LCD_RES_PORT,LCD_RES_PIN, 0) //RES拉低 #define LCD_RES_Set() gpio_bit_write(LCD_RES_PORT,LCD_RES_PIN, 1) //RES拉高 #define LCD_DC_Clr() gpio_bit_write(LCD_DC_PORT,LCD_DC_PIN, 0) //DC拉低,写命令 #define LCD_DC_Set() gpio_bit_write(LCD_DC_PORT,LCD_DC_PIN, 1) //DC拉高,写数据 #define LCD_CS_Clr() gpio_bit_write(LCD_CS_PORT,LCD_CS_PIN, 0) //CS拉低,选中设备 #define LCD_CS_Set() gpio_bit_write(LCD_CS_PORT,LCD_CS_PIN, 1) //CS拉高,取消选中 #define LCD_BLK_Clr() gpio_bit_write(LCD_BLK_PORT,LCD_BLK_PIN, 0)//背光关闭 #define LCD_BLK_Set() gpio_bit_write(LCD_BLK_PORT,LCD_BLK_PIN, 1)//背光打开 // 函数声明 void LCD_GPIO_Init(void); void LCD_Writ_Bus(u8 dat); void LCD_WR_DATA8(u8 dat); void LCD_WR_DATA(u16 dat); void LCD_WR_REG(u8 dat); void LCD_Address_Set(u16 x1,u16 y1,u16 x2,u16 y2); void LCD_Init(void); #endif

关键点解释

  • LCD_RCU_ENABLE():这个宏一次性打开了GPIOA、GPIOB和SPI外设的时钟,这是GD32操作外设的第一步。
  • LCD_SCL_AFLCD_SDA_AF:这是引脚复用功能选择,GPIO_AF_0表示选择AF0功能,具体对应哪个SPI需要查GD32VW553的数据手册。我这里的配置适用于SPI0。
  • 其他的SetClr宏就是对相应引脚进行高低电平控制,非常直观。

3.3 修改底层通信函数LCD_Writ_Bus

这个函数负责通过SPI发送一个字节的数据,是驱动层最底层的函数。我们需要根据GD32的SPI库函数来重写它。打开lcd_init.c,找到LCD_Writ_Bus函数,修改如下:

void LCD_Writ_Bus(u8 dat) { uint8_t recv_data = 0; LCD_CS_Clr(); // 拉低片选,开始通信 // 等待发送缓冲区为空 while(RESET == spi_flag_get(SPI, SPI_FLAG_TBE)); // 通过SPI发送一个字节数据 spi_data_transmit(SPI, dat); // 等待接收缓冲区非空标志(虽然我们不需要接收的数据,但读一下可以清标志) while(RESET == spi_flag_get(SPI, SPI_FLAG_RBNE)); recv_data = spi_data_receive(SPI); LCD_CS_Set(); // 拉高片选,结束本次通信 return recv_data; }

这里用到了GD32标准库的SPI函数。spi_flag_get用来查询状态标志,spi_data_transmit发送数据,spi_data_receive读取数据(主要是为了清除接收标志)。

3.4 修改GPIO和SPI初始化

接下来,我们需要初始化这些引脚和SPI外设。在lcd_init.c中找到LCD_GPIO_Init函数,确保其内容如下:

void LCD_GPIO_Init(void) { // 1. 使能时钟 LCD_RCU_ENABLE(); // 2. 配置SPI引脚(SCL, SDA)为复用功能 gpio_af_set(LCD_SCL_PORT, LCD_SCL_AF, LCD_SCL_PIN); gpio_af_set(LCD_SDA_PORT, LCD_SDA_AF, LCD_SDA_PIN); gpio_mode_set(LCD_SCL_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, LCD_SCL_PIN); gpio_mode_set(LCD_SDA_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, LCD_SDA_PIN); gpio_output_options_set(LCD_SCL_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, LCD_SCL_PIN); gpio_output_options_set(LCD_SDA_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, LCD_SDA_PIN); // 3. 配置控制引脚(RES, DC, CS, BLK)为推挽输出 gpio_mode_set(LCD_RES_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, LCD_RES_PIN); gpio_mode_set(LCD_DC_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, LCD_DC_PIN); gpio_mode_set(LCD_CS_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, LCD_CS_PIN); gpio_mode_set(LCD_BLK_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, LCD_BLK_PIN); gpio_output_options_set(LCD_RES_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, LCD_RES_PIN); gpio_output_options_set(LCD_DC_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, LCD_DC_PIN); gpio_output_options_set(LCD_CS_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, LCD_CS_PIN); gpio_output_options_set(LCD_BLK_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, LCD_BLK_PIN); // 4. 初始化控制引脚状态 LCD_CS_Set(); // 片选默认高(不选中) LCD_DC_Set(); // 默认设置为数据模式 // 5. 配置SPI外设参数 spi_parameter_struct spi_init_struct; spi_struct_para_init(&spi_init_struct); // 初始化结构体 spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX; // 全双工模式 spi_init_struct.device_mode = SPI_MASTER; // 主机模式 spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT; // 8位数据帧 spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_2EDGE; // 时钟极性相位,根据ST7735手册设置 spi_init_struct.nss = SPI_NSS_SOFT; // 软件控制NSS(CS) spi_init_struct.prescale = SPI_PSC_4; // 时钟预分频,决定SPI速度 spi_init_struct.endian = SPI_ENDIAN_MSB; // 高位先发送 // 6. 初始化SPI并使能 spi_init(SPI, &spi_init_struct); spi_enable(SPI); }

代码要点

  1. 时钟使能:操作任何外设前必须先开时钟。
  2. 引脚模式:SPI的SCK和MOSI引脚要设置为复用功能(AF),而RES、DC等控制引脚设为推挽输出
  3. SPI配置SPI_NSS_SOFT表示我们用软件控制CS引脚(即我们之前定义的LCD_CS_Clr/Set)。SPI_PSC_4是时钟分频,需要根据你的系统时钟和屏幕支持的SPI速度来调整。如果屏幕显示异常,可以尝试增大分频值(降低速度)。
  4. 时钟极性相位SPI_CK_PL_HIGH_PH_2EDGE是SPI的模式1 (CPOL=1, CPHA=1),这是ST7735芯片常用的模式,务必确认与你的屏幕规格一致。

3.5 修复中文字库索引问题(关键!)

这是一个很容易踩坑的地方。原始资料里的字库代码可能是为某些编译器准备的,其汉字索引数组是3个字节(为了兼容UTF-8编码),但默认的查找步进是2字节。我们需要修改lcd.c文件中的LCD_ShowChinese函数。

找到这个函数,里面有一行s+=2;,把它改成s+=3;

void LCD_ShowChinese(u16 x,u16 y,u8 *s,u16 fc,u16 bc,u8 sizey,u8 mode) { while(*s!=0) { if(sizey==12) LCD_ShowChinese12x12(x,y,s,fc,bc,sizey,mode); else if(sizey==16) LCD_ShowChinese16x16(x,y,s,fc,bc,sizey,mode); else if(sizey==24) LCD_ShowChinese24x24(x,y,s,fc,bc,sizey,mode); else if(sizey==32) LCD_ShowChinese32x32(x,y,s,fc,bc,sizey,mode); else return; s+=3; // 将原来的 s+=2; 修改为 s+=3; x+=sizey; } }

同时,还需要修改字库文件lcdfont.c。找到字库结构体数组tfont12,tfont16,tfont24,tfont32的定义,将其中的索引数组大小从Index[2]改为Index[3]

例如,找到typFNT_GB12结构体定义,修改如下:

typedef struct { unsigned char Index[3]; // 将 [2] 改为 [3] unsigned char Msk[24]; } typFNT_GB12;

typFNT_GB16typFNT_GB24typFNT_GB32结构体进行同样的修改。

如果不做这个修改,显示中文时会出现乱码或者只能显示第一个字的问题。

4. 编写测试程序:让屏幕亮起来

所有驱动代码修改好后,就可以写个主程序来测试了。在你的main.c文件中,添加头文件并调用初始化函数。

#include "gd32vw55x.h" #include "systick.h" #include "lcd_init.h" #include "lcd.h" // 假设你有一个图片数组,例如 gImage_1 // #include "pic.h" int main(void) { // 系统时钟、延时初始化等 systick_config(); // LCD初始化 LCD_Init(); // 清屏为白色 LCD_Fill(0, 0, LCD_W, LCD_H, WHITE); // 打开背光 LCD_BLK_Set(); while(1) { // 1. 显示中文 LCD_ShowChinese(20, 0, (const u8*)"嵌入式开发", RED, WHITE, 16, 0); // 2. 显示英文字符串 LCD_ShowString(10, 20, (const u8*)"Width:", RED, WHITE, 16, 0); LCD_ShowIntNum(58, 20, LCD_W, 3, RED, WHITE, 16); // 显示屏幕宽度数值 LCD_ShowString(10, 40, (const u8*)"Height:", RED, WHITE, 16, 0); LCD_ShowIntNum(70, 40, LCD_H, 3, RED, WHITE, 16); // 显示屏幕高度数值 // 3. 显示一个递增的浮点数(模拟动态数据) static float counter = 0; LCD_ShowFloatNum1(10, 60, counter, 4, RED, WHITE, 16); // 显示到小数点后两位 counter += 0.11; // 4. 画一些图形 LCD_DrawLine(10, 80, 70, 80, BLUE); // 画线 LCD_DrawRectangle(20, 90, 60, 120, GREEN); // 画矩形框 LCD_Fill(80, 90, 120, 120, YELLOW); // 填充矩形 Draw_Circle(100, 150, 15, RED); // 画圆 // 5. 显示图片 (需要先准备好图片数组并包含头文件) // LCD_ShowPicture(100, 20, 40, 40, gImage_1); delay_1ms(500); // 延时500ms } }

编译工程并下载到GD32VW553开发板。如果一切顺利,你应该能看到屏幕上显示出了文字、图形,并且浮点数在不断变化。

5. 调试与常见问题

1. 屏幕白屏或全黑?

  • 检查电源和背光:首先用万用表量一下VCC和GND之间是不是3.3V,BLK引脚是否为高电平。
  • 检查复位时序:在LCD_Init()函数里,确保有正确的复位序列(拉低RES一段时间再拉高)。
  • 检查SPI速率:尝试将spi_init_struct.prescale改成一个更大的值(如SPI_PSC_8SPI_PSC_16),降低SPI速度。

2. 显示花屏、错位?

  • 检查DC引脚:确保在发送命令 (LCD_WR_REG) 前将DC拉低,发送数据 (LCD_WR_DATA) 前将DC拉高。这个时序错误是导致花屏的常见原因。
  • 检查屏幕初始化序列LCD_Init()函数里有一长串寄存器配置命令,这些是屏幕厂商提供的,一般不要改动。确认这些命令都正确发送了。
  • 检查USE_HORIZONTAL:这个宏定义了屏幕的显示方向。如果你希望横屏显示但实际是竖屏,或者坐标不对,可以尝试修改这个值(0/1为竖屏,2/3为横屏)。

3. 中文显示乱码?

  • 确保你已经完成了第3.5节的修改,将s+=2改为s+=3,并修改了字库结构体的索引大小。
  • 检查你传入的中文字符串编码。示例代码中的中文是GB2312/GBK编码,如果你的编译器环境默认是UTF-8,可能需要转换或使用其他字库。

4. 显示内容刷新慢?

  • LCD_FillLCD_ShowPicture这种全屏操作会发送大量数据,本身就会慢。可以考虑局部刷新,只更新变化区域。
  • 优化LCD_Writ_Bus函数,检查SPI时钟分频是否还能提高(在屏幕允许的范围内)。

移植工作到这里就基本完成了。整个过程的关键在于引脚配置、SPI初始化和底层通信函数的适配。一旦底层打通,上层的图形、文字显示函数都是通用的。你可以基于lcd.c中提供的画点、画线、填充、显示字符等函数,构建更复杂的用户界面。希望这篇教程能帮你顺利点亮手中的小屏幕。

http://www.jsqmd.com/news/473534/

相关文章:

  • UABEA:Unity资源处理的全流程解决方案
  • Spring Boot日志实战:从基础配置到Logback高级定制
  • InternLM2-Chat-1.8B入门实践:Python爬虫数据清洗与智能分析
  • 【自控】线性系统典型环节的传递函数解析与应用
  • Simulink入门实战:从零搭建PID控制系统(含模块速查表)
  • ai辅助开发:让快马平台的智能模型成为你的私人c++面试教练
  • 猫抓cat-catch媒体捕获全攻略:从资源嗅探到跨平台适配的革新实践
  • Fish-Speech 1.5快速上手:无需代码,Web界面直接文字转语音
  • 【技术解析】Triangle Splatting:如何用可微分三角形泼溅场革新实时渲染管线
  • 雷达技术核心原理与应用场景解析
  • 日报26-003
  • 实用方案:如何让「丹青幻境」AI绘画服务支持多设备访问
  • 【CVPR2025】BridgeAD+: Enhancing End-to-End Autonomous Driving with Multi-Step Historical Context Fusi
  • 零门槛声音克隆教程:用CosyVoice2快速生成专属语音内容
  • 提升mc游戏效率:借助快马平台打造个性化指令批量处理工具
  • 深入解析Swin Transformer:从架构设计到实现细节
  • Linux内核设计原则:只提供机制,不实现策略
  • 资源管理新范式:UABEA跨平台资源处理工具的全流程解决方案
  • Psins工具箱核心子函数深度剖析:从初始化到状态更新的关键模块
  • 5分钟实战:用油猴脚本为任意网页注入动态交互特效
  • 一站式Crypto解密工具集锦【实战技巧全解析】
  • 手眼标定实战:从9点标定到精准抓取
  • 中文文献管理效能倍增:Zotero茉莉花插件全流程优化指南
  • 从分离轴理论到UE4实现:OBB碰撞检测的实战解析
  • AutoGen Studio企业级部署架构设计
  • E-Prime实验设计进阶技巧:从多字符输入到N-Back任务
  • 中文文献效率提升指南:Zotero茉莉花插件智能管理方案
  • 3步解决音画不同步:LosslessCut无损编辑实战指南
  • 反激电源设计进阶:电压环与电流环的协同控制策略
  • HTML+CSS打造动态圣诞树:从基础到进阶效果实现