平台:Zephyr 4.4.0 + zephyr-sdk-1.0.1,板卡 FRDM-MCXA366(MCX A366, Cortex-M33 @ 180 MHz,1 MB Flash,240 KiB 可用 RAM) 屏幕:NXP LCD-PAR-S035(3.5" 480×320,ST7796S 主控 + GT911 电容触摸,8080 并口) 本文重点:prj.conf 逐条解读 与 LCD-PAR-S035 的匹配(适配)分析。
1. 移植成果一览
LVGL 9.5 正常显示,全屏 DMA 刷新 ~15 ms,单帧渲染 3~4 ms,表盘动画流畅(33 FPS);
GT911 触摸开箱即用,坐标方向与画面一致,按钮响应正常;
应用侧没有一行显示/触摸初始化代码——全部由 shield + devicetree + Zephyr LVGL 胶水层自动接好;
代价:板级 dts 增加 3 个 nodelabel 节点,FlexIO LCD 驱动修复 3 个 eDMA V4 缺陷。


整条链路分六层:应用只跟 LVGL API 打交道;LVGL 渲染后通过 Zephyr 胶水层的 flush 回调走 display_write();DISPLAY 子系统按 chosen: zephyr,display 找到 ST7796S 节点;底层 mipi_dbi_nxp_flexio_lcdif 驱动把像素经 FlexIO0 的 16 位并口
eDMA 推给屏幕。触摸方向相反:GT911 中断 → INPUT 子系统 → 胶水层自动创建的 lv_indev(含坐标旋转变换)→ LVGL 控件事件。
2. prj.conf 逐条解读
prj.conf 是整个工程的"总开关",按功能分五组(完整文件见仓库根目录):


2.1 外设驱动组
CONFIG_GPIO=y
CONFIG_PWM=y # eFlexPWM via drivers/pwm/pwm_mcux.c (nxp,imx-pwm)
CONFIG_I2C=y # LPI2C1 -> AS5600, LPI2C3 -> GT911 touch
CONFIG_ADC=y # LPADC1 -> Vbus
CONFIG_DISPLAY=y
CONFIG_INPUT=y
CONFIG_INPUT_GT911=y
CONFIG_DISPLAY=y / CONFIG_INPUT=y 是 LVGL 胶水层挂接显示和触摸的前提; 真正的屏幕驱动(ST7796S、MIPI-DBI、FlexIO、eDMA)由 devicetree 节点的 status = "okay" 和依赖关系自动带入,不需要手写。
CONFIG_INPUT_GT911=y:GT911 在 DT_HAS_GOODIX_GT911_ENABLED 时本来就会 default y,显式写出只是为了可读性——读 prj.conf 就知道触摸用的是哪颗料。
2.2 浮点 FPU 组(本工程最大的坑)
CONFIG_FPU=y
CONFIG_FPU_SHARING=y
CONFIG_CBPRINTF_FP_SUPPORT=y # printk %f (called from the control thread only)
FOC 算法要求硬浮点;CONFIG_FPU_SHARING=y 的语义是:只有创建时声明了 K_FP_REGS 的线程才允许执行浮点指令,否则直接 Usage Fault。
这条对 LVGL 移植有直接影响:LVGL 9 内部大量使用 float(例如 lv_scale 画表针要算三角函数),所以 LVGL 必须跑在带 K_FP_REGS 的专用线程里: K_THREAD_DEFINE(ui_tid, 8192, ui_thread, NULL, NULL, NULL, 6, K_FP_REGS, 0) (见 src/main.c:53)。
Shell 线程没有 FPU 上下文,凡是涉及浮点参数的命令(motor tune 等)都采用 "延迟执行"模式:Shell 只保存字符串,由控制线程代为解析执行。
CONFIG_CBPRINTF_FP_SUPPORT=y 让 printk("%f") 可用,但只从有 FPU 权限的 控制线程调用。
2.3 Shell 组(调试通道)
CONFIG_SHELL=y
CONFIG_SHELL_BACKEND_SERIAL=y
CONFIG_SHELL_PROMPT_UART="motor> "
CONFIG_SHELL_VT100_COMMANDS=y
CONFIG_SHELL_TAB_AUTOCOMPLETION=y
串口 115200 上的交互式命令行(motor calib/open/vel/pos/stop/clear/status/ print/tune),和触摸 UI 并行使用,方便调试与自动化测试。
2.4 LVGL 组
CONFIG_LVGL=y
CONFIG_LV_Z_MEM_POOL_SIZE=65536
CONFIG_LV_FONT_MONTSERRAT_12=y
CONFIG_LV_FONT_MONTSERRAT_14=y
CONFIG_LV_FONT_MONTSERRAT_16=y
CONFIG_LV_FONT_MONTSERRAT_18=y
CONFIG_LV_FONT_MONTSERRAT_20=y
CONFIG_LV_FONT_MONTSERRAT_28=y
CONFIG_LV_FONT_MONTSERRAT_36=y
CONFIG_LV_USE_SCALE=y # lv_scale gauge (replaces LVGL v8 lv_meter)
CONFIG_LV_USE_SYSMON=y
CONFIG_LV_USE_PERF_MONITOR=y
逐条说明:
| CONFIG_LVGL=y | 启用 LVGL 模块和 Zephyr 胶水层(display/input 自动绑定) |
| CONFIG_LV_Z_MEM_POOL_SIZE=65536 | LVGL 堆 64 KB。480×320 界面 + 7 档抗锯齿字体,默认值不够用;64 KB 实测充裕 |
| CONFIG_LV_FONT_MONTSERRAT_* | Montserrat 每个字号独立编译进 Flash,按需开。界面用到 7 个字号就开 7 个。注意内置字体无中文字形,界面文字需用英文,要中文得用 LVGL font converter 导字库 |
| CONFIG_LV_USE_SCALE=y | LVGL 9 迁移要点:v8 的 lv_meter 被删除,表盘必须用 lv_scale(刻度 + section 弧 + 线条指针)自行拼装,不开此项 lv_scale 不编译 |
| CONFIG_LV_USE_SYSMON=y + CONFIG_LV_USE_PERF_MONITOR=y | 左下角 FPS/CPU 监视窗,移植阶段验证刷新性能非常直观,量产可关 |
2.5 系统杂项组
CONFIG_MAIN_STACK_SIZE=4096
CONFIG_LOG=y
CONFIG_LOG_DEFAULT_LEVEL=3
UI 线程栈(8 KB)是在 K_THREAD_DEFINE 里指定的,这里的 MAIN_STACK_SIZE 只服务 main() 本身(只做 motor_foc_init(),4 KB 足够)。
3. LCD-PAR-S035 匹配分析
这是移植的核心工作量所在。结论先行:Zephyr 上游有 lcd_par_s035 shield, 但官方从未适配 frdm_mcxa366,需要补齐板级 dts 的"契约节点",并修复驱动在 eDMA V4 平台上的三个既有缺陷。
3.1 shield 机制与 overlay 处理顺序
构建命令 west build -b frdm_mcxa366 --shield lcd_par_s035_8080 ... 会把 zephyr/boards/shields/lcd_par_s035/lcd_par_s035_8080.overlay 叠加进设备树。 关键时序:shield overlay 先于应用 overlay 处理。


shield overlay 里写的是 &zephyr_mipi_dbi_parallel { ... } 这样的引用, 它假定板级 dts 已经提供三个约定俗成的 nodelabel:
| nxp_lcd_8080_connector | J8 排针 gpio-map(pin9=触摸 INT,pin10=背光,pin11=复位) | frdm_mcxa366.dts |
| zephyr_mipi_dbi_parallel | nxp,mipi-dbi-flexio-lcdif,16 位总线、eDMA ch0 请求 71 | frdm_mcxa366.dts |
| nxp_8080_touch_panel_i2c | 挂在 lpi2c3(GT911,P3_27/P3_28 是板级默认 pinmux) | frdm_mcxa366.dts |
FRDM-MCXA366 的板级文件原本一个都没有——这正是官方没适配这块板的原因。 如果把它们写在应用 overlay 里,shield 展开时 label 尚不存在,编译直接报 undefined node label。所以三个节点必须加在 Zephyr 树的板级 dts 中(同家族 frdm_mcxa156 可当模板),配套 pinctrl 加在 frdm_mcxa366-pinctrl.dtsi (pinmux_flexio_lcd:16 根数据线 + WR/RD + CS/RS/RST/背光)。
应用 overlay(boards/frdm_mcxa366.overlay)因此只剩一件屏幕相关的事: 背光 gpio-hog,上电即点亮:
&gpio4 {
lcd_backlight_hog: lcd-backlight-hog {
gpio-hog;
gpios = <4 GPIO_ACTIVE_HIGH>;
output-high;
};
};
3.2 引脚与外设匹配
屏幕直插 J8 排针,信号与 MCU 资源的对应关系(与 UM12438 Table 17 一致):


匹配设计上有几个值得注意的点:
数据/选通全在 FlexIO0 上:16 根数据线映射到 FlexIO0 D16..D31,WR/RD 用 D7/D8,由 FlexIO 的 shifter+timer 拼出标准 8080 时序,CPU 完全不参与位翻转。
CS/RS/RST/背光是普通 GPIO,由 MIPI-DBI 层和 gpio-hog 管理。
触摸 INT(P2_24)不需要 pinctrl:MCXA 引脚复位默认 mux 即 GPIO。
eDMA 通道 0、请求源 71(FlexIO0 shifter0,编号来自 NXP HAL 的 PERI_DMA.h)。
MCXA366 的 FlexIO0 只有 4 个 shifter(PARAM=0x04200404), shifters-count 必须配 <4>;照抄其他板子配 8 会在初始化报 -ENOBUFS。
与电机引脚零冲突:电机用 P3_1/9/11/22/30/31 + P1_0/1,屏幕全部信号 一个不沾;GT911(LPI2C3)和 AS5600(LPI2C1)也不在同一总线。
3.3 显示链路:ST7796S over FlexIO + eDMA
shield overlay 基于 zephyr_mipi_dbi_parallel 创建 ST7796S 节点并设置 chosen { zephyr,display = &st7796s; }。板级 dts 侧的总线节点核心属性:
flexio0_lcd: zephyr_mipi_dbi_parallel {
compatible = "nxp,mipi-dbi-flexio-lcdif";
data-pin-start = <16>; /* FlexIO0 D16..D31 = 16 根数据线 */
enwr-pin = <7>; /* WR = FlexIO0 D7 */
rd-pin = <8>; /* RD = FlexIO0 D8 */
shifters-count = <4>; /* MCXA366 FlexIO0 只有 4 个 shifter */
timers-count = <1>;
dmas = <&edma0 0 71>; /* ch0, 请求源 71 = FlexIO0 shifter0 */
...
};
驱动(mipi_dbi_nxp_flexio_lcdif)工作方式:4 个 shifter 级联成 16 字节 缓冲链,timer 生成 WR 时序,每根线 10 MHz × 16 位;像素由 eDMA 从帧缓冲 搬进 shifter,理论速度全屏 ~15 ms。
3.4 触摸链路:GT911 五步使能(应用零代码)
shield overlay 自带 GT911 节点(lpi2c3 @ 0x5D,swapped-x-y + inverted-x),并设好 chosen { zephyr,touch };
lpi2c3 的 pinmux(SCL=P3_27、SDA=P3_28)和时钟是板级默认,status = "okay" 即生效;
prj.conf 显式加 CONFIG_INPUT_GT911=y;
LVGL 指针输入由 LV_Z_POINTER_FROM_CHOSEN_TOUCH 自动创建并绑定到 zephyr,display,坐标旋转/翻转映射一并完成;
shield.c 的 lcd_par_s035_init()(POST_KERNEL)先把 INT 拉低再初始化 GT911,完成 0x5D 地址选择——GT911 节点没接 reset-gpios,靠上电状态 + INT 拉低选中地址,实测工作正常。
3.5 驱动层三个缺陷与修复(eDMA V4)
调试中发现 nxp,mipi-dbi-flexio-lcdif 驱动在 eDMA V4(MCXA,无 DMAMUX) 平台上的三个既有缺陷,修复文件: zephyr/drivers/mipi_dbi/mipi_dbi_nxp_flexio_lcdif.c。


DMA 方向错误(现象:顶部一条彩带 + 全屏噪点):驱动声明 MEMORY_TO_MEMORY,eDMA V4 把整行 960 B 压成单个 burst 灌入,FlexIO 只有 4 shifter(16 B 缓冲),排空前数据即被覆写,每行仅 ~8 像素到达屏幕。 修复:channel_direction 改为 MEMORY_TO_PERIPHERAL,由 shifter 空请求 按 16 B minor loop pacing。
DOFF 被清零(现象:传输死锁):Zephyr DMA API 约定 M2P 外设侧地址不 递增(TCD DOFF=0),但目标是 SHIFTBUF0..3 四个寄存器,必须 16 B 取模循环 寻址。修复:dma_config() 后恢复 TCD_DOFF = sizeof(uint32_t)。
WFI 下请求停摆(现象:每块写屏 ~950 ms):eDMA V4 的 FlexIO 请求 pacing 在 CPU 进 idle(WFI)时基本停摆(~4k loops/s),忙等时跑满 ~1.4M loops/s;EARQ 位无效,疑似平台级怪癖(NXP 官方 SDK 在此芯片上也用 SmartDMA 而非 eDMA 推 LCD)。修复(workaround):忙等 DMA 完成回调,不再 k_sem_take 睡眠。代价是刷屏期 CPU 被占(全屏上限 ~15 ms)。
同平台的 FRDM-MCXA156 大概率存在同样问题,这三处修复值得上游化。
3.6 性能与资源占用(实测)
| 编译体积 | FLASH 487 KB(46%),RAM 163 KB(66%,含 LVGL 64 KB 内存池) |
| 裸屏色带测试 | 480×64×5 块,每块 3 ms,全屏 ~15 ms(总线理论速度) |
| LVGL 单帧渲染 | 3~4 ms(性能监视窗显示 33 FPS) |
| UI 线程 | 30 ms 心跳,8 KB 栈,prio 6,K_FP_REGS |
4. LVGL 8 → 9 迁移要点
从 LVGL v8 代码迁移时踩到的三个点:
lv_meter 没了:表盘用 lv_scale 重建——刻度尺(31 格 / 270° 扫角)
一个 section 弧(目标转速)+ 一根 lv_line 指针(实际转速),刷新就是 lv_scale_set_line_needle_value() + lv_scale_set_section_range() 两行 (实现见 src/motor_demo_ui.c)。
线程模型:lv_timer_handler() 30 ms 周期调用,配合脏区域局部刷新; LVGL API 只能在 UI 线程调用,其他线程想刷新界面就写共享整型变量,由 UI 线程的定时器读取重绘。
FPU 权限:见 2.2 节——CONFIG_FPU_SHARING 下 UI 线程必须带 K_FP_REGS,这是带屏工程相对裸机工程最容易漏的一项。
5. 常见坑速查
| 编译报 undefined node label | 三个契约 nodelabel 写在了应用 overlay | 挪到板级 frdm_mcxa366.dts |
| 初始化报 -ENOBUFS | shifters-count 配了 8 | MCXA366 FlexIO0 只有 4 个 shifter,配 <4> |
| 画面顶部彩带 + 噪点 | DMA 方向 M2M | 驱动改 MEMORY_TO_PERIPHERAL(3.5 节问题 1) |
| 写屏死锁 | TCD DOFF=0 | 恢复 DOFF=4(问题 2) |
| 刷新 ~1 秒一块 | WFI 下 eDMA 停摆 | 忙等完成回调(问题 3) |
| Usage Fault(一进 LVGL 就崩) | UI 线程没声明 K_FP_REGS | K_THREAD_DEFINE(..., K_FP_REGS, 0) |
| 中文显示为方框 | Montserrat 无中文字形 | 界面改英文,或 font converter 导中文字库 |
| west flash 报 No matching devices | 板载调试器固件非 linkserver | 本板 MCU-Link 已刷 J-Link 固件,用 west flash -r jlink |
6. 构建与验证
west build -b frdm_mcxa366 --shield lcd_par_s035_8080 -p always . -d build
west flash -r jlink
验证路径:上电背光即亮 → LVGL demo 界面出现(左下角性能窗看 FPS)→ 触摸按钮计数正常 → 触摸/串口两条通道都能控制电机。
7. 参考来源
引脚映射:NXP MCUXpresso SDK frdmmcxa366 display 例程 (smartdma_camera_flexio_mculcd、lvgl_examples)+ UM12438 Table 17
DMA 请求源:modules/hal/nxp periph2/PERI_DMA.h (kDma0RequestMuxFlexIO0ShiftRegister0Request = 71)
Zephyr shield:zephyr/boards/shields/lcd_par_s035/
同家族参考实现:zephyr/boards/nxp/frdm_mcxa156/

我要赚赏金
