手把手教你为1.8寸ST7735S屏移植LVGL图形库(基于STM32标准库/HAL库)

手把手教你为1.8寸ST7735S屏移植LVGL图形库(基于STM32标准库/HAL库) STM32与1.8寸ST7735S屏的LVGL移植实战指南在智能穿戴设备和小型工控HMI开发中为嵌入式系统添加现代化图形界面已成为提升用户体验的关键。本文将深入探讨如何将流行的LVGL图形库移植到基于STM32的1.8寸ST7735S SPI屏幕上涵盖从底层驱动适配到创建第一个交互界面的完整流程。1. 硬件准备与环境搭建1.1 硬件选型与连接我们选择的硬件组合是STM32F103C8T6蓝莓开发板与1.8寸ST7735S SPI屏幕这是性价比极高的开发方案。屏幕引脚连接如下屏幕引脚STM32引脚功能说明GNDGND电源地VCC3.3V电源正极SCLPA5SPI时钟线SDAPA7SPI数据线RESPB0复位信号DCPB1数据/命令选择CSPB10片选信号BLPB11背光控制提示不同厂商的ST7735S屏幕可能存在引脚定义差异务必参考具体产品的数据手册。1.2 开发环境配置对于标准库开发需要准备Keil MDK 5.25STM32标准外设库ST-Link调试器对于HAL库开发推荐使用STM32CubeMX 6.3Keil MDK或STM32CubeIDELVGL库v8.3安装必要的软件包# 通过STM32CubeMX安装HAL库 $ stm32cubemx --installf12. 底层显示驱动实现2.1 SPI接口初始化无论是标准库还是HAL库都需要正确配置SPI接口。以下是HAL库的配置示例// SPI1初始化代码 void MX_SPI1_Init(void) { hspi1.Instance SPI1; hspi1.Init.Mode SPI_MODE_MASTER; hspi1.Init.Direction SPI_DIRECTION_2LINES; hspi1.Init.DataSize SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity SPI_POLARITY_LOW; hspi1.Init.CLKPhase SPI_PHASE_1EDGE; hspi1.Init.NSS SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler SPI_BAUDRATEPRESCALER_4; hspi1.Init.FirstBit SPI_FIRSTBIT_MSB; hspi1.Init.TIMode SPI_TIMODE_DISABLE; hspi1.Init.CRCCalculation SPI_CRCCALCULATION_DISABLE; hspi1.Init.CRCPolynomial 10; if (HAL_SPI_Init(hspi1) ! HAL_OK) { Error_Handler(); } }2.2 ST7735S驱动函数实现需要实现以下核心函数供LVGL调用// 发送命令函数 void st7735_send_cmd(uint8_t cmd) { HAL_GPIO_WritePin(DC_GPIO_Port, DC_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); HAL_SPI_Transmit(hspi1, cmd, 1, HAL_MAX_DELAY); HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET); } // 发送数据函数 void st7735_send_data(uint8_t *data, uint16_t length) { HAL_GPIO_WritePin(DC_GPIO_Port, DC_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); HAL_SPI_Transmit(hspi1, data, length, HAL_MAX_DELAY); HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET); }3. LVGL移植与配置3.1 LVGL库集成从LVGL官网下载最新库文件后需要重点关注以下文件结构/lvgl /src # 核心源码 /examples # 示例代码 /lv_conf.h # 配置文件修改lv_conf.h关键配置#define LV_COLOR_DEPTH 16 // 匹配ST7735S的RGB565格式 #define LV_HOR_RES_MAX 128 // 屏幕水平分辨率 #define LV_VER_RES_MAX 160 // 屏幕垂直分辨率 #define LV_USE_PERF_MONITOR 1 // 启用性能监控3.2 显示接口对接实现LVGL所需的显示驱动接口// 显示刷新回调函数 static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p) { st7735_set_window(area-x1, area-y1, area-x2, area-y2); uint32_t size (area-x2 - area-x1 1) * (area-y2 - area-y1 1); st7735_send_data((uint8_t *)color_p, size * 2); lv_disp_flush_ready(disp_drv); } // 初始化显示驱动 void lv_port_disp_init(void) { static lv_disp_draw_buf_t draw_buf; static lv_color_t buf[LV_HOR_RES_MAX * 10]; // 定义缓冲区 lv_disp_draw_buf_init(draw_buf, buf, NULL, LV_HOR_RES_MAX * 10); static lv_disp_drv_t disp_drv; lv_disp_drv_init(disp_drv); disp_drv.draw_buf draw_buf; disp_drv.flush_cb disp_flush; disp_drv.hor_res LV_HOR_RES_MAX; disp_drv.ver_res LV_VER_RES_MAX; lv_disp_drv_register(disp_drv); }4. 内存优化与性能调优4.1 内存配置策略STM32F103C8T6仅有20KB SRAM需要精心规划内存使用显示缓冲区优化// 双缓冲配置推荐 #define LV_DISP_DEF_REFR_PERIOD 30 #define LV_INDEV_DEF_READ_PERIOD 30 static lv_color_t buf1[LV_HOR_RES_MAX * 20]; static lv_color_t buf2[LV_HOR_RES_MAX * 20];LVGL内存池配置#define LV_MEM_SIZE (8 * 1024) // 分配8KB给LVGL #define LV_MEM_ATTR #define LV_MEM_ADR 0 #define LV_MEM_CUSTOM 04.2 渲染性能优化启用DMA传输修改SPI配置使用DMA// 在CubeMX中启用SPI TX DMA通道 hspi1.hdmatx hdma_spi1_tx;优化刷新区域只刷新发生变化的区域disp_drv.full_refresh 0; // 禁用全屏刷新调整SPI时钟在稳定前提下尽可能提高时钟频率hspi1.Init.BaudRatePrescaler SPI_BAUDRATEPRESCALER_2;5. 创建第一个用户界面5.1 基础UI组件使用创建一个简单的仪表界面void create_ui(void) { // 创建主容器 lv_obj_t * cont lv_obj_create(lv_scr_act()); lv_obj_set_size(cont, LV_HOR_RES_MAX, LV_VER_RES_MAX); // 添加标签 lv_obj_t * label lv_label_create(cont); lv_label_set_text(label, STM32LVGL Demo); lv_obj_align(label, LV_ALIGN_TOP_MID, 0, 10); // 创建仪表 lv_obj_t * meter lv_meter_create(cont); lv_obj_set_size(meter, 100, 100); lv_obj_align(meter, LV_ALIGN_CENTER, 0, 0); // 添加刻度 lv_meter_scale_t * scale lv_meter_add_scale(meter); lv_meter_set_scale_ticks(meter, scale, 11, 2, 10, lv_palette_main(LV_PALETTE_GREY)); lv_meter_set_scale_major_ticks(meter, scale, 1, 2, 15, lv_color_black(), 10); // 添加指针 lv_meter_indicator_t * indic lv_meter_add_needle_line(meter, scale, 4, lv_palette_main(LV_PALETTE_RED), -10); // 创建按钮 lv_obj_t * btn lv_btn_create(cont); lv_obj_set_size(btn, 80, 40); lv_obj_align(btn, LV_ALIGN_BOTTOM_MID, 0, -10); // 按钮事件回调 lv_obj_add_event_cb(btn, btn_event_handler, LV_EVENT_ALL, NULL); }5.2 触摸屏支持可选如果屏幕支持触摸功能需要实现输入设备接口// 触摸屏读取函数 static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data) { static lv_coord_t last_x 0; static lv_coord_t last_y 0; if(touch_get_point(last_x, last_y)) { >static void custom_theme_init(void) { static lv_theme_t theme; lv_theme_default_init(NULL, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_RED), LV_THEME_DEFAULT_DARK, lv_font_montserrat_12); theme.style_btn.radius 3; theme.style_btn.bg_color lv_palette_lighten(LV_PALETTE_GREY, 3); theme.style_btn.bg_opa LV_OPA_COVER; lv_disp_set_theme(NULL, theme); }6.2 动画效果优化针对资源受限设备优化动画// 简化动画配置 lv_anim_set_path_cb(a, lv_anim_path_linear); lv_anim_set_time(a, 300); // 缩短动画时间 lv_anim_set_early_apply(a, true); // 提前应用6.3 电源管理优化背光控制以节省功耗// 自动背光调节 void backlight_control(bool on) { static uint8_t brightness 100; if(on) { for(int i0; ibrightness; i10) { HAL_GPIO_WritePin(BL_GPIO_Port, BL_Pin, GPIO_PIN_SET); HAL_Delay(1); } } else { HAL_GPIO_WritePin(BL_GPIO_Port, BL_Pin, GPIO_PIN_RESET); } }在实际项目中我发现ST7735S屏幕在SPI时钟超过18MHz时会出现数据不稳定现象建议将SPI时钟设置在8-12MHz范围内最为可靠。另外使用双缓冲技术虽然会占用更多内存但能显著提升界面流畅度在20KB SRAM的STM32F103上分配4-6KB作为显示缓冲区是比较理想的选择。