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

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


7 [7 {1 x1 [* F  G& j0. 效果
LCD 显示 LVGL 菜单界面:标题 Joystick Menu + 4 个菜单按钮(LED1 GREEN / LED2 RED / LED3 BLUE / About)+ 底部状态行。LCD 扩展板上的五向摇杆导航:上下/左右移动焦点(黄色高亮框),按下确认——前 3 项直接翻转板载 LD1/LD2/LD3,状态行同步显示 LDx ON/OFF。已在实物上验证通过。
1. 系统架构五向摇杆(电阻分压)                    ST7735 LCD
/ \0 J) {4 v6 l, b6 p! E) R$ k0 x* T) N       │                                  ▲% {& s' b# H  W) W' d1 F9 v3 F
       ▼ 模拟电压                          │ SPI4 @10MHz1 u0 }' p& c( k  i3 l2 g
ADC1_INP9 (PB0, Arduino A3)               │
6 W; |, u" `9 p       │                                  │" f& Q: g  \( z6 g
       ▼                                  │
6 C8 b  N5 E& H0 E4 D3 Y9 ^Zephyr adc-keys 输入驱动                   │   ┌─────────────────────┐  |) c- K# ?2 J1 X: B* ^
(50ms 轮询 + 最近阈值匹配 + 消抖)            │   │ lcd_bare 裸 SPI 驱动  │
+ g: k- b; d5 ~3 f3 B# p  `  r       │ INPUT_KEY_* 事件                  ├──▶│ display_bare shim    │
" M. A& O- ]9 h( w) ?( W       ▼                                  │   │ (Zephyr display API) │; W. J+ f3 @0 v1 m8 w) W2 _
zephyr,lvgl-keypad-input                  │   └─────────────────────┘
, }7 m8 f2 \7 u6 I0 X8 F(input-codes → lvgl-codes 映射)            │            ▲
* N! w( |/ q1 i5 L9 ^  f2 g* y       │ LV_KEY_*                         │ display_write(RGB565)
* m* @3 @; C9 O" ]       ▼                                  │            │
. B  z. V& M; D$ o  H, _LVGL keypad indev → lv_group 焦点组 ──────┴─── LVGL 渲染(lv_timer_handler)
LVGL 显示与输入在 Zephyr 中全部通过设备树声明对接,应用代码零胶水。
2. 硬件连接(LCD 扩展板插 Arduino 排针)功能Arduino 位H5E5ZJ 引脚外设
$ X0 ?8 h  ]# }' }, L: iSPI_SCKD13PE12SPI4_SCK, J9 A: \7 _" J, l/ Z
SPI_MISOD12PE13SPI4_MISO
" |' o' o, x3 D! i; S3 y! D* J# SSPI_MOSID11PE14SPI4_MOSI; Z+ ~/ f8 ?% l" g$ s
LCD CSD10PE11GPIO(软件控制)' w, r% C. U' i: h
LCD DCD8PA9GPIO
' a6 ^0 q# v5 C8 G8 L& {五向摇杆A3PB0ADC1_INP9
引脚依据为 MB2129 原理图第 5 页连接器部分:
mb2129_arduino_cn5_cn9.png
摇杆原理
电阻分压式:3.3 V 经上拉,拨向某方向把对应电阻短到 GND,输出不同分压。ADC(12 位,3.3 V 基准)实测各方向电压:
方向ADC raw(约)电压(约)press-thresholds-mv
. e% m" r; C* V0 G, h6 Z# T- e* C右8871 mV71
. H' k7 n# s! D& y# W  D! s上738595 mV595
/ V; G# C) J' m$ N确认(按下)13101055 mV1055
, l  [0 K( z7 Q, @+ o0 n左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/
6 K$ d: y+ d2 K0 v- o/ I├── CMakeLists.txt
  e: U& f2 R7 g7 E7 W% o├── prj.conf% ~, G5 u- P/ v& J, |/ \
├── boards/nucleo_h5e5zj.overlay
+ ?- r' v6 U4 D& {0 z├── dts/bindings/display/
  [8 [, j9 i7 r" ^. i" u3 J9 D" C" ~│   ├── uniknect,lcd-bare.yaml   # display shim 绑定
3 s$ g7 f- A: [3 [' O2 r7 V$ M│   └── 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 的显示后端)
2 O! f1 ^3 e. ^1 Z7 [    └── 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>
* V3 |9 d& {( `7 j" h#include <zephyr/dt-bindings/lvgl/lvgl.h>& x( s8 p! E( {& ^; L1 l
​
+ V: F, i7 j9 t0 x7 F$ ^3 V/ {
; A* ?; P  V( R+ I3 G% k- H+ Z    joystick: joystick {/ k% o7 x  }8 M& j$ ?( N+ G2 |
        compatible = "adc-keys";
- v  U5 i2 V, n1 y1 H+ }& D        io-channels = <&adc1 9>;( @  J7 {5 X" s
        sample-period-ms = <50>;
. n1 P( i4 m: f! j0 t$ o        keyup-threshold-mv = <3300>;          /* 松开=开路电压 */
! v5 p! {  l* ~! f​
" x) n7 j+ o$ W* `  Q' {7 \  z        key_right  { press-thresholds-mv = <71>;   zephyr,code = <INPUT_KEY_RIGHT>; };
- N6 k% h+ s8 M# Y" H8 x  J5 z        key_up     { press-thresholds-mv = <595>;  zephyr,code = <INPUT_KEY_UP>; };
: b; H) g, V9 }8 g1 \6 y' K        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
    };
4 p8 u1 R" _" e& Q! ~};
ADC 通道配置(v4.4 的 H5E5 设备树中 adc1 是独立节点,无 adc12_common 包装层,adc_ker 时钟和分频直接写在 &adc1 上):
&adc1 {
0 L+ B) `3 v; N# @    clocks = <&rcc STM32_CLOCK(AHB2, 10)>,4 j% X8 @1 `3 K% U+ T: v
         <&rcc STM32_SRC_HCLK ADCDAC_SEL(0)>;
/ [- i! N/ b1 n% U1 K9 r$ W- ~    clock-names = "adcx", "adc_ker";
- w  p$ E( j, i$ F    pinctrl-0 = <&adc1_inp9_pb0>;2 x" @7 ?6 h" [. h9 G% z  l
    pinctrl-names = "default";
9 ^+ O6 ^4 w, q2 S0 q# m" u    st,adc-clock-source = "ASYNC";
' s' O7 {/ c% w# K    st,adc-prescaler = <6>;
- M* Q6 A6 H4 V$ e: f" j- z  S4 k    status = "okay";3 [3 ?( o$ g2 P% k" l
    #address-cells = <1>;, G7 p8 L* n# y1 B
    #size-cells = <0>;
! M+ {- s4 S. f6 t! q, k​# 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 判定,必须给基准 */
3 B8 t: [$ o" C# T  ]; ^        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; B
    lvgl_keypad {
: l" Y" S8 \3 A        compatible = "zephyr,lvgl-keypad-input";
5 n8 H) c: j* i$ J( A( g# g        input = <&joystick>;
3 y& b2 V9 S0 Z7 j# z; a5 f/ K+ Z2 ~        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
1 E9 L  f. }3 F- |6 m# J: N                  LV_KEY_ENTER>;, H4 W: x& T  q/ H& d$ p
    };: k8 ]) `: _& U; u
};
踩坑记录:最初把方向映射成 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
: Y0 N! P9 r/ \+ H) V) f9 kCONFIG_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
​
+ m, y' b# `; s3 QCONFIG_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
, ~9 v4 o+ a5 K( U7 B2 U; _5 @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 输入设备并绑定焦点组 */
5 p- C  n) l. Y. E# N; j8 Xlv_indev_t *indev = NULL;
$ [9 {3 d( p  o& Q- W; C# \- {3 _while ((indev = lv_indev_get_next(indev)) != NULL) {, u! b2 d3 S8 z4 z
    if (lv_indev_get_type(indev) == LV_INDEV_TYPE_KEYPAD) {
0 H) X) m) x. s! I        keypad = indev;
6 p, ?# m' H8 F* n* b    }; 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); 每个菜单项都加入 */
& e/ R; o/ k2 D) G2 G& _  Mlv_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);
% T: d8 U8 |. M% ]$ D8 H+ b​" z: r/ V* @; M$ ^' V* c( B4 i
/* 3. 主循环 */
% e# C/ c" d* Cdisplay_blanking_off(display_dev);
: _  i0 G) @2 f& N6 C8 Q- h* Owhile (1) {1 }1 l/ R/ G- w1 Y% b
    uint32_t sleep_ms = lv_timer_handler();
& W" O2 R9 |! t* _/ j    k_msleep(MIN(sleep_ms, 20));
) W; ?" i" l/ g! v4 u}
输入事件在 lv_timer_handler() 内部从消息队列取出处理,主循环单线程即可,无需额外同步。
第 6 步:编译、烧录、验证cd /d/luglZephyrproject
3 o, W4 ^( h7 hexport 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):
  • 菜单正常渲染,字体/颜色正确(RGB565 字节序约定有效);
  • 摇杆上/下/左/右:焦点框在 4 个菜单项间循环移动;
  • 摇杆按下:触发当前项,板载 LD1/LD2/LD3 随之翻转,状态行显示 LDx ON/OFF。
    % A# C9 M' s: o2 e5 Y& z% D
nucleo_h5e5zj_lvgl.png
- |* T. l+ k0 A" H4. 经验总结
  • 电阻分压式摇杆不要自己写驱动: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 工程的经验。

    $ ?$ \) c9 I1 U/ v( }
lvgl_系统架构图.png
赞 收藏 评论0 发布时间:2026-10-16 22:34

举报

0个回答

所属标签

相似技术帖

官网相关资源

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