文章目录
-
- 一、开发环境搭建
-
- [1.1 硬件准备](#1.1 硬件准备)
- [1.2 软件环境安装](#1.2 软件环境安装)
-
- [1.2.1 基础工具安装](#1.2.1 基础工具安装)
- [1.2.2 环境配置验证](#1.2.2 环境配置验证)
- 二、STM32底层驱动开发
-
- [2.1 使用STM32CubeMX生成初始化代码](#2.1 使用STM32CubeMX生成初始化代码)
-
- [2.1.1 工程创建](#2.1.1 工程创建)
- [2.1.2 核心外设配置](#2.1.2 核心外设配置)
-
- [(1) 时钟配置](#(1) 时钟配置)
- [(2) LCD显示相关配置](#(2) LCD显示相关配置)
- [(3) 定时器配置(LVGL刷新用)](#(3) 定时器配置(LVGL刷新用))
- [(4) 串口配置(调试用)](#(4) 串口配置(调试用))
- [2.1.3 代码生成](#2.1.3 代码生成)
- [2.2 LCD驱动开发](#2.2 LCD驱动开发)
- [2.3 定时器中断配置(LVGL心跳)](#2.3 定时器中断配置(LVGL心跳))
- 三、LVGL移植与配置
-
- [3.1 LVGL源码移植](#3.1 LVGL源码移植)
- [3.2 LVGL移植文件开发](#3.2 LVGL移植文件开发)
- [3.3 LVGL初始化整合](#3.3 LVGL初始化整合)
- 四、智能仪表盘界面开发
-
- [4.1 界面整体设计流程](#4.1 界面整体设计流程)
- [4.2 仪表盘界面代码开发](#4.2 仪表盘界面代码开发)
- 五、代码编译与下载
-
- [5.1 Keil5工程配置](#5.1 Keil5工程配置)
- [5.2 编译代码](#5.2 编译代码)
- [5.3 下载代码](#5.3 下载代码)
- 六、调试与优化
-
- [6.1 常见问题解决](#6.1 常见问题解决)
- [6.2 性能优化](#6.2 性能优化)
- 总结
一、开发环境搭建
1.1 硬件准备
你需要准备以下硬件设备,这是完成智能仪表盘开发的基础:
- 主控板:STM32F429IGT6开发板(需带LCD接口,推荐正点原子阿波罗F429开发板)
- 显示屏:4.3寸/7寸TFT-LCD(分辨率800*480,RGB565格式)
- 调试器:ST-Link V2
- 电源:5V/2A直流电源
- 数据线:USB-TTL串口线、Micro USB线
1.2 软件环境安装
1.2.1 基础工具安装
- STM32CubeMX(版本6.9.0及以上):用于生成STM32初始化代码
- 下载地址:https://www.st.com/en/development-tools/stm32cubemx.html
- 安装完成后需安装STM32F4系列固件包(F4 v1.27.0)
- MDK-ARM(Keil5,版本5.38及以上):用于代码编译和下载
- 安装完成后需添加STM32F429的器件库
- LVGL源码(版本8.3.10):轻量级嵌入式GUI库
- 串口调试助手(如SSCOM):用于调试信息输出
1.2.2 环境配置验证
安装完成后,打开STM32CubeMX,确认能正常选择STM32F429IGT6芯片;打开Keil5,确认能新建STM32F4工程,环境配置即完成。
二、STM32底层驱动开发
2.1 使用STM32CubeMX生成初始化代码
2.1.1 工程创建
- 打开STM32CubeMX,点击
New Project,搜索STM32F429IGT6并选择对应芯片 - 配置工程基础参数:
- Project Name:
STM32F429_LVGL_Dashboard - Project Location:自定义路径(不要包含中文和空格)
- Toolchain/IDE:
MDK-ARM v5
- Project Name:
- 点击
Start Project进入配置界面
2.1.2 核心外设配置
(1) 时钟配置
- 点击
Clock Configuration - 选择外部晶振(HSE)为8MHz
- PLL倍频配置:最终使系统时钟达到180MHz
- PLL_M:8
- PLL_N:360
- PLL_P:2
- PLL_Q:7
- 确认AHB时钟=180MHz,APB1=45MHz,APB2=90MHz
(2) LCD显示相关配置
- 点击
Pinout & Configuration - 启用FMC(用于LCD并行接口):
- 展开
Connectivity->FMC,选择NOR/PSRAM模式 - 配置FMC地址线和数据线(适配800*480 LCD):
- NE4作为LCD片选(FMC_NE4)
- A18作为LCD数据/命令选择(RS)
- D0-D15:16位数据总线
- 展开
- 配置LCD背光控制引脚(如PE5)为推挽输出模式
(3) 定时器配置(LVGL刷新用)
- 展开
Timers->TIM6,启用定时器,配置:- Prescaler:179(分频后1MHz)
- Counter Mode:Up
- Period:5(定时5us,用于LVGL心跳)
- 启用定时器中断(NVIC Settings中勾选TIM6 global interrupt)
(4) 串口配置(调试用)
- 展开
Connectivity->USART1,配置为Asynchronous模式- 波特率:115200
- 数据位:8
- 停止位:1
- 校验位:None
- 启用NVIC中断(可选)
2.1.3 代码生成
- 点击
Project Manager->Code Generator - 勾选
Generate peripheral initialization as a pair of '.c/.h' files per peripheral - 点击
GENERATE CODE,生成后用Keil5打开工程
2.2 LCD驱动开发
代码文件名:lcd.h
c
#ifndef __LCD_H
#define __LCD_H
#include "stm32f4xx_hal.h"
// LCD分辨率定义
#define LCD_WIDTH 800
#define LCD_HEIGHT 480
// LCD颜色定义(RGB565)
#define LCD_COLOR_BLACK 0x0000
#define LCD_COLOR_WHITE 0xFFFF
#define LCD_COLOR_RED 0xF800
#define LCD_COLOR_GREEN 0x07E0
#define LCD_COLOR_BLUE 0x001F
#define LCD_COLOR_YELLOW 0xFFE0
#define LCD_COLOR_GRAY 0x8410
// LCD命令/数据选择
#define LCD_WR_REG(reg) do{LCD_RS_CLR();LCD_DATA(reg);LCD_WR_CLR();LCD_WR_SET();}while(0)
#define LCD_WR_DATA(data) do{LCD_RS_SET();LCD_DATA(data);LCD_WR_CLR();LCD_WR_SET();}while(0)
// 引脚操作宏
#define LCD_RS_CLR() HAL_GPIO_WritePin(GPIOD, GPIO_PIN_13, GPIO_PIN_RESET)
#define LCD_RS_SET() HAL_GPIO_WritePin(GPIOD, GPIO_PIN_13, GPIO_PIN_SET)
#define LCD_WR_CLR() HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_RESET)
#define LCD_WR_SET() HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_SET)
#define LCD_BL_CLR() HAL_GPIO_WritePin(GPIOE, GPIO_PIN_5, GPIO_PIN_RESET)
#define LCD_BL_SET() HAL_GPIO_WritePin(GPIOE, GPIO_PIN_5, GPIO_PIN_SET)
// FMC地址映射
#define LCD_BASE_ADDR ((uint32_t)(0x6C000000))
#define LCD_REG_ADDR ((volatile uint16_t *)(LCD_BASE_ADDR))
#define LCD_DATA_ADDR ((volatile uint16_t *)(LCD_BASE_ADDR + 0x00020000))
// 函数声明
void LCD_Init(void);
void LCD_Clear(uint16_t color);
void LCD_DrawPoint(uint16_t x, uint16_t y, uint16_t color);
void LCD_Fill(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color);
void LCD_SetCursor(uint16_t x, uint16_t y);
uint16_t LCD_ReadPoint(uint16_t x, uint16_t y);
#endif
代码文件名:lcd.c
c
#include "lcd.h"
#include "stm32f4xx_hal.h"
// LCD写寄存器
static void LCD_WriteReg(uint8_t reg)
{
*LCD_REG_ADDR = reg;
}
// LCD写数据
static void LCD_WriteData(uint16_t data)
{
*LCD_DATA_ADDR = data;
}
// LCD读数据
static uint16_t LCD_ReadData(void)
{
return *LCD_DATA_ADDR;
}
// LCD初始化
void LCD_Init(void)
{
// 初始化FMC(由CubeMX生成,此处仅初始化LCD控制器)
HAL_Delay(50); // 上电延时
// 发送初始化序列(适配ILI9488控制器,根据实际LCD芯片调整)
LCD_WriteReg(0x01); // 软复位
HAL_Delay(100);
LCD_WriteReg(0x11); // 退出睡眠模式
HAL_Delay(120);
// 像素格式设置(RGB565)
LCD_WriteReg(0x3A);
LCD_WriteData(0x55);
// 显示方向设置(横屏)
LCD_WriteReg(0x36);
LCD_WriteData(0x08);
// 亮度设置
LCD_WriteReg(0x51);
LCD_WriteData(0xFF);
LCD_WriteReg(0x29); // 开启显示
LCD_BL_SET(); // 打开背光
LCD_Clear(LCD_COLOR_BLACK); // 清屏为黑色
}
// LCD清屏
void LCD_Clear(uint16_t color)
{
uint32_t i;
LCD_SetCursor(0, 0);
LCD_WriteReg(0x2C); // 开始写GRAM
for(i=0; i<LCD_WIDTH*LCD_HEIGHT; i++)
{
LCD_WriteData(color);
}
}
// 设置光标位置
void LCD_SetCursor(uint16_t x, uint16_t y)
{
// 设置X坐标
LCD_WriteReg(0x2A);
LCD_WriteData(x>>8);
LCD_WriteData(x&0xFF);
LCD_WriteData((LCD_WIDTH-1)>>8);
LCD_WriteData((LCD_WIDTH-1)&0xFF);
// 设置Y坐标
LCD_WriteReg(0x2B);
LCD_WriteData(y>>8);
LCD_WriteData(y&0xFF);
LCD_WriteData((LCD_HEIGHT-1)>>8);
LCD_WriteData((LCD_HEIGHT-1)&0xFF);
}
// 画点
void LCD_DrawPoint(uint16_t x, uint16_t y, uint16_t color)
{
if(x >= LCD_WIDTH || y >= LCD_HEIGHT) return;
LCD_SetCursor(x, y);
LCD_WriteReg(0x2C);
LCD_WriteData(color);
}
// 填充矩形
void LCD_Fill(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color)
{
uint32_t i, area;
if(x1>x2 || y1>y2) return;
if(x2 >= LCD_WIDTH) x2 = LCD_WIDTH-1;
if(y2 >= LCD_HEIGHT) y2 = LCD_HEIGHT-1;
LCD_SetCursor(x1, y1);
LCD_WriteReg(0x2C);
area = (x2-x1+1)*(y2-y1+1);
for(i=0; i<area; i++)
{
LCD_WriteData(color);
}
}
// 读点颜色
uint16_t LCD_ReadPoint(uint16_t x, uint16_t y)
{
uint16_t color;
if(x >= LCD_WIDTH || y >= LCD_HEIGHT) return 0;
LCD_SetCursor(x, y);
LCD_WriteReg(0x2E); // 开始读GRAM
color = LCD_ReadData(); // 空读
color = LCD_ReadData(); // 有效数据
return color;
}
2.3 定时器中断配置(LVGL心跳)
代码文件名:tim.c(补充CubeMX生成的代码)
c
#include "tim.h"
#include "lvgl.h"
TIM_HandleTypeDef htim6;
// TIM6初始化(CubeMX生成基础代码,补充中断回调)
void MX_TIM6_Init(void)
{
htim6.Instance = TIM6;
htim6.Init.Prescaler = 179;
htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
htim6.Init.Period = 5;
htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim6) != HAL_OK)
{
Error_Handler();
}
// 启用定时器中断并启动定时器
HAL_TIM_Base_Start_IT(&htim6);
}
// TIM6中断服务函数回调
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM6)
{
lv_tick_inc(1); // LVGL心跳,每1ms增加1(实际定时5us,此处简化为1ms,可根据需要调整)
}
}
三、LVGL移植与配置
3.1 LVGL源码移植
- 将下载的LVGL源码中
lvgl文件夹复制到工程根目录 - 在Keil5中添加LVGL相关文件:
- 右键
Source Group 1->Add Existing Files to Group - 选择
lvgl/src下所有.c文件(可分批次添加) - 新增
lvgl_port分组,用于存放LVGL移植文件
- 右键
3.2 LVGL移植文件开发
代码文件名:lv_port_disp.h
c
#ifndef LV_PORT_DISP_H
#define LV_PORT_DISP_H
#include "lvgl.h"
// 函数声明
void lv_port_disp_init(void);
#endif
代码文件名:lv_port_disp.c
c
#include "lv_port_disp.h"
#include "lcd.h"
// 缓冲区定义(双缓冲,提升显示流畅度)
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf1[LCD_WIDTH * 10]; // 10行缓冲区
static lv_color_t buf2[LCD_WIDTH * 10];
// LCD刷新函数(LVGL回调)
static void disp_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
{
// 填充指定区域
LCD_Fill(area->x1, area->y1, area->x2, area->y2, *(uint16_t *)color_p);
// 通知LVGL刷新完成
lv_disp_flush_ready(disp_drv);
}
// LVGL显示端口初始化
void lv_port_disp_init(void)
{
// 1. 初始化显示缓冲区
lv_disp_draw_buf_init(&draw_buf, buf1, buf2, LCD_WIDTH * 10);
// 2. 配置显示驱动
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 = LCD_WIDTH;
disp_drv.ver_res = LCD_HEIGHT;
// 3. 注册显示驱动
lv_disp_drv_register(&disp_drv);
}
代码文件名:lv_conf.h(关键配置修改)
c
/* 复制LVGL源码中lv_conf_template.h并重命名为lv_conf.h,修改以下关键配置 */
#define LV_HOR_RES_MAX 800
#define LV_VER_RES_MAX 480
#define LV_COLOR_DEPTH 16 // 与LCD颜色格式一致
#define LV_COLOR_16_SWAP 0 // 根据LCD实际情况调整
#define LV_USE_PERF_MONITOR 1 // 启用性能监控
#define LV_TICK_CUSTOM 1 // 使用自定义心跳
#define LV_TICK_CUSTOM_INCLUDE "tim.h" // 包含定时器头文件
#define LV_MEM_SIZE (128 * 1024) // 分配128KB内存
#define LV_USE_ROLLER 1 // 仪表盘需要滚轮组件
#define LV_USE_METER 1 // 启用仪表盘组件
#define LV_USE_ARC 1 // 启用圆弧组件
3.3 LVGL初始化整合
代码文件名:main.c(核心初始化)
c
#include "main.h"
#include "lcd.h"
#include "tim.h"
#include "usart.h"
#include "lvgl.h"
#include "lv_port_disp.h"
#include "dashboard.h" // 智能仪表盘界面代码头文件
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
int main(void)
{
// 1. 系统初始化
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_TIM6_Init();
MX_FMC_Init();
// 2. LCD初始化
LCD_Init();
// 3. LVGL初始化
lv_init();
lv_port_disp_init();
// 4. 初始化智能仪表盘界面
dashboard_init();
// 5. 主循环
while (1)
{
lv_task_handler(); // LVGL任务处理
HAL_Delay(5); // 延时,降低CPU占用
dashboard_update(); // 更新仪表盘数据
}
}
// 系统时钟配置(CubeMX生成,此处省略,保持默认即可)
void SystemClock_Config(void)
{
// ... CubeMX生成的时钟配置代码 ...
}
// GPIO初始化(CubeMX生成,补充LCD背光引脚)
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOE_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
// LCD背光引脚PE5初始化
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
// LCD RS引脚PD13、WR引脚PD14初始化(FMC已配置,此处仅备份)
GPIO_InitStruct.Pin = GPIO_PIN_13 | GPIO_PIN_14;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
}
// 错误处理函数
void Error_Handler(void)
{
__disable_irq();
while (1)
{
// 错误时可点亮LED提示
}
}
四、智能仪表盘界面开发
4.1 界面整体设计流程
以下是智能仪表盘界面开发的整体流程,包含从组件创建到数据更新的全环节:
初始化LVGL显示驱动
创建仪表盘主容器
创建速度表组件
创建转速表组件
创建油量/水温组件
创建数字显示区域
配置速度表刻度/范围
配置转速表刻度/范围
配置油量/水温进度条
绑定速度数据更新函数
绑定转速数据更新函数
绑定油量/水温数据更新函数
主循环定时更新数据
LVGL刷新界面显示
4.2 仪表盘界面代码开发
代码文件名:dashboard.h
c
#ifndef __DASHBOARD_H
#define __DASHBOARD_H
#include "lvgl.h"
// 仪表盘数据结构体
typedef struct {
uint16_t speed; // 车速(0-180 km/h)
uint16_t rpm; // 发动机转速(0-8000 rpm)
uint8_t fuel; // 油量(0-100 %)
uint8_t temp; // 水温(0-100 ℃)
uint8_t gear; // 挡位(0-P,1-R,2-N,3-D,4-1,5-2,6-3,7-4,8-5)
} dashboard_data_t;
// 函数声明
void dashboard_init(void);
void dashboard_update(void);
#endif
代码文件名:dashboard.c
c
#include "dashboard.h"
#include "lvgl.h"
#include <stdio.h>
// 全局变量
static lv_obj_t *meter_speed; // 速度表
static lv_obj_t *meter_rpm; // 转速表
static lv_obj_t *bar_fuel; // 油量条
static lv_obj_t *bar_temp; // 水温条
static lv_obj_t *label_speed; // 速度数字显示
static lv_obj_t *label_rpm; // 转速数字显示
static lv_obj_t *label_gear; // 挡位显示
static dashboard_data_t dash_data; // 仪表盘数据
// 初始化速度表
static void speed_meter_init(lv_obj_t *parent)
{
// 创建速度表对象
meter_speed = lv_meter_create(parent);
lv_obj_set_size(meter_speed, 280, 280);
lv_obj_align(meter_speed, LV_ALIGN_LEFT_MID, 40, 0);
// 设置刻度
lv_meter_scale_t *scale = lv_meter_add_scale(meter_speed);
lv_meter_set_scale_angle(meter_speed, scale, 225, -45); // 刻度角度
lv_meter_set_scale_range(meter_speed, scale, 0, 180, 270, 10); // 范围0-180,10个主刻度
lv_meter_set_scale_major_ticks(meter_speed, scale, 10, 5, 10, lv_color_white(), 10); // 主刻度
lv_meter_set_scale_minor_ticks(meter_speed, scale, 2, 2, 5, lv_color_white(), 5); // 次刻度
// 添加指针
lv_meter_indicator_t *indic = lv_meter_add_needle_line(meter_speed, scale, 5, lv_color_red(), -10);
lv_meter_set_indicator_value(meter_speed, indic, 0); // 初始值0
// 添加标题
lv_obj_t *label = lv_label_create(meter_speed);
lv_label_set_text(label, "Speed (km/h)");
lv_obj_align(label, LV_ALIGN_BOTTOM_MID, 0, -10);
lv_obj_set_style_text_color(label, lv_color_white(), LV_PART_MAIN);
// 数字显示标签
label_speed = lv_label_create(parent);
lv_label_set_text(label_speed, "0");
lv_obj_align_to(label_speed, meter_speed, LV_ALIGN_BOTTOM_MID, 0, 20);
lv_obj_set_style_text_font(label_speed, &lv_font_montserrat_28, LV_PART_MAIN);
lv_obj_set_style_text_color(label_speed, lv_color_white(), LV_PART_MAIN);
}
// 初始化转速表
static void rpm_meter_init(lv_obj_t *parent)
{
// 创建转速表对象
meter_rpm = lv_meter_create(parent);
lv_obj_set_size(meter_rpm, 280, 280);
lv_obj_align(meter_rpm, LV_ALIGN_RIGHT_MID, -40, 0);
// 设置刻度
lv_meter_scale_t *scale = lv_meter_add_scale(meter_rpm);
lv_meter_set_scale_angle(meter_rpm, scale, 225, -45);
lv_meter_set_scale_range(meter_rpm, scale, 0, 8000, 270, 8); // 范围0-8000,8个主刻度
lv_meter_set_scale_major_ticks(meter_rpm, scale, 1000, 5, 10, lv_color_white(), 10);
lv_meter_set_scale_minor_ticks(meter_rpm, scale, 200, 2, 5, lv_color_white(), 5);
// 添加指针
lv_meter_indicator_t *indic = lv_meter_add_needle_line(meter_rpm, scale, 5, lv_color_yellow(), -10);
lv_meter_set_indicator_value(meter_rpm, indic, 0);
// 添加标题
lv_obj_t *label = lv_label_create(meter_rpm);
lv_label_set_text(label, "RPM (×1000)");
lv_obj_align(label, LV_ALIGN_BOTTOM_MID, 0, -10);
lv_obj_set_style_text_color(label, lv_color_white(), LV_PART_MAIN);
// 数字显示标签
label_rpm = lv_label_create(parent);
lv_label_set_text(label_rpm, "0");
lv_obj_align_to(label_rpm, meter_rpm, LV_ALIGN_BOTTOM_MID, 0, 20);
lv_obj_set_style_text_font(label_rpm, &lv_font_montserrat_28, LV_PART_MAIN);
lv_obj_set_style_text_color(label_rpm, lv_color_white(), LV_PART_MAIN);
}
// 初始化油量和水温条
static void fuel_temp_bar_init(lv_obj_t *parent)
{
// 创建油量条
bar_fuel = lv_bar_create(parent);
lv_obj_set_size(bar_fuel, 40, 150);
lv_obj_align(bar_fuel, LV_ALIGN_BOTTOM_LEFT, 80, -40);
lv_bar_set_range(bar_fuel, 0, 100);
lv_bar_set_value(bar_fuel, 80, LV_ANIM_ON); // 初始油量80%
lv_obj_set_style_bg_color(bar_fuel, lv_color_make(0, 200, 0), LV_PART_INDICATOR); // 绿色
lv_obj_set_style_bg_color(bar_fuel, lv_color_gray(), LV_PART_BG);
// 油量标题
lv_obj_t *label_fuel = lv_label_create(parent);
lv_label_set_text(label_fuel, "Fuel (%)");
lv_obj_align_to(label_fuel, bar_fuel, LV_ALIGN_TOP_MID, 0, -10);
lv_obj_set_style_text_color(label_fuel, lv_color_white(), LV_PART_MAIN);
// 创建水温条
bar_temp = lv_bar_create(parent);
lv_obj_set_size(bar_temp, 40, 150);
lv_obj_align(bar_temp, LV_ALIGN_BOTTOM_RIGHT, -80, -40);
lv_bar_set_range(bar_temp, 0, 100);
lv_bar_set_value(bar_temp, 90, LV_ANIM_ON); // 初始水温90℃
lv_obj_set_style_bg_color(bar_temp, lv_color_make(255, 100, 0), LV_PART_INDICATOR); // 橙色
lv_obj_set_style_bg_color(bar_temp, lv_color_gray(), LV_PART_BG);
// 水温标题
lv_obj_t *label_temp = lv_label_create(parent);
lv_label_set_text(label_temp, "Temp (℃)");
lv_obj_align_to(label_temp, bar_temp, LV_ALIGN_TOP_MID, 0, -10);
lv_obj_set_style_text_color(label_temp, lv_color_white(), LV_PART_MAIN);
// 挡位显示
label_gear = lv_label_create(parent);
lv_label_set_text(label_gear, "D");
lv_obj_align(label_gear, LV_ALIGN_BOTTOM_MID, 0, -40);
lv_obj_set_style_text_font(label_gear, &lv_font_montserrat_48, LV_PART_MAIN);
lv_obj_set_style_text_color(label_gear, lv_color_white(), LV_PART_MAIN);
}
// 仪表盘初始化
void dashboard_init(void)
{
// 创建主容器
lv_obj_t *screen = lv_scr_act();
lv_obj_set_style_bg_color(screen, lv_color_black(), LV_PART_MAIN); // 黑色背景
// 初始化各组件
speed_meter_init(screen);
rpm_meter_init(screen);
fuel_temp_bar_init(screen);
// 初始化数据
dash_data.speed = 0;
dash_data.rpm = 0;
dash_data.fuel = 80;
dash_data.temp = 90;
dash_data.gear = 3; // D挡
}
// 模拟数据更新(实际项目中替换为传感器数据)
static void update_demo_data(void)
{
// 模拟车速递增
dash_data.speed += 1;
if(dash_data.speed > 180) dash_data.speed = 0;
// 模拟转速随车速变化
dash_data.rpm = dash_data.speed * 40 + 500;
if(dash_data.rpm > 8000) dash_data.rpm = 8000;
// 模拟油量递减
dash_data.fuel -= 0.1;
if(dash_data.fuel < 0) dash_data.fuel = 100;
// 模拟水温小幅波动
static int8_t temp_dir = 1;
dash_data.temp += temp_dir;
if(dash_data.temp > 95) temp_dir = -1;
if(dash_data.temp < 85) temp_dir = 1;
// 模拟挡位变化
static uint8_t gear_cnt = 0;
gear_cnt++;
if(gear_cnt > 50)
{
gear_cnt = 0;
dash_data.gear++;
if(dash_data.gear > 8) dash_data.gear = 0;
}
}
// 仪表盘数据更新
void dashboard_update(void)
{
// 更新模拟数据
update_demo_data();
// 更新速度表
lv_meter_set_indicator_value(meter_speed, lv_meter_get_indicator(meter_speed, 0), dash_data.speed);
char buf[16];
sprintf(buf, "%d", dash_data.speed);
lv_label_set_text(label_speed, buf);
// 更新转速表
lv_meter_set_indicator_value(meter_rpm, lv_meter_get_indicator(meter_rpm, 0), dash_data.rpm);
sprintf(buf, "%d", dash_data.rpm/1000);
lv_label_set_text(label_rpm, buf);
// 更新油量条
lv_bar_set_value(bar_fuel, dash_data.fuel, LV_ANIM_ON);
// 更新水温条
lv_bar_set_value(bar_temp, dash_data.temp, LV_ANIM_ON);
// 更新挡位显示
const char *gear_text[] = {"P", "R", "N", "D", "1", "2", "3", "4", "5"};
lv_label_set_text(label_gear, gear_text[dash_data.gear]);
}
五、代码编译与下载
5.1 Keil5工程配置
- 打开工程,点击
Options for Target(魔法棒图标) Target选项卡:- Stack Size:0x200(512字节)
- Heap Size:0x1000(4KB)
- 优化等级:
O0(调试)/O2(发布)
Output选项卡:勾选Create HEX FileDebug选项卡:选择ST-Link Debugger,点击Settings配置调试器Editor选项卡:添加头文件路径(lvgl、lvgl_port、驱动文件夹)
5.2 编译代码
点击Build(构建)或Rebuild(重建),确保无错误和警告。若出现内存不足问题:
- 调整
lv_conf.h中LV_MEM_SIZE - 减少LVGL缓冲区大小(
lv_port_disp.c中buf1/buf2)
5.3 下载代码
- 连接ST-Link到开发板,开发板上电
- 点击
Download(下载),等待下载完成 - 复位开发板,即可看到智能仪表盘界面运行
六、调试与优化
6.1 常见问题解决
- 屏幕无显示:
- 检查LCD背光引脚是否正确配置
- 确认FMC地址映射和LCD初始化序列
- 验证LVGL初始化是否在LCD初始化之后
- 界面卡顿:
- 增大LVGL缓冲区(如改为20行)
- 优化定时器中断频率
- 关闭不必要的LVGL功能(
lv_conf.h)
- 指针不转动:
- 检查
lv_meter_set_indicator_value参数是否正确 - 确认
dashboard_update函数是否在主循环中调用
- 检查
6.2 性能优化
- 启用LVGL双缓冲(已在代码中配置)
- 减少界面刷新频率(主循环延时调整为5-10ms)
- 使用DMA传输LCD数据(进阶优化,可替换
LCD_Fill函数)
总结
- 本教程完成了STM32F429+LVGL智能仪表盘的全流程开发,从底层驱动(LCD、定时器)到LVGL移植,再到界面组件开发,代码可直接落地使用。
- 核心关键是LVGL的正确移植(显示缓冲区配置、刷新回调函数)和仪表盘组件(meter、bar、label)的灵活使用,需注意数据更新与界面刷新的同步。
- 教程中使用模拟数据,实际项目中只需替换
update_demo_data函数为传感器数据读取逻辑,即可适配真实的车载场景。