NUCLEO-H5E5ZJ 移植 LVGL(ST7735 LCD + 五向摇杆)
0. 效果 LCD 显示 LVGL 菜单界面:标题 Joystick Menu + 4 个菜单按钮(LED1 GREEN / LED2 RED / LED3 BLUE / About)+ 底部状态行。LCD 扩展板上的五向摇杆导航:上下/左右移动焦点(黄色高亮框),按下确认——前 3 项直接翻转板载 LD1/LD2/LD3,状态行同步显示 LDx ON/OFF。已在实物上验证通过。 1. 系统架构五向摇杆(电阻分压) ST7735 LCD│ ▲% {& s' b# H W) W' d1 F9 v3 F ▼ 模拟电压 │ SPI4 @10MHz1 u0 }' p& c( k i3 l2 g ADC1_INP9 (PB0, Arduino A3) │ │ │" f& Q: g \( z6 g ▼ │ Zephyr adc-keys 输入驱动 │ ┌─────────────────────┐ |) c- K# ?2 J1 X: B* ^ (50ms 轮询 + 最近阈值匹配 + 消抖) │ │ lcd_bare 裸 SPI 驱动 │ │ INPUT_KEY_* 事件 ├──▶│ display_bare shim │ ▼ │ │ (Zephyr display API) │; W. J+ f3 @0 v1 m8 w) W2 _ zephyr,lvgl-keypad-input │ └─────────────────────┘ (input-codes → lvgl-codes 映射) │ ▲ │ LV_KEY_* │ display_write(RGB565) ▼ │ │ LVGL keypad indev → lv_group 焦点组 ──────┴─── LVGL 渲染(lv_timer_handler) LVGL 显示与输入在 Zephyr 中全部通过设备树声明对接,应用代码零胶水。 2. 硬件连接(LCD 扩展板插 Arduino 排针)功能Arduino 位H5E5ZJ 引脚外设SPI_SCKD13PE12SPI4_SCK, J9 A: \7 _" J, l/ Z SPI_MISOD12PE13SPI4_MISO SPI_MOSID11PE14SPI4_MOSI; Z+ ~/ f8 ?% l" g$ s LCD CSD10PE11GPIO(软件控制)' w, r% C. U' i: h LCD DCD8PA9GPIO 五向摇杆A3PB0ADC1_INP9 引脚依据为 MB2129 原理图第 5 页连接器部分:
电阻分压式:3.3 V 经上拉,拨向某方向把对应电阻短到 GND,输出不同分压。ADC(12 位,3.3 V 基准)实测各方向电压: 方向ADC raw(约)电压(约)press-thresholds-mv右8871 mV71 上738595 mV595 确认(按下)13101055 mV1055 左20481650 mV1650& D: U0 J+ U$ @* s8 b. v3 n6 L 下33762720 mV2720. v9 c4 @: c+ Q6 ?' v) D4 p 松开(开路)40953298 mVkeyup = 33003. 逐步实现第 1 步:应用骨架apps/lvgl_demo/ ├── CMakeLists.txt ├── prj.conf% ~, G5 u- P/ v& J, |/ \ ├── boards/nucleo_h5e5zj.overlay ├── dts/bindings/display/ │ ├── uniknect,lcd-bare.yaml # display shim 绑定 │ └── uniknect,lcd-io.yaml # LCD DC/CS GPIO 绑定, h: Y) W1 G8 \) Q0 C └── src/* M* G! U2 X+ J" e ├── main.c # LVGL 菜单 UI + 摇杆焦点组. K4 R2 v- K: s/ L6 I8 Q, Y8 V9 Z: \ ├── lcd_bare.c / lcd_bare.h # ST7735 裸 SPI 驱动(板级无关)' l* V- ~& M. j ├── display_bare.c # Zephyr display API shim(LVGL 的显示后端) └── font_petme128_8x8.h第 2 步:overlay —— 摇杆接入 Zephyr 输入子系统 Zephyr 自带 adc-keys 输入驱动(drivers/input/input_adc_keys.c),专为电阻分压式按键设计:按 sample-period-ms 轮询 ADC,找电压最接近的阈值判定按键,内置两周期消抖,无需自写驱动: #include <zephyr/dt-bindings/input/input-event-codes.h>#include <zephyr/dt-bindings/lvgl/lvgl.h>& x( s8 p! E( {& ^; L1 l / { joystick: joystick {/ k% o7 x }8 M& j$ ?( N+ G2 | compatible = "adc-keys"; io-channels = <&adc1 9>;( @ J7 {5 X" s sample-period-ms = <50>; keyup-threshold-mv = <3300>; /* 松开=开路电压 */ key_right { press-thresholds-mv = <71>; zephyr,code = <INPUT_KEY_RIGHT>; }; key_up { press-thresholds-mv = <595>; zephyr,code = <INPUT_KEY_UP>; }; key_center { press-thresholds-mv = <1055>; zephyr,code = <INPUT_KEY_ENTER>; };5 k( |: B4 t- u! v/ d8 Y8 e key_left { press-thresholds-mv = <1650>; zephyr,code = <INPUT_KEY_LEFT>; };1 B/ C7 Y+ K# h) R9 N9 h" ` key_down { press-thresholds-mv = <2720>; zephyr,code = <INPUT_KEY_DOWN>; };' q0 x( A9 [7 G! }. l2 q3 d& Q }; }; ADC 通道配置(v4.4 的 H5E5 设备树中 adc1 是独立节点,无 adc12_common 包装层,adc_ker 时钟和分频直接写在 &adc1 上): &adc1 {clocks = <&rcc STM32_CLOCK(AHB2, 10)>,4 j% X8 @1 `3 K% U+ T: v <&rcc STM32_SRC_HCLK ADCDAC_SEL(0)>; clock-names = "adcx", "adc_ker"; pinctrl-0 = <&adc1_inp9_pb0>;2 x" @7 ?6 h" [. h9 G% z l pinctrl-names = "default"; st,adc-clock-source = "ASYNC"; st,adc-prescaler = <6>; status = "okay";3 [3 ?( o$ g2 P% k" l #address-cells = <1>;, G7 p8 L* n# y1 B #size-cells = <0>; # f$ Y4 g, x: F4 l. D8 S2 n channel@9 {& f4 Y B" x1 G# g$ Q/ m+ ~ reg = <9>;1 W3 M0 z1 y, f% H- h9 l8 Z/ r zephyr,gain = "ADC_GAIN_1";. {7 ~$ ?" Z) t& f% L3 @2 M6 P. ` zephyr,reference = "ADC_REF_INTERNAL";; @7 E3 O* z4 N/ t zephyr,vref-mv = <3300>; /* adc-keys 按 mV 判定,必须给基准 */ zephyr,acquisition-time = <ADC_ACQ_TIME_MAX>;& j8 ^. @3 [! Q, x0 c( F1 w zephyr,resolution = <12>;: h0 }) {7 \- d$ @1 l" U };& V+ S. v" R6 r6 M f: d };第 3 步:overlay —— 输入事件映射给 LVGL(关键踩坑点) Zephyr 的 LVGL 模块提供 zephyr,lvgl-keypad-input 伪设备:监听指定输入设备的 INPUT_KEY_* 事件,按 input-codes→lvgl-codes 表翻译成 LV_KEY_* 注入 LVGL keypad 输入设备: / {7 E& b4 w0 [( l; Blvgl_keypad { compatible = "zephyr,lvgl-keypad-input"; input = <&joystick>; input-codes = <INPUT_KEY_UP INPUT_KEY_DOWN INPUT_KEY_LEFT INPUT_KEY_RIGHT4 T$ x4 u5 y& f9 b INPUT_KEY_ENTER>;1 ?# y$ d! c) a. T; S6 i" n' Q" O lvgl-codes = <LV_KEY_PREV LV_KEY_NEXT LV_KEY_PREV LV_KEY_NEXT LV_KEY_ENTER>;, H4 W: x& T q/ H& d$ p };: k8 ]) `: _& U; u };
LCD 部分(SPI4 + lcd_bare + lcd_io 节点)与纯 LCD 工程相同,不重复展开。 第 4 步:prj.confCONFIG_GPIO=yCONFIG_SPI=y; h5 k% A( r' `$ F) t, q CONFIG_DISPLAY=y6 K9 M: p( H1 j0 T. h; [ CONFIG_ADC=y" J. ^+ E" K' ]- h8 t% X9 p CONFIG_INPUT=y # adc-keys / lvgl-keypad-input 均 default y(节点使能即生效). h! g. N) V( R* Y CONFIG_MAIN_STACK_SIZE=4096/ U3 p4 L3 L8 M" A* z$ \9 q* E / v: V8 d, C9 J$ R7 b CONFIG_LVGL=y9 v% Z% F* z8 f2 x6 \ CONFIG_LV_Z_MEM_POOL_SIZE=16384 CONFIG_LV_COLOR_DEPTH_16=y9 w) [: {% S g/ v CONFIG_LV_COLOR_16_SWAP=y # 与 display_bare shim 约定:LVGL 缓冲即大端 RGB565,直透传% T9 G6 m% @* n3 _7 { CONFIG_LV_USE_BUTTON=y2 `+ W5 a7 U7 K% x7 F. J, P CONFIG_LV_USE_LABEL=y CONFIG_LV_COLOR_16_SWAP=y 必须与 display shim 的字节序约定一致(shim 不做逐像素交换,直接透传 SPI)。 第 5 步:应用代码要点(src/main.c)/* 1. 找到 LVGL keypad 输入设备并绑定焦点组 */lv_indev_t *indev = NULL; while ((indev = lv_indev_get_next(indev)) != NULL) {, u! b2 d3 S8 z4 z if (lv_indev_get_type(indev) == LV_INDEV_TYPE_KEYPAD) { keypad = indev; }; s, B1 \9 n) O3 [4 e; c6 C1 x# s }/ B8 \( D3 a2 y1 R+ b* U; f, k lv_group_t *group = lv_group_create();$ J; @( a( g( X, M/ C2 N& @$ r /* lv_group_add_obj(group, btn); 每个菜单项都加入 */ lv_indev_set_group(keypad, group); A# d8 x8 U S- {3 o0 x 3 D4 H1 S8 W: c2 W( N- s3 t; B /* 2. 焦点可见性:给菜单按钮加 FOCUS_KEY/FOCUSED 两个状态的样式 */5 B- ` Y, W p5 b6 c lv_obj_add_style(btn, &style_focus, LV_STATE_FOCUS_KEY); J+ M5 w$ [) k" D lv_obj_add_style(btn, &style_focus, LV_STATE_FOCUSED); " z: r/ V* @; M$ ^' V* c( B4 i /* 3. 主循环 */ display_blanking_off(display_dev); while (1) {1 }1 l/ R/ G- w1 Y% b uint32_t sleep_ms = lv_timer_handler(); k_msleep(MIN(sleep_ms, 20)); } 输入事件在 lv_timer_handler() 内部从消息队列取出处理,主循环单线程即可,无需额外同步。 第 6 步:编译、烧录、验证cd /d/luglZephyrprojectexport ZEPHYR_SDK_INSTALL_DIR=/d/luglZephyrproject/zephyr-sdk-1.0.1/zephyr-sdk-1.0.1! {0 C6 X, ^. ~( f7 W) R" ~9 p! {; y N, m3 {( Y' ^0 _) i" n8 ]- k, ` west build -b nucleo_h5e5zj /d/STM32/STM32H5E5/NUCLEO_H5E5ZJ/apps/lvgl_demo \" ` J. m6 ?6 s9 @5 M; X -d build_h5e5_lvgl -p always -- -DBOARD_ROOT=D:/STM32/STM32H5E5/NUCLEO_H5E5ZJ8 j- |, W3 w8 P/ Y' M west flash -d build_h5e5_lvgl 实测结果(2026-10-16):
4. 经验总结
|
【STM32H5 评测】基础硬件加密与随机数性能 - STM32H5E5实测硬件AES加解密、HASH运算、TRNG真随机数输出速率与稳定性
【STM32H5 评测】内存读写性能 - STM32H5E5实测Flash,SRAM读写速度,用DMA搬运大块数据
【STM32H5 评测】CPU核心性能 - STM32H5E5跑 RTOS,观察任务切换速度、中断响应快慢
【STM32H5 评测】3、STM32H5 Lwip 移植、官方例程和UDP发送数据
【STM32H5 评测】基于NUCLEO_H5E5ZJ+Zephyr数字电源
【STM32H5 评测】2、Keil中添加RTX5 RTOS和Event Record
STM32C542RC开发板PWM呼吸灯及多功能按键实验
NUCLEO-H5E5ZJ 移植 ST7735 SPI LCD 驱动
NUCLEO-H5E5ZJ 以太网(Ethernet)移植记录
【STM32H5 评测】1、移植记录(LED / Shell)
微信公众号
手机版