你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

NUCLEO-H5E5ZJ 移植 LVGL

[复制链接]
lugl 发布时间:2026-10-16 22:34
NUCLEO-H5E5ZJ 移植 LVGL(ST7735 LCD + 五向摇杆)
日期:2026-10-16Zephyr 版本:v4.4.0(LVGL v9)目标板:NUCLEO-H5E5ZJ(STM32H5E5ZJT6)移植产物:D:\STM32\STM32H5E5\NUCLEO_H5E5ZJ\apps\lvgl_demo

3 ^& W# {% P% W; G* v4 Z( W
0. 效果
LCD 显示 LVGL 菜单界面:标题 Joystick Menu + 4 个菜单按钮(LED1 GREEN / LED2 RED / LED3 BLUE / About)+ 底部状态行。LCD 扩展板上的五向摇杆导航:上下/左右移动焦点(黄色高亮框),按下确认——前 3 项直接翻转板载 LD1/LD2/LD3,状态行同步显示 LDx ON/OFF。已在实物上验证通过。
1. 系统架构五向摇杆(电阻分压)                    ST7735 LCD+ i! t. Y, s, j& p" x
       │                                  ▲2 N' `4 V$ g! j
       ▼ 模拟电压                          │ SPI4 @10MHz& p# K2 f$ C' `/ q" i
ADC1_INP9 (PB0, Arduino A3)               │: y& B) H: X+ P: i# r3 K
       │                                  │! J3 @  L# Y4 [  Y
       ▼                                  │
3 U. s; I+ I. rZephyr adc-keys 输入驱动                   │   ┌─────────────────────┐
* u: x( k) h! n& ]4 P(50ms 轮询 + 最近阈值匹配 + 消抖)            │   │ lcd_bare 裸 SPI 驱动  │
$ B% m  b. T! B) H& l+ h" S! `' g3 n       │ INPUT_KEY_* 事件                  ├──▶│ display_bare shim    │
* ^" G9 ^  N) b+ W% \- C       ▼                                  │   │ (Zephyr display API) │
# R& P" g& e' o+ T2 p7 e; D& [" jzephyr,lvgl-keypad-input                  │   └─────────────────────┘$ L0 k2 E8 r0 U7 N" J
(input-codes → lvgl-codes 映射)            │            ▲" y% b  P) c  D: q, \
       │ LV_KEY_*                         │ display_write(RGB565)$ x) v4 k5 \/ A5 @5 m
       ▼                                  │            │
+ x8 n1 e: |& ]4 e* z- MLVGL keypad indev → lv_group 焦点组 ──────┴─── LVGL 渲染(lv_timer_handler)
LVGL 显示与输入在 Zephyr 中全部通过设备树声明对接,应用代码零胶水。
2. 硬件连接(LCD 扩展板插 Arduino 排针)功能Arduino 位H5E5ZJ 引脚外设
6 c3 U2 l, [0 _& z3 n. pSPI_SCKD13PE12SPI4_SCK9 X; J- f( l  _) V" U- i/ r
SPI_MISOD12PE13SPI4_MISO
" t6 H" o# v* s% {SPI_MOSID11PE14SPI4_MOSI0 D; K. s8 ?) E! u2 ], @; G" \4 ^; P
LCD CSD10PE11GPIO(软件控制)
: `6 m. d1 K7 h; U: KLCD DCD8PA9GPIO
+ r6 b# l' S: W+ m- f( u8 S五向摇杆A3PB0ADC1_INP9
引脚依据为 MB2129 原理图第 5 页连接器部分:
mb2129_arduino_cn5_cn9.png
摇杆原理
电阻分压式:3.3 V 经上拉,拨向某方向把对应电阻短到 GND,输出不同分压。ADC(12 位,3.3 V 基准)实测各方向电压:
方向ADC raw(约)电压(约)press-thresholds-mv. W4 f- V) P7 y  p
右8871 mV71
1 n0 J% e- e9 ]" p上738595 mV595: d- R. [! l2 V+ G1 w
确认(按下)13101055 mV1055# J: X/ _4 q; R$ K8 m+ u
左20481650 mV16509 I4 i+ {- Q! w
下33762720 mV2720
  i7 w+ D! s5 D+ Z+ F+ U4 c% H" s松开(开路)40953298 mVkeyup = 33003. 逐步实现第 1 步:应用骨架apps/lvgl_demo/
* R% l+ R* g" ^( \├── CMakeLists.txt; w6 U9 L7 {( z, g- Z
├── prj.conf
+ y# K" s! l7 l: E3 K├── boards/nucleo_h5e5zj.overlay$ Y; ]* n$ ]# _
├── dts/bindings/display/
( v5 H; C$ j5 d8 s' Z5 U│   ├── uniknect,lcd-bare.yaml   # display shim 绑定
+ ?4 q6 s7 F. m2 c│   └── uniknect,lcd-io.yaml     # LCD DC/CS GPIO 绑定
7 \' l4 P/ ^, e5 U4 `└── src/' q' f2 O) A: m! l! b
    ├── main.c                   # LVGL 菜单 UI + 摇杆焦点组7 l" U4 N3 r7 m# p: I% ^+ V' T; K
    ├── lcd_bare.c / lcd_bare.h  # ST7735 裸 SPI 驱动(板级无关)
  X" e8 h0 R! c' L5 J  y    ├── display_bare.c           # Zephyr display API shim(LVGL 的显示后端)
