济南做网站建设公司apache 多网站
济南做网站建设公司,apache 多网站,欧美在路边给了钱就可以做网站,热门搜索关键词模块移植手册#xff1a;基于梁山派GD32F470的0.96寸ST7735彩屏驱动移植实战
最近在梁山派GD32F470开发板上折腾一个小屏幕#xff0c;型号是0.96寸的ST7735驱动IPS彩屏。这种小彩屏价格便宜、显示效果好#xff0c;非常适合用在各种DIY项目里做状态显示。但拿到手发现…模块移植手册基于梁山派GD32F470的0.96寸ST7735彩屏驱动移植实战最近在梁山派GD32F470开发板上折腾一个小屏幕型号是0.96寸的ST7735驱动IPS彩屏。这种小彩屏价格便宜、显示效果好非常适合用在各种DIY项目里做状态显示。但拿到手发现卖家给的例程通常都是针对STM32或者51单片机的直接用在咱们的GD32上跑不起来。这不我就把整个移植过程整理了一下从找资料到最终点亮屏幕一步步带你走一遍。无论你是刚接触嵌入式的新手还是想快速搞定模块移植的老手这篇实战指南都能帮到你。1. 准备工作了解屏幕与获取资料1.1 屏幕模块简介咱们这次要驱动的是一块0.96寸的IPS彩屏核心驱动芯片是ST7735。这种屏幕色彩鲜艳、视角广性价比很高。先来看看它的基本参数参数项规格说明工作电压2.8V ~ 3.3V (直接接梁山派的3.3V引脚就行)工作电流约30mA (功耗很低)屏幕尺寸24mm x 30mm (非常小巧)分辨率80 x 160像素 (RGB排列)通信协议SPI (串行外设接口)接口引脚8个Pin (2.54mm间距排针)屏幕通过SPI接口和单片机通信这意味着我们只需要几根线时钟、数据、片选等就能控制它非常节省单片机的IO口资源。1.2 资料获取移植的第一步也是最重要的一步就是找到正确的资料。卖家提供的资料里通常包含了屏幕的数据手册、测试例程和驱动代码这是我们移植的基础。我用的这块屏幕资料链接如下你可以根据自己购买的店铺寻找类似资料资料下载链接https://pan.baidu.com/s/19DxY8JJEzNt4XYF_CwVbDw提取码8888下载后你会看到一个压缩包解压后里面通常有一个以厂家名或屏幕型号命名的文件夹。重点找到里面的LCD文件夹这里面就包含了驱动屏幕的核心代码文件lcd.c、lcd.h、lcd_init.c等和一个演示用的工程。注意不同卖家提供的代码结构可能略有不同但核心的驱动文件尤其是lcd.c和lcd_init.c是类似的。我们的目标就是把这些文件“搬”到我们自己的GD32工程里并让它正常工作。2. 移植思路与步骤总览移植第三方模块驱动听起来复杂其实有个清晰的套路。咱们的目标是把厂家为其他平台比如STM32写的代码适配到梁山派GD32F470上。整个过程可以分解为以下五步导入源码把厂家的LCD驱动文件夹复制到我们的GD32工程目录下。粗改编译错误先把工程编译一遍根据报错信息解决最明显的兼容性问题比如头文件找不到、数据类型未定义等。修改引脚配置根据我们实际连接的梁山派引脚修改驱动代码里的引脚定义和初始化函数。这部分会详细讲软件SPI和硬件SPI两种方式。修改时序配置主要是调整延时函数确保屏幕初始化时序正确。功能验证写一个简单的测试程序看看屏幕能不能正常显示。下面咱们就一步步来操作。3. 工程搭建与初步修改3.1 导入驱动文件首先在你的梁山派工程目录下比如User文件夹里新建一个文件夹可以命名为LCD。然后把从厂家资料里找到的LCD文件夹下的所有.c和.h文件都复制过来。接着打开你的IDE比如Keil MDK在工程管理窗口将刚才复制的.c文件主要是lcd.c和lcd_init.c添加到工程中。然后在工程的“包含路径”Include Paths设置里添加你刚才存放LCD文件的路径这样编译器才能找到对应的.h头文件。3.2 解决头文件与数据类型错误添加完文件后先别管引脚直接编译一下工程。这时候大概率会报错别慌这是移植的必经之路。错误1找不到sys.h厂家代码里可能包含了#include sys.h这是他们原有平台的定义文件。在GD32工程里我们需要把它替换成GD32的标准头文件。修改位置打开lcd_init.h和lcd.h文件。修改方法找到#include sys.h这一行将其改为#include gd32f4xx.h。错误2u8、u16、u32类型未定义很多厂家习惯用u8、u16、u32来代表无符号的8位、16位、32位整数但GD32的标准库可能不这么定义。我们需要自己定义一下。修改位置在lcd_init.h和lcd.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这是C标准库定义的类型在gd32f4xx.h中已经包含了。错误3找不到delay.h或delay_ms函数厂家代码里用delay_ms()函数进行毫秒延时。我们需要把它替换成我们自己工程里的延时函数。假设你的工程里有一个systick.h头文件里面提供了delay_1ms()函数。修改位置打开lcd_init.c和lcd.c文件。修改方法将#include delay.h改为#include systick.h。为了最小化改动我们可以在文件开头添加一个宏替换将delay_ms映射到我们的delay_1ms#include systick.h #define delay_ms delay_1ms这样原代码里所有调用delay_ms()的地方都会自动替换成调用delay_1ms()。解决完这几类错误后再次编译剩下的错误应该就主要是和引脚操作相关的了这说明我们离成功又近了一步。4. 核心步骤引脚配置软件SPI vs 硬件SPI屏幕有8个引脚我们需要搞清楚每个引脚是干嘛的以及如何在梁山派上连接它们。屏幕引脚功能必须连接吗说明VCC电源正极是接3.3VGND电源地是接GNDSCL (SCK)SPI时钟线是SPI通信的时钟信号SDA (MOSI)SPI数据线(主机输出)是单片机发送数据给屏幕RES复位建议连接用于硬件复位屏幕可接普通GPIODC数据/命令选择是高电平传输数据低电平传输命令CS (NSS)片选是低电平选中该SPI从设备屏幕BLK背光控制可选接高电平常亮或接PWM引脚调光提示如果单片机GPIO口非常紧张RES可以接到MCU的复位引脚这样单片机复位时屏幕也跟着复位BLK可以直接接3.3V或悬空代价是无法软件控制背光开关。接下来我们分软件SPI和硬件SPI两种方式来配置引脚。软件SPI简单通用硬件SPI效率高。4.1 软件SPI移植通用方法软件SPI就是用代码控制GPIO口的高低电平变化来模拟SPI的时序。好处是不挑引脚任何GPIO都能用缺点是速度慢且会占用CPU资源。第一步定义引脚宏为了方便修改我们在lcd_init.h文件中用宏定义好每个引脚对应的GPIO端口和引脚号。下面是我在梁山派上使用的引脚分配示例你可以根据实际情况修改// 引脚时钟、端口、引脚号定义 #define RCU_LCD_SCL RCU_GPIOB // SCL时钟线接PB13 #define PORT_LCD_SCL GPIOB #define GPIO_LCD_SCL GPIO_PIN_13 #define RCU_LCD_SDA RCU_GPIOB // SDA数据线接PB15 #define PORT_LCD_SDA GPIOB #define GPIO_LCD_SDA GPIO_PIN_15 #define RCU_LCD_CS RCU_GPIOB // CS片选接PB12 #define PORT_LCD_CS GPIOB #define GPIO_LCD_CS GPIO_PIN_12 #define RCU_LCD_DC RCU_GPIOC // DC数据/命令接PC6 #define PORT_LCD_DC GPIOC #define GPIO_LCD_DC GPIO_PIN_6 #define RCU_LCD_RES RCU_GPIOD // RES复位接PD0 #define PORT_LCD_RES GPIOD #define GPIO_LCD_RES GPIO_PIN_0 #define RCU_LCD_BLK RCU_GPIOC // BLK背光接PC7 #define PORT_LCD_BLK GPIOC #define GPIO_LCD_BLK GPIO_PIN_7第二步编写GPIO初始化函数在lcd_init.c文件中找到LCD_GPIO_Init函数如果没有就自己写一个根据上面的宏定义来初始化每个引脚。每个引脚的配置模式都类似先使能时钟再设置为推挽输出模式。void LCD_GPIO_Init(void) { /* 使能各引脚对应的GPIO时钟 */ rcu_periph_clock_enable(RCU_LCD_SCL); rcu_periph_clock_enable(RCU_LCD_SDA); rcu_periph_clock_enable(RCU_LCD_CS); rcu_periph_clock_enable(RCU_LCD_DC); rcu_periph_clock_enable(RCU_LCD_RES); rcu_periph_clock_enable(RCU_LCD_BLK); /* 配置SCL (SCK) 引脚为推挽输出上拉速度50MHz */ gpio_mode_set(PORT_LCD_SCL, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_LCD_SCL); gpio_output_options_set(PORT_LCD_SCL, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_LCD_SCL); gpio_bit_write(PORT_LCD_SCL, GPIO_LCD_SCL, SET); // 初始化为高电平 /* 配置SDA (MOSI) 引脚 */ gpio_mode_set(PORT_LCD_SDA, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_LCD_SDA); gpio_output_options_set(PORT_LCD_SDA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_LCD_SDA); gpio_bit_write(PORT_LCD_SDA, GPIO_LCD_SDA, SET); /* 配置DC引脚 */ gpio_mode_set(PORT_LCD_DC, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_LCD_DC); gpio_output_options_set(PORT_LCD_DC, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_LCD_DC); gpio_bit_write(PORT_LCD_DC, GPIO_LCD_DC, SET); /* 配置CS引脚 */ gpio_mode_set(PORT_LCD_CS, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_LCD_CS); gpio_output_options_set(PORT_LCD_CS, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_LCD_CS); gpio_bit_write(PORT_LCD_CS, GPIO_LCD_CS, SET); /* 配置RES引脚 */ gpio_mode_set(PORT_LCD_RES, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_LCD_RES); gpio_output_options_set(PORT_LCD_RES, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_LCD_RES); gpio_bit_write(PORT_LCD_RES, GPIO_LCD_RES, SET); /* 配置BLK引脚 */ gpio_mode_set(PORT_LCD_BLK, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_LCD_BLK); gpio_output_options_set(PORT_LCD_BLK, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_LCD_BLK); gpio_bit_write(PORT_LCD_BLK, GPIO_LCD_BLK, SET); // 背光默认点亮 }第三步修改端口操作宏厂家代码里会用一些宏来控制引脚高低电平比如LCD_SCLK_Clr()表示拉低时钟线。我们需要在lcd_init.h里把这些宏改成我们GD32的GPIO操作函数。#define LCD_SCLK_Clr() gpio_bit_write(PORT_LCD_SCL, GPIO_LCD_SCL, RESET) // SCLK 0 #define LCD_SCLK_Set() gpio_bit_write(PORT_LCD_SCL, GPIO_LCD_SCL, SET) // SCLK 1 #define LCD_MOSI_Clr() gpio_bit_write(PORT_LCD_SDA, GPIO_LCD_SDA, RESET) // MOSI 0 #define LCD_MOSI_Set() gpio_bit_write(PORT_LCD_SDA, GPIO_LCD_SDA, SET) // MOSI 1 #define LCD_RES_Clr() gpio_bit_write(PORT_LCD_RES, GPIO_LCD_RES, RESET) // RES 0 #define LCD_RES_Set() gpio_bit_write(PORT_LCD_RES, GPIO_LCD_RES, SET) // RES 1 #define LCD_DC_Clr() gpio_bit_write(PORT_LCD_DC, GPIO_LCD_DC, RESET) // DC 0 #define LCD_DC_Set() gpio_bit_write(PORT_LCD_DC, GPIO_LCD_DC, SET) // DC 1 #define LCD_CS_Clr() gpio_bit_write(PORT_LCD_CS, GPIO_LCD_CS, RESET) // CS 0 #define LCD_CS_Set() gpio_bit_write(PORT_LCD_CS, GPIO_LCD_CS, SET) // CS 1 #define LCD_BLK_Clr() gpio_bit_write(PORT_LCD_BLK, GPIO_LCD_BLK, RESET) // BLK 0 #define LCD_BLK_Set() gpio_bit_write(PORT_LCD_BLK, GPIO_LCD_BLK, SET) // BLK 1完成以上三步软件SPI的移植就基本完成了。厂家提供的LCD_Writ_Bus等底层通信函数已经用这些宏实现了SPI时序我们不需要改动。4.2 硬件SPI移植高效方法硬件SPI利用单片机内部的SPI外设控制器来生成通信时序速度快、不占用CPU时间。梁山派GD32F470有6个SPI外设我们以SPI1为例。第一步选择支持SPI复用功能的引脚硬件SPI要求SCK和MOSI引脚必须连接到单片机指定的、支持SPI复用功能的引脚上。查阅GD32F470的数据手册可知PB13是SPI1_SCKPB15是SPI1_MOSI。其他引脚RES, DC, CS, BLK仍然可以使用任意GPIO。 我的引脚分配如下SCL (SCK)- PB13 (SPI1_SCK)SDA (MOSI)- PB15 (SPI1_MOSI)CS- PB12 (普通GPIO软件控制)DC- PC6RES- PD0BLK- PC7在lcd_init.h中的宏定义与软件SPI类似但需要额外定义SPI外设相关的宏// 引脚定义部分与软件SPI相同参考上一节... // 新增SPI硬件相关定义 #define RCU_SPI_HARDWARE RCU_SPI1 // SPI1时钟 #define PORT_SPI SPI1 // SPI1外设 #define LINE_AF_SPI GPIO_AF_5 // 引脚复用功能编号为AF5第二步编写硬件SPI的GPIO与SPI初始化函数这里的初始化分为两部分一是配置PB13和PB15为SPI复用功能二是初始化SPI1外设本身。void LCD_GPIO_Init(void) { spi_parameter_struct spi_init_struct; /* 1. 开启所有用到的GPIO时钟和SPI1时钟 */ rcu_periph_clock_enable(RCU_LCD_SCL); rcu_periph_clock_enable(RCU_LCD_SDA); rcu_periph_clock_enable(RCU_LCD_CS); rcu_periph_clock_enable(RCU_LCD_DC); rcu_periph_clock_enable(RCU_LCD_RES); rcu_periph_clock_enable(RCU_LCD_BLK); rcu_periph_clock_enable(RCU_SPI_HARDWARE); // 使能SPI1时钟 /* 2. 配置SPI1的SCK引脚(PB13)为复用功能 */ gpio_af_set(PORT_LCD_SCL, LINE_AF_SPI, GPIO_LCD_SCL); // 设置复用功能为AF5 gpio_mode_set(PORT_LCD_SCL, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_LCD_SCL); // 模式复用功能 gpio_output_options_set(PORT_LCD_SCL, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_LCD_SCL); // 推挽输出高速 /* 3. 配置SPI1的MOSI引脚(PB15)为复用功能 */ gpio_af_set(PORT_LCD_SDA, LINE_AF_SPI, GPIO_LCD_SDA); gpio_mode_set(PORT_LCD_SDA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_LCD_SDA); gpio_output_options_set(PORT_LCD_SDA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_LCD_SDA); /* 4. 配置其他GPIODC, RES, BLK, CS为普通推挽输出与软件SPI部分相同 */ // ... 此处代码与上一节软件SPI的DC/RES/BLK/CS配置完全一样省略以节省篇幅 // 注意CS引脚虽然用软件控制但模式也是GPIO_MODE_OUTPUT /* 5. 配置SPI1外设参数 */ 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; // 时钟极性相位常用模式3 spi_init_struct.nss SPI_NSS_SOFT; // 软件控制NSS(CS)引脚 spi_init_struct.prescale SPI_PSC_2; // SPI时钟2分频 spi_init_struct.endian SPI_ENDIAN_MSB; // 高位先发送 spi_init(PORT_SPI, spi_init_struct); /* 6. 使能SPI1 */ spi_enable(PORT_SPI); }注意SPI_CK_PL_HIGH_PH_2EDGE是SPI模式3这是ST7735屏幕常用的通信模式。如果屏幕不亮可以尝试改为SPI_CK_PL_LOW_PH_1EDGE模式0。第三步修改底层数据发送函数这是硬件SPI移植最关键的一步。厂家原来的LCD_Writ_Bus函数是用软件模拟时序的我们需要把它改成使用SPI硬件发送数据。 在lcd_init.c文件中找到void LCD_Writ_Bus(u8 dat)函数将其修改为void LCD_Writ_Bus(u8 dat) { LCD_CS_Clr(); // 拉低CS选中屏幕 // 等待发送缓冲区为空表示可以发送新数据了 while(RESET spi_i2s_flag_get(PORT_SPI, SPI_FLAG_TBE)); // 通过SPI硬件发送一个字节数据 spi_i2s_data_transmit(PORT_SPI, dat); // 等待接收缓冲区非空对于只发送不接收此步骤可确保数据已发送完成 while(RESET spi_i2s_flag_get(PORT_SPI, SPI_FLAG_RBNE)); // 读取接收到的数据虽然我们不需要但该操作会清除标志位 spi_i2s_data_receive(PORT_SPI); LCD_CS_Set(); // 拉高CS取消选中 }这个函数的作用就是通过SPI1发送一个字节的数据给屏幕。spi_i2s_flag_get函数用于查询SPI状态标志位确保数据正确发送完毕。第四步修改端口操作宏这一步和软件SPI完全一样因为DC、RES、CS、BLK这些控制引脚的操作方式没有变直接使用4.1节第三步中相同的宏定义即可。至此硬件SPI的移植也完成了。两种方式任选其一我建议如果引脚允许优先使用硬件SPI性能好很多。5. 功能验证与测试所有代码修改完成后就可以写个简单的main函数来测试屏幕是否正常工作了。在你的main.c文件中包含必要的头文件然后初始化屏幕并显示一些内容。#include gd32f4xx.h #include systick.h // 你的延时函数头文件 #include lcd_init.h #include lcd.h int main(void) { float test_number 0.0f; // 系统初始化根据你的工程设置 nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2); systick_config(); // 初始化滴答定时器提供delay_1ms函数 // 屏幕初始化 LCD_Init(); // 清屏填充黑色 LCD_Fill(0, 0, LCD_W, LCD_H, BLACK); while(1) { // 显示屏幕宽度和高度 LCD_ShowString(0, 16*2, LCD_W:, WHITE, BLACK, 16, 0); LCD_ShowIntNum(48, 16*2, LCD_W, 3, WHITE, BLACK, 16); // LCD_W通常是80 LCD_ShowString(80, 16*2, LCD_H:, WHITE, BLACK, 16, 0); LCD_ShowIntNum(128, 16*2, LCD_H, 3, WHITE, BLACK, 16); // LCD_H通常是160 // 显示一个递增的浮点数 LCD_ShowString(0, 16*3, Num:, WHITE, BLACK, 16, 0); LCD_ShowFloatNum1(8*4, 16*3, test_number, 4, WHITE, BLACK, 16); test_number 0.11; // 延时1秒 delay_1ms(1000); } }编译工程下载到梁山派开发板上电。如果一切顺利你应该能看到屏幕点亮并显示屏幕的尺寸信息以及一个不断变化的小数。如果屏幕没有显示别着急按以下顺序排查检查硬件连接VCC和GND是否接反所有线是否接触良好这是最常见的问题。检查背光BLK引脚是否接高电平或接在了能输出高电平的GPIO上屏幕背光可能单独控制。检查SPI模式尝试修改spi_init_struct.clock_polarity_phase在模式0和模式3之间切换。ST7735常用模式3。检查延时屏幕初始化需要一定的延时确保你的delay_1ms函数工作正常。使用调试器单步调试LCD_Init()函数看是否卡在某个循环里或者用逻辑分析仪抓一下SPI引脚波形看是否有数据发出。移植成功后你就可以愉快地使用厂家提供的LCD_ShowString、LCD_DrawLine、LCD_ShowPicture等各种函数在屏幕上绘制图形和文字了。整个移植过程虽然步骤不少但每一步都有明确的目