1. RT-Thread下ILI9341液晶驱动开发实战
最近在正点原子MINI STM32开发板上实现了RT-Thread操作系统下的ILI9341液晶驱动,过程中踩了不少坑,也积累了一些实战经验。这个240x320分辨率的TFT屏在嵌入式领域应用广泛,但它的并口驱动实现有不少技术细节需要注意,特别是当结合RT-Thread这样的实时操作系统时。
2. 硬件准备与原理分析
2.1 开发板与屏幕选型
我使用的是正点原子MINI STM32 V2.0开发板,主控为STM32F103RBT6。这块板子虽然资源有限(128KB Flash/20KB RAM),但完全足够驱动ILI9341液晶屏。屏幕接口为16位并口,使用FSMC(Flexible Static Memory Controller)接口可以大幅提升刷新效率。
注意:STM32F103系列的FSMC只有100系列和互联型产品才有,MINI板正好有这个功能
2.2 引脚分配关键点
查看原理图发现PB3和PB4默认是JTAG功能引脚,必须特别处理:
c复制// 关闭JTAG复用功能
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
这一步非常关键,否则这两个IO无法正常控制。实测发现如果不做这个配置,屏幕的DC和RESET信号会异常。
3. RT-Thread下的驱动实现
3.1 底层IO初始化
首先配置FSMC接口,这里采用Bank1的NE4片选区域:
c复制// FSMC配置结构体
FSMC_NORSRAMInitTypeDef FSMC_InitStructure;
FSMC_InitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM4;
FSMC_InitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable;
FSMC_InitStructure.FSMC_MemoryType = FSMC_MemoryType_SRAM;
FSMC_InitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;
FSMC_InitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable;
FSMC_InitStructure.FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable;
FSMC_InitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
FSMC_InitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable;
FSMC_InitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;
FSMC_InitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable;
FSMC_InitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable;
FSMC_InitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable;
FSMC_InitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable;
FSMC_InitStructure.FSMC_ReadWriteTimingStruct = &readWriteTiming;
FSMC_NORSRAMInit(&FSMC_InitStructure);
FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM4, ENABLE);
3.2 核心驱动函数实现
基础写命令和写数据函数:
c复制#define LCD_CMD_ADDR ((uint32_t)0x60000000)
#define LCD_DATA_ADDR ((uint32_t)0x60020000)
void LCD_WR_REG(uint8_t reg) {
*(volatile uint16_t *)LCD_CMD_ADDR = reg;
}
void LCD_WR_DATA(uint16_t data) {
*(volatile uint16_t *)LCD_DATA_ADDR = data;
}
技巧:使用volatile关键字防止编译器优化,确保每次访问都是真实的硬件操作
4. ILI9341初始化序列
完整的初始化序列如下,每个命令都有特定作用:
| 命令 | 参数 | 功能说明 |
|---|---|---|
| 0xCF | 0x00,0xC1,0x30 | 电源控制A |
| 0xED | 0x64,0x03,0x12,0x81 | 电源控制B |
| 0xE8 | 0x85,0x10,0x7A | 驱动时序控制A |
| 0xCB | 0x39,0x2C,0x00,0x34,0x02 | 驱动时序控制B |
| 0xF7 | 0x20 | 泵比率控制 |
| 0xEA | 0x00,0x00 | 驱动时序控制B |
关键初始化步骤:
- 发送软件复位命令(0x01)
- 延时120ms等待复位完成
- 配置电源控制寄存器
- 设置像素格式(0x3A)
- 配置内存访问控制(0x36)
- 退出睡眠模式(0x11)
- 开启显示(0x29)
5. 屏幕方向控制技巧
ILI9341支持8种扫描方向,通过0x36寄存器控制:
c复制void LCD_Scan_Dir(uint8_t dir) {
uint16_t regval = 0;
switch(dir) {
case L2R_U2D: regval |= (0<<7)|(0<<6)|(0<<5); break;
case L2R_D2U: regval |= (1<<7)|(0<<6)|(0<<5); break;
case R2L_U2D: regval |= (0<<7)|(1<<6)|(0<<5); break;
// 其他方向类似...
}
LCD_WR_REG(0x36);
LCD_WR_DATA(regval | 0x08); // 固定设置BGR格式
}
实测发现:某些扫描方向需要交换X/Y坐标的设定值,否则显示区域会错乱
6. 性能优化技巧
6.1 使用DMA加速填充
对于全屏填充等操作,可以启用DMA:
c复制void LCD_Fill_DMA(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) {
LCD_SetWindow(x1, y1, x2, y2);
DMA_Config(color, (x2-x1+1)*(y2-y1+1));
LCD_WR_REG(0x2C);
// 启动DMA传输...
}
6.2 双缓冲机制
在RT-Thread中可以实现双缓冲减少闪烁:
- 创建两个显示缓冲区
- 使用信号量同步访问
- 在RT-Thread的绘图线程中切换缓冲区
7. 常见问题排查
7.1 屏幕无显示
- 检查背光电路是否正常
- 测量RESET信号是否正常
- 确认初始化序列是否完整执行
- 检查FSMC时序配置
7.2 显示花屏
- 检查内存访问控制寄存器(0x36)设置
- 确认扫描方向与坐标范围匹配
- 检查FSMC数据线连接是否牢固
7.3 颜色异常
- 检查像素格式设置(0x3A)
- 确认是RGB还是BGR格式
- 检查颜色数据位是否对齐
8. RT-Thread集成建议
将驱动封装为RT-Thread的设备驱动:
c复制static struct rt_device lcd_dev;
static struct rt_device_ops lcd_ops = {
.init = lcd_init,
.open = lcd_open,
.control = lcd_control
};
int lcd_hw_init(void) {
rt_device_register(&lcd_dev, "lcd", RT_DEVICE_FLAG_RDWR);
return 0;
}
INIT_DEVICE_EXPORT(lcd_hw_init);
这样上层应用可以通过标准设备接口访问LCD,提高代码可移植性。
9. 实际应用示例
在RT-Thread中创建一个刷屏线程:
c复制static void lcd_thread_entry(void *parameter) {
while(1) {
LCD_Clear(RED);
rt_thread_mdelay(500);
LCD_Clear(GREEN);
rt_thread_mdelay(500);
LCD_Clear(BLUE);
rt_thread_mdelay(500);
}
}
int lcd_test_init(void) {
rt_thread_t tid = rt_thread_create("lcd",
lcd_thread_entry,
RT_NULL,
1024,
20,
10);
if(tid != RT_NULL) rt_thread_startup(tid);
return 0;
}
10. 进阶开发方向
- 实现LVGL或LittlevGL图形库支持
- 添加触摸屏驱动
- 开发硬件加速绘图功能
- 支持多图层混合显示
- 实现屏幕旋转动画效果
经过实际测试,这套驱动在RT-Thread下运行稳定,刷屏速度可以达到15fps以上(全屏填充)。对于需要GUI的嵌入式应用,这是一个很好的起点。
