拿到STM32L562E-DK开发板的第一时间,我就被这块240x240分辨率的IPS触摸屏吸引了。作为ST新一代安全微控制器,L5系列在保持低功耗特性的同时增加了TrustZone硬件隔离功能,这块开发板正是探索这些特性的绝佳平台。下面分享我从零开始驱动这块屏幕的完整过程。
对于嵌入式开发者而言,显示屏驱动是检验硬件掌握程度的试金石。不同于简单的LED闪烁,屏幕驱动涉及时钟配置、硬件初始化、图形库调用等多个环节,任何一个步骤出错都可能导致白屏、花屏或者触摸失灵。通过这个案例,你将掌握STM32L5系列外设驱动的标准开发流程。
STM32L562E-DK开发板采用STM32L562QEI6QU主控,基于Arm Cortex-M33内核,运行频率110MHz。板载资源包括:
特别要注意的是,L5系列采用了新型的智能架构:
开发板通过16位8080并行接口连接LCD,触摸屏采用I2C接口。查看原理图发现:
在CubeMX中配置时,需要特别注意:
并行接口必须配置为存储器映射模式,才能使用BSP库的加速功能
L5系列的时钟树比F系列复杂许多,包含多个时钟域:
c复制void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
// 配置MSI为110MHz
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;
RCC_OscInitStruct.MSIState = RCC_MSI_ON;
RCC_OscInitStruct.MSICalibrationValue = RCC_MSICALIBRATION_DEFAULT;
RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_6; // 110MHz
HAL_RCC_OscConfig(&RCC_OscInitStruct);
// 配置时钟分频
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5);
}
关键点说明:
硬件初始化遵循严格顺序:
c复制void SystemHardwareInit(void)
{
/* 1. 初始化LED指示灯 */
BSP_LED_Init(LED9); // 绿色
BSP_LED_Init(LED10); // 红色
/* 2. 配置用户按钮为外部中断 */
BSP_PB_Init(BUTTON_USER, BUTTON_MODE_EXTI);
/* 3. LCD初始化 */
BSP_LCD_Init(0, LCD_ORIENTATION_PORTRAIT);
UTIL_LCD_SetFuncDriver(&lcdDrv);
UTIL_LCD_Clear(UTIL_LCD_COLOR_WHITE);
/* 4. 触摸屏初始化 */
TS_Init_t TsInit = {
.Width = 240,
.Height = 240,
.Orientation = TS_ORIENTATION_PORTRAIT,
.Accuracy = 10
};
BSP_TS_Init(0, &TsInit);
BSP_TS_EnableIT(0); // 使能触摸中断
}
常见问题排查:
ST提供了UTIL_LCD抽象层,支持多种绘图操作:
c复制// 设置字体
UTIL_LCD_SetFont(&Font20);
// 设置颜色
UTIL_LCD_SetTextColor(UTIL_LCD_COLOR_DARKBLUE);
UTIL_LCD_SetBackColor(UTIL_LCD_COLOR_WHITE);
// 绘制文本
UTIL_LCD_DisplayStringAt(0, 10, (uint8_t *)"Hello World", CENTER_MODE);
// 绘制矩形
UTIL_LCD_FillRect(0, 145, 240, 50, UTIL_LCD_COLOR_BLUE);
// 绘制位图
UTIL_LCD_DrawBitmap(80, 65, (uint8_t *)logo);
实际项目通常需要页面管理系统:
c复制typedef struct {
void (*Init)(void);
void (*Draw)(void);
void (*Handler)(void);
} Page_t;
Page_t pages[] = {
{Home_Init, Home_Draw, Home_Handler},
{Menu_Init, Menu_Draw, Menu_Handler},
};
void Display_Run(void)
{
static uint8_t currentPage = 0;
while(1) {
pages[currentPage].Draw();
pages[currentPage].Handler();
if(buttonPressed) {
currentPage = (currentPage + 1) % 2;
pages[currentPage].Init();
}
}
}
优化技巧:
通过中断获取触摸坐标:
c复制void BSP_TS_Callback(uint16_t Instance)
{
static TS_State_t TS_State;
BSP_TS_GetState(0, &TS_State);
if(TS_State.TouchDetected) {
uint16_t X = TS_State.TouchX;
uint16_t Y = TS_State.TouchY;
// 坐标转换(根据旋转方向)
if(LcdOrientation == LCD_ORIENTATION_LANDSCAPE) {
X = 240 - Y;
Y = TS_State.TouchX;
}
// 处理触摸事件
Handle_Touch(X, Y);
}
}
基础手势识别实现:
c复制typedef struct {
uint16_t startX, startY;
uint16_t lastX, lastY;
uint32_t startTime;
} Gesture_t;
void Detect_Gesture(Gesture_t* gesture)
{
// 计算移动距离
int16_t dx = gesture->lastX - gesture->startX;
int16_t dy = gesture->lastY - gesture->startY;
// 判断手势类型
if(abs(dx) > 50 && abs(dy) < 30) {
if(dx > 0) printf("Swipe Right\n");
else printf("Swipe Left\n");
}
else if(abs(dy) > 50 && abs(dx) < 30) {
if(dy > 0) printf("Swipe Down\n");
else printf("Swipe Up\n");
}
}
使用MDMA加速矩形填充:
c复制void LCD_FillRect_DMA(uint16_t Xpos, uint16_t Ypos, uint16_t Width, uint16_t Height, uint32_t Color)
{
// 配置DMA2D
hdma2d.Init.Mode = DMA2D_R2M;
hdma2d.Init.ColorMode = DMA2D_OUTPUT_RGB565;
hdma2d.Init.OutputOffset = 0;
// 启动传输
HAL_DMA2D_Start(&hdma2d, Color, (uint32_t)&hlcd.Layer1, Width, Height);
HAL_DMA2D_PollForTransfer(&hdma2d, 100);
}
针对L5的256KB内存限制:
c复制// 从QSPI加载位图
void Load_Bitmap(uint32_t addr, uint8_t* buf, uint32_t size)
{
BSP_QSPI_EnableMemoryMappedMode();
memcpy(buf, (void*)(0x90000000 + addr), size);
BSP_QSPI_DisableMemoryMappedMode();
}
经过一周的调试优化,最终实现了稳定60fps的界面刷新率,触摸响应时间小于50ms。这个案例展示了STM32L5系列在嵌入式图形应用中的强大能力,特别是其安全特性非常适合工业HMI应用。