5 a7 K# _2 ?& }) y% I% a    └── 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>
: w5 }7 h- U* V, _#include <zephyr/dt-bindings/lvgl/lvgl.h>8 G4 y( S' U! H: ]% [
​3 k# F( T/ b% d1 _
/ {
% V! C3 g+ I- N" B  _    joystick: joystick {
7 i- v6 D2 U3 H        compatible = "adc-keys";0 H% j! b& o+ j- ^  O5 y* A& Q
        io-channels = <&adc1 9>;+ J" _4 D* j( P) u
        sample-period-ms = <50>;
$ {! d) K% d! w        keyup-threshold-mv = <3300>;          /* 松开=开路电压 */
( ?9 D* v4 T* ]  |" p​6 q$ G" g1 A* v% r* i" j! h8 s
        key_right  { press-thresholds-mv = <71>;   zephyr,code = <INPUT_KEY_RIGHT>; };
% t" P6 d7 a" r: k, z& q/ `# B! _. d2 x        key_up     { press-thresholds-mv = <595>;  zephyr,code = <INPUT_KEY_UP>; };3 A: K+ g1 I/ A+ _
        key_center { press-thresholds-mv = <1055>; zephyr,code = <INPUT_KEY_ENTER>; };
6 m6 s4 F& w4 Z& F        key_left   { press-thresholds-mv = <1650>; zephyr,code = <INPUT_KEY_LEFT>; };
# i1 h  V( R: o9 t0 A, c        key_down   { press-thresholds-mv = <2720>; zephyr,code = <INPUT_KEY_DOWN>; };
; I8 v; L4 U' l$ X    };0 }7 F7 K" X6 z% X. x& F8 x, e6 K" y
};
ADC 通道配置(v4.4 的 H5E5 设备树中 adc1 是独立节点,无 adc12_common 包装层,adc_ker 时钟和分频直接写在 &adc1 上):
&adc1 {; b  }2 G; f# C
    clocks = <&rcc STM32_CLOCK(AHB2, 10)>,  F5 W7 \8 Y) n" A4 ~4 C. D
         <&rcc STM32_SRC_HCLK ADCDAC_SEL(0)>;
0 @/ w. R% R: z! D6 i    clock-names = "adcx", "adc_ker";
# W) Q4 |9 c: X$ n    pinctrl-0 = <&adc1_inp9_pb0>;
; t" K. h2 e4 D8 d( w    pinctrl-names = "default";
  Q3 ~* T% D5 @9 I( i9 M2 L    st,adc-clock-source = "ASYNC";3 h8 a5 u& J: y
    st,adc-prescaler = <6>;
' C9 p  s! M" `# o    status = "okay";
1 R# f. V  n/ r( T% M1 w    #address-cells = <1>;8 m4 }- K0 `% P+ ]! J6 f6 j
    #size-cells = <0>;
" x* `0 z2 G$ \$ u8 b5 q​' R# q" z( a0 H6 Q
    channel@9 {
& `) x* {& f* i+ i9 @        reg = <9>;
! M% K- l' }) {- I0 d1 D" x        zephyr,gain = "ADC_GAIN_1";6 g/ |' r, z  _1 }; a
        zephyr,reference = "ADC_REF_INTERNAL";
! g) S+ x' T8 F3 a2 J4 M& e        zephyr,vref-mv = <3300>;          /* adc-keys 按 mV 判定,必须给基准 */
9 y3 m- e' |- h        zephyr,acquisition-time = <ADC_ACQ_TIME_MAX>;3 `( n! K% ]3 J. g( l# p" \
        zephyr,resolution = <12>;
: R% Q! }, ^* p7 o) n8 }    };0 C. d; U  }. c, ]3 }- d
};第 3 步:overlay —— 输入事件映射给 LVGL(关键踩坑点)
Zephyr 的 LVGL 模块提供 zephyr,lvgl-keypad-input 伪设备:监听指定输入设备的 INPUT_KEY_* 事件,按 input-codes→lvgl-codes 表翻译成 LV_KEY_* 注入 LVGL keypad 输入设备:
/ {
) b+ `, j0 ~+ K6 ?  Q  m6 X    lvgl_keypad {( H6 r2 @% i' N- O
        compatible = "zephyr,lvgl-keypad-input";
  N" u* k4 P, X/ \5 ^; J        input = <&joystick>;
+ s* f# x* L  t$ y* y        input-codes = <INPUT_KEY_UP INPUT_KEY_DOWN INPUT_KEY_LEFT INPUT_KEY_RIGHT; g( \# C" n* |# h3 a
                   INPUT_KEY_ENTER>;: {; ^6 d- K+ V7 ~) C# s
        lvgl-codes = <LV_KEY_PREV LV_KEY_NEXT LV_KEY_PREV LV_KEY_NEXT
6 i9 @6 y# B: l; T- T- \; c                  LV_KEY_ENTER>;/ `$ z; C; X* s; I$ I2 a
    };
; k8 f0 g- o( u+ l};
踩坑记录:最初把方向映射成 LV_KEY_UP/DOWN/LEFT/RIGHT,结果确认键正常、方向完全不动。原因是 LVGL 中 LV_KEY_PREV/NEXT 才负责移动焦点组焦点,方向键是派发给当前焦点控件自己处理的(按钮不处理方向)。用 INPUT_CALLBACK_DEFINE(NULL, cb, NULL) 打印全部输入事件确认事件码正确(上=103/下=108/左=105/右=106)后,定位为 LVGL 侧映射问题,改映射为 PREV/NEXT 即恢复。菜单类导航的正确映射:上/左→LV_KEY_PREV,下/右→LV_KEY_NEXT,确认→LV_KEY_ENTER。
LCD 部分(SPI4 + lcd_bare + lcd_io 节点)与纯 LCD 工程相同,不重复展开。
第 4 步:prj.confCONFIG_GPIO=y
! x% L! w# O$ ]8 _; D/ p" hCONFIG_SPI=y
2 t% ]8 W8 M2 f  u: nCONFIG_DISPLAY=y
: h& G3 B- G) d: ZCONFIG_ADC=y! ^" B  t6 w( ?$ g+ O6 J
CONFIG_INPUT=y          # adc-keys / lvgl-keypad-input 均 default y(节点使能即生效)
+ F* Z/ z! ?( h( V, f  J​
) P7 L0 V9 C  v0 L  S3 J, ~" V% `CONFIG_MAIN_STACK_SIZE=4096
  I6 C2 W0 J8 ?+ H( O+ N​1 P* W% p; Q6 a0 M
CONFIG_LVGL=y
% j2 B1 X5 ]# ~) u" \CONFIG_LV_Z_MEM_POOL_SIZE=16384
) A$ Z- t) {  J/ |CONFIG_LV_COLOR_DEPTH_16=y5 |* G2 Z! q% T6 I, ]$ k& |
CONFIG_LV_COLOR_16_SWAP=y    # 与 display_bare shim 约定:LVGL 缓冲即大端 RGB565,直透传
2 `0 z3 O1 [/ g+ w" JCONFIG_LV_USE_BUTTON=y
4 x: P; S2 q+ `# ~, o& e6 KCONFIG_LV_USE_LABEL=y
CONFIG_LV_COLOR_16_SWAP=y 必须与 display shim 的字节序约定一致(shim 不做逐像素交换,直接透传 SPI)。
第 5 步:应用代码要点(src/main.c)/* 1. 找到 LVGL keypad 输入设备并绑定焦点组 */
+ n/ k0 J/ O3 Slv_indev_t *indev = NULL;3 v* N& `/ j& b5 d
while ((indev = lv_indev_get_next(indev)) != NULL) {# M5 ]2 `4 R/ g( O' C( o* q
    if (lv_indev_get_type(indev) == LV_INDEV_TYPE_KEYPAD) {% w7 ]. v& `" ]" j
        keypad = indev;. L, Y/ [. |3 z  L# k6 I- A
    }) m: \: B! u/ O5 z: d
}* F3 i* ~* f9 K' i( x7 I, j
lv_group_t *group = lv_group_create();, b5 r- O5 q, O
/* lv_group_add_obj(group, btn); 每个菜单项都加入 */
/ P& E+ C, X' R1 f* {. Ilv_indev_set_group(keypad, group);& S4 a+ j+ ]  K
​0 R; z' s9 k7 D% k3 k4 r) z
/* 2. 焦点可见性:给菜单按钮加 FOCUS_KEY/FOCUSED 两个状态的样式 */
. q! K3 C& G& V/ q& ~. @& qlv_obj_add_style(btn, &style_focus, LV_STATE_FOCUS_KEY);1 b( k0 t5 t7 c2 M4 R  k, o: r
lv_obj_add_style(btn, &style_focus, LV_STATE_FOCUSED);3 K$ h8 ^+ x3 q9 a0 q3 L
​$ X* p: O- ^% L' E
/* 3. 主循环 */
/ T- c# `! M. x. @8 V% Tdisplay_blanking_off(display_dev);' X! ]& Q& z& p% D
while (1) {5 C2 ~0 b8 k4 I1 F% G
    uint32_t sleep_ms = lv_timer_handler();. g  z# K4 y, J0 {
    k_msleep(MIN(sleep_ms, 20));
/ R0 S. @3 q: ?2 D3 w2 x}
输入事件在 lv_timer_handler() 内部从消息队列取出处理,主循环单线程即可,无需额外同步。
第 6 步:编译、烧录、验证cd /d/luglZephyrproject
, Z+ @: ~$ n$ _2 \( j0 N8 nexport ZEPHYR_SDK_INSTALL_DIR=/d/luglZephyrproject/zephyr-sdk-1.0.1/zephyr-sdk-1.0.18 u' |8 F. s& F
​$ s( I# p- N# ^& e' A, H! {; y" \  s& P
west build -b nucleo_h5e5zj /d/STM32/STM32H5E5/NUCLEO_H5E5ZJ/apps/lvgl_demo \
9 K6 r6 O. j( {; i' G6 A% c% }  -d build_h5e5_lvgl -p always -- -DBOARD_ROOT=D:/STM32/STM32H5E5/NUCLEO_H5E5ZJ( O- _4 H4 ^2 {- {% l" j
west flash -d build_h5e5_lvgl
实测结果(2026-10-16):
  • 菜单正常渲染,字体/颜色正确(RGB565 字节序约定有效);
  • 摇杆上/下/左/右:焦点框在 4 个菜单项间循环移动;
  • 摇杆按下:触发当前项,板载 LD1/LD2/LD3 随之翻转,状态行显示 LDx ON/OFF。

    + Z1 ~/ a. A" x( t0 |1 c
nucleo_h5e5zj_lvgl.png 6 C3 U8 r& E" t, d
4. 经验总结
  • 电阻分压式摇杆不要自己写驱动:Zephyr 自带 adc-keys 输入驱动(最近阈值匹配 + 消抖),dts 里填电压阈值即可;通道记得配 zephyr,vref-mv,驱动按 mV 判定;
  • LVGL 菜单导航映射 LV_KEY_PREV/NEXT,不是方向键——方向键发给焦点控件自用,这是本次唯一的逻辑踩坑点;
  • 输入链路可分段验证:INPUT_CALLBACK_DEFINE(NULL, cb, NULL) 全局打印输入事件,先确认事件码(上=103/下=108/左=105/右=106/确认=28)再查 LVGL 侧;
  • 显示与输入的对接全部在设备树声明(zephyr,display + zephyr,lvgl-keypad-input),应用代码只负责建 UI 和焦点组;
  • 自定义 dts 节点的 GPIO 属性必须写绑定(type: phandle-array),同 LCD 工程的经验。

    8 y/ A6 s0 P* Y- f6 m
lvgl_系统架构图.png
赞 收藏 评论0 发布时间:2026-10-16 22:34

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版