STM32移植正点原子LCD驱动教程
文章目录
本人学到LCD屏幕模块的时候,卡了两天,LCD这部分相较于其他基础模块难度提高了不少,视频看了两三遍资料也反复看才对整个原理和流程搞懂了个大概。在使用的时候其实只需要移植官方写好的驱动程序就可以很快上手直接调用API就行。之前用过一个0.96寸的OLED屏幕,移植非常简单,根据自己的硬件连接配好引脚,再找厂家提供的驱动程序直接搬进工程里就可以直接调用相关API了。但是显示屏TFTLCD移植还是比较麻烦点,好些地方需要修改,故在在此记录整个移植流程。
(文末提供我移植好的工程项目文件和包含LCD驱动程序的正点原子官方例程)
一、硬件设备
我的硬件设备是STM32F407ZGT6(正点原子的探索者V3开发板)。
2.8TFTLCD屏幕是买的第三方的厂家的,跟正点原子的开发板兼容,可以直接在相应的接口上对插。
二、使用Cube MX配置驱动接口
Cube MX配置
sys、rcc、串口配置
sys和rcc相关的基础配置是建立工程的首要配置,非常简单便不介绍了。
接下来可以配置一个USART串口用来打印信息(此处可以不配置,官方LCD初始化里有用到,不配置的话删除那句代码即可)。
FSMC配置
红框中的数字参数符合范围基本就行,其实就是经验值,可以自己修改测试看看,我感觉不出太大差别。其他参数根据硬件平台资料设置即可。
FSMC设置好后引脚是自动分配好的,此时最好检查一下看看分配好的引脚是否与自己的硬件平台接口引脚相一致,不一致单独修改即可。
还以一个背光引脚需要自己单独设置如下:
之后就可以导出工程了。
三、LCD驱动程序移植
正点原子的LCD驱动程序主要包括以下四个文件:
里面还用到了正点原子自己写的sys、delay、usart相关文件(.c和.h)。其中usart如果cubemx配置了就会生成自己的usart文件,没有配置就删除里面的#include "usart.h"和lcd_init()函数里的printf串口打印语句即可。
此时有两种方案:
方案一: sys和delay相关文件可以直接copy到自己的工程里,这样就少修改一些程序部分;
方案二: 不copy“sys”和“delay”相关文件,删除各个相关程序里的#include “'… sys.h” 和#include “'… delay.h”,然后将delay_ms(x)改成HAL_Delay(x),将delay_us(x)改成HAL_Delay(1)。
我采用的方案一:
1、 先将所需文件copy到LCD文件夹里,然后将文件夹放到工程里。
以上都是keil工程里添加项目文件和路径的。
2、 lcd.c中修改初始化程序部分
下面这个HAL_SRAM_MspInit函数要删除,因为Cube MX生成的fsmc.c文件中包含了。
lcd_init()函数中的引脚和fsmc参数设置部分也要删除或注释,同样生成的fsmc.c文件中包含了。只保留FSMC_NORSRAM_TimingTypeDef fsmc_write_handle,因为后面有用到。
void lcd_init(void)
{
// GPIO_InitTypeDef gpio_init_struct;
// FSMC_NORSRAM_TimingTypeDef fsmc_read_handle;
FSMC_NORSRAM_TimingTypeDef fsmc_write_handle;
// LCD_CS_GPIO_CLK_ENABLE(); /* LCD_CS脚时钟使能 */
// LCD_WR_GPIO_CLK_ENABLE(); /* LCD_WR脚时钟使能 */
// LCD_RD_GPIO_CLK_ENABLE(); /* LCD_RD脚时钟使能 */
// LCD_RS_GPIO_CLK_ENABLE(); /* LCD_RS脚时钟使能 */
// LCD_BL_GPIO_CLK_ENABLE(); /* LCD_BL脚时钟使能 */
//
// gpio_init_struct.Pin = LCD_CS_GPIO_PIN;
// gpio_init_struct.Mode = GPIO_MODE_AF_PP; /* 推挽复用 */
// gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
// gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
// gpio_init_struct.Alternate = GPIO_AF12_FSMC; /* 复用为FSMC */
// HAL_GPIO_Init(LCD_CS_GPIO_PORT, &gpio_init_struct); /* 初始化LCD_CS引脚 */
// gpio_init_struct.Pin = LCD_WR_GPIO_PIN;
// HAL_GPIO_Init(LCD_WR_GPIO_PORT, &gpio_init_struct); /* 初始化LCD_WR引脚 */
// gpio_init_struct.Pin = LCD_RD_GPIO_PIN;
// HAL_GPIO_Init(LCD_RD_GPIO_PORT, &gpio_init_struct); /* 初始化LCD_RD引脚 */
// gpio_init_struct.Pin = LCD_RS_GPIO_PIN;
// HAL_GPIO_Init(LCD_RS_GPIO_PORT, &gpio_init_struct); /* 初始化LCD_RS引脚 */
// gpio_init_struct.Pin = LCD_BL_GPIO_PIN;
// gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出 */
// HAL_GPIO_Init(LCD_BL_GPIO_PORT, &gpio_init_struct); /* LCD_BL引脚模式设置(推挽输出) */
// g_sram_handle.Instance = FSMC_NORSRAM_DEVICE;
// g_sram_handle.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;
//
// g_sram_handle.Init.NSBank = FSMC_NORSRAM_BANK4; /* 使用NE4 */
// g_sram_handle.Init.DataAddressMux = FSMC_DATA_ADDRESS_MUX_DISABLE; /* 地址/数据线不复用 */
// g_sram_handle.Init.MemoryDataWidth = FSMC_NORSRAM_MEM_BUS_WIDTH_16; /* 16位数据宽度 */
// g_sram_handle.Init.BurstAccessMode = FSMC_BURST_ACCESS_MODE_DISABLE; /* 是否使能突发访问,仅对同步突发存储器有效,此处未用到 */
// g_sram_handle.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW; /* 等待信号的极性,仅在突发模式访问下有用 */
// g_sram_handle.Init.WaitSignalActive = FSMC_WAIT_TIMING_BEFORE_WS; /* 存储器是在等待周期之前的一个时钟周期还是等待周期期间使能NWAIT */
// g_sram_handle.Init.WriteOperation = FSMC_WRITE_OPERATION_ENABLE; /* 存储器写使能 */
// g_sram_handle.Init.WaitSignal = FSMC_WAIT_SIGNAL_DISABLE; /* 等待使能位,此处未用到 */
// g_sram_handle.Init.ExtendedMode = FSMC_EXTENDED_MODE_ENABLE; /* 读写使用不同的时序 */
// g_sram_handle.Init.AsynchronousWait = FSMC_ASYNCHRONOUS_WAIT_DISABLE; /* 是否使能同步传输模式下的等待信号,此处未用到 */
// g_sram_handle.Init.WriteBurst = FSMC_WRITE_BURST_DISABLE; /* 禁止突发写 */
//
// /* FSMC读时序控制寄存器 */
// fsmc_read_handle.AddressSetupTime = 0x0F; /* 地址建立时间(ADDSET)为15个fsmc_ker_ck(1/168=6)即6*15=90ns */
// fsmc_read_handle.AddressHoldTime = 0x00; /* 地址保持时间(ADDHLD) 模式A是没有用到 */
// fsmc_read_handle.DataSetupTime = 60; /* 数据保存时间(DATAST)为60个fsmc_ker_ck=6*60=360ns */
// /* 因为液晶驱动IC的读数据的时候,速度不能太快,尤其是个别奇葩芯片 */
// fsmc_read_handle.AccessMode = FSMC_ACCESS_MODE_A; /* 模式A */
//
// /* FSMC写时序控制寄存器 */
// fsmc_write_handle.AddressSetupTime = 9; /* 地址建立时间(ADDSET)为9个fsmc_ker_ck=6*9=54ns */
// fsmc_write_handle.AddressHoldTime = 0x00; /* 地址保持时间(ADDHLD) 模式A是没有用到 */
// fsmc_write_handle.DataSetupTime = 9; /* 数据保存时间(DATAST)为9个fsmc_ker_ck=6*9=54ns */
// /* 注意:某些液晶驱动IC的写信号脉宽,最少也得50ns */
// fsmc_write_handle.AccessMode = FSMC_ACCESS_MODE_A; /* 模式A */
//
// HAL_SRAM_Init(&g_sram_handle, &fsmc_read_handle, &fsmc_write_handle);
// delay_ms(50);
/* 尝试9341 ID的读取 */
lcd_wr_regno(0xD3);
lcddev.id = lcd_rd_data(); /* dummy read */
lcddev.id = lcd_rd_data(); /* 读到0x00 */
lcddev.id = lcd_rd_data(); /* 读取93 */
lcddev.id <<= 8;
lcddev.id |= lcd_rd_data(); /* 读取41 */
。。。。。。
。。。。。。
。。。。。。
3、 替换所有头文件格式,可以先编译,根据编译的报错信息定位需要修改的地方。
4、 主程序部分调用初始化模块后就可以驱动正常LCD了
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_FSMC_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
delay_init(168);
lcd_init();
lcd_show_string(10, 40, 240, 32, 32, "STM32", RED);
lcd_show_string(10, 80, 240, 24, 24, "TFTLCD TEST", RED);
lcd_show_string(10, 110, 240, 16, 16, "ATOM@ALIENTEK", RED);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
lcd中的其他API也可正常使用
/******************************************************************************************/
/* 函数声明 */
void lcd_wr_data(volatile uint16_t data); /* LCD写数据 */
void lcd_wr_regno(volatile uint16_t regno); /* LCD写寄存器编号/地址 */
void lcd_write_reg(uint16_t regno, uint16_t data); /* LCD写寄存器的值 */
void lcd_init(void); /* 初始化LCD */
void lcd_display_on(void); /* 开显示 */
void lcd_display_off(void); /* 关显示 */
void lcd_scan_dir(uint8_t dir); /* 设置屏扫描方向 */
void lcd_display_dir(uint8_t dir); /* 设置屏幕显示方向 */
void lcd_ssd_backlight_set(uint8_t pwm); /* SSD1963 背光控制 */
void lcd_write_ram_prepare(void); /* 准备写GRAM */
void lcd_set_cursor(uint16_t x, uint16_t y); /* 设置光标 */
uint32_t lcd_read_point(uint16_t x, uint16_t y); /* 读点(32位颜色,兼容LTDC) */
void lcd_draw_point(uint16_t x, uint16_t y, uint32_t color);/* 画点(32位颜色,兼容LTDC) */
void lcd_clear(uint16_t color); /* LCD清屏 */
void lcd_fill_circle(uint16_t x, uint16_t y, uint16_t r, uint16_t color); /* 填充实心圆 */
void lcd_draw_circle(uint16_t x0, uint16_t y0, uint8_t r, uint16_t color); /* 画圆 */
void lcd_draw_hline(uint16_t x, uint16_t y, uint16_t len, uint16_t color); /* 画水平线 */
void lcd_set_window(uint16_t sx, uint16_t sy, uint16_t width, uint16_t height); /* 设置窗口 */
void lcd_fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint32_t color); /* 纯色填充矩形(32位颜色,兼容LTDC) */
void lcd_color_fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint16_t *color); /* 彩色填充矩形 */
void lcd_draw_line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color); /* 画直线 */
void lcd_draw_rectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color);/* 画矩形 */
void lcd_show_char(uint16_t x, uint16_t y, char chr, uint8_t size, uint8_t mode, uint16_t color); /* 显示一个字符 */
void lcd_show_num(uint16_t x, uint16_t y, uint32_t num, uint8_t len, uint8_t size, uint16_t color); /* 显示数字 */
void lcd_show_xnum(uint16_t x, uint16_t y, uint32_t num, uint8_t len, uint8_t size, uint8_t mode, uint16_t color); /* 扩展显示数字 */
void lcd_show_string(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t size, char *p, uint16_t color); /* 显示字符串 */
#endif
如果不使用串口则将下面的lcd_init()中的printf函数注释或删掉
如果需要使用则配置打开串口并重定向fputc:
usart.h中添加:
#include "stdio.h"
int fputc(int ch, FILE *f);
usart.c中添加:
int fputc(int ch, FILE *f)
{
while ((USART1->SR & 0X40) == 0);
USART1->DR = (uint8_t)ch;
return ch;
}
——————————工程文件链接—————————————-
通过网盘分享的文件:LCD移植
链接: https://pan.baidu.com/s/1-6SlhXdby1uMBmbVKvW74A?pwd=v67m 提取码: v67m
–来自百度网盘超级会员v6的分享
作者:Gui_Zeng