用 NUCLEO-H5E5ZJ 打造一台网络收音机(Zephyr 4.4 全网首发移植 + 踩坑全记录)
一、前言:为什么做这个项目 NUCLEO-H5E5ZJ 是 ST 新出的 Nucleo-144 板子,搭载 STM32H5E5ZJ(Cortex-M33 @250MHz、4MB Flash、1.5MB SRAM、板载以太网)。拿到手第一件事当然是跑 Zephyr——结果发现 Zephyr 官方还没有这块板子的支持(SoC 支持刚进 4.4,板级没人做)。 那就自己移植呗。既然板子有以太网、手里又有 LCD 扩展板和 MAX98357A 功放模块,干脆做个网络收音机:插网线、摇杆选台、喇叭出声,一次把 GPIO/UART/以太网/SPI/I2S/ADC/LVGL/TLS 全部点亮。 先上效果(音频解码 CPU 占用 19%,播放流畅):
整体设计原则:网络缓冲与音频缓冲分离、生产者永远快于消费者、丢比堵好(流媒体的血泪经验,后面调试篇细说)。
数据通路: 互联网电台 ──HTTP/TLS──► 以太网(RMII) ──► net_stream 接收线程 ──► mp3_pipe(24KB 抖动缓冲)──► minimp3 软解 + 重采样(48.225kHz) ──► PCM 播放队列(8 块≈190ms) ──► audio_i2s(音量/静音兜底) ──► I2S3+GPDMA ──► MAX98357A ──► 喇叭 控制通路:五向摇杆 ──► ADC1 ──► adc-keys ──► LVGL keypad ──► 选台 UI ──► 播放控制。 线程模型(4 线程各司其职): 线程职责优先级net_rxsocket 接收灌管道抢占 6 net_radioMP3 解码 + 重采样抢占 6 audio_i2sI2S 投喂 + 静音兜底协作 -2(原因见调试篇) mainLVGL UI抢占 0三、硬件连接模块信号接到哪说明 MAX98357ABCKCN7 pin1(PC10,I2S3_SCK)I2S 位时钟 DINCN7 pin3(PC12,I2S3_SDO)I2S 数据 LRCKCN7 pin17(PA15,I2S3_WS)I2S 帧时钟 Vin / GND3V3~5V / GNDCN7 上有 3V3(pin16)/5V(pin18)/GND SD3V3关断脚,接高使能 喇叭OUT+/OUT−4~8Ω,BTL 差分,两端都不能接地! LCD 扩展板整板Arduino 排针(CN5/6/8/9)SPI4=PE12/13/14,CS=PE11(D10),DC=PA9(D8),摇杆=A3(PB0) 网线RJ45CN14板载 LAN8742A,路由器需能上网 USB-CCN1电脑供电 + 调试 + ST-LINK VCP 串口(115200) 原理图依据(MB2129 第 5 页连接器部分,I2S3 的三个脚在 CN7 上都标着):
Zephyr 4.4 + 板外板(BOARD_ROOT)方式,不动 Zephyr 官方树: NUCLEO_H5E5ZJ/├── boards/st/nucleo_h5e5zj/ # 自移植的板级支持包(本次的主角之一) │ ├── nucleo_h5e5zj.dts # LED/按键/USART3/以太网/时钟/分区 │ ├── board.yml / Kconfig.* / *_defconfig / board.cmake │ └── nucleo_h5e5zj.yaml └── apps/net_radio/ # 网络收音机应用 ├── boards/nucleo_h5e5zj.overlay # LCD/摇杆/I2S3 的板级接线 ├── dts/bindings/display/ # 自定义显示绑定 └── src/ ├── main.c # LVGL 选台 UI ├── net_stream.c # HTTP/TLS 拉流层(socket 接收线程 → mp3_pipe) ├── net_radio.c # minimp3 解码 + 重采样 + 电台表 ├── audio_i2s.c # I2S 输出层(播放队列/音量/静音兜底) ├── lcd_bare.c # ST7735 裸 SPI 驱动 ├── display_bare.c # Zephyr display API shim(LVGL 显示后端) └── minimp3.h # 纯 C MP3 解码器 构建烧录一条龙: cd /d/luglZephyrprojectexport ZEPHYR_SDK_INSTALL_DIR=/d/luglZephyrproject/zephyr-sdk-1.0.1/zephyr-sdk-1.0.1 west build -b nucleo_h5e5zj apps/net_radio -d build_h5e5_radio -p always \ -- -DBOARD_ROOT=D:/STM32/STM32H5E5/NUCLEO_H5E5ZJ west flash -d build_h5e5_radio五、Zephyr 关键实现5.1 板级移植(从 0 到有) Zephyr 4.4 只有 STM32H5E5 的 SoC 级支持,板级文件自己写。几个要点:
H5E5 没有 SAI,I2S 由 SPI3 复用(I2S3)。Zephyr 的 i2s_stm32 驱动 + GPDMA 直推: /* H5 的 pinctrl 没有单独的 i2s*_sd 节点——数据脚与 SPI_MOSI 同脚同复用,* 直接用 spi3_mosi_pc12 */ &i2s3 { pinctrl-0 = <&i2s3_ck_pc10 &spi3_mosi_pc12 &i2s3_ws_pa15>; status = "okay"; }; 应用层 8 块播放队列(≈190ms),音频线程协作式优先级保证永不断粮: /* i2s_stm32 的纪律:先写数据再 START;队列排空即 ERROR,DROP 后才能重来 */i2s_configure(i2s_dev, I2S_DIR_TX, &cfg); for (int i = 0; i < 2; i++) i2s_write(i2s_dev, blk, BLOCK_SIZE); i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_START);5.3 网络流与 MP3 解码
TLS 1.2 socket + mbedTLS,电台表加 tls 标志即可混用 HTTP/HTTPS: fd = zsock_socket(AF_INET, SOCK_STREAM, IPPROTO_TLS_1_2);int verify = TLS_PEER_VERIFY_NONE; /* 公开电台流不校验证书 */ zsock_setsockopt(fd, SOL_TLS, TLS_PEER_VERIFY, &verify, sizeof(verify)); zsock_setsockopt(fd, SOL_TLS, TLS_HOSTNAME, host, strlen(host) + 1); 配套要点:CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y(不开的话 MBEDTLS_SSL_CLI_C 都不编进来)、板级 dts 的 chosen { zephyr,entropy = &rng; }(PSA 加密库的熵源,漏了握手直接报 INVALID_ARGUMENT)、密码套件要开 GCM 的(现在主流服务器都用 GCM)。 5.5 摇杆与 LVGL五向摇杆是电阻分压式(Arduino A3=PB0=ADC1_IN9),Zephyr 自带 adc-keys 输入驱动直接搞定(阈值 + 消抖全在 dts 里),再经 zephyr,lvgl-keypad-input 映射成 LVGL 按键: lvgl-codes = <LV_KEY_PREV LV_KEY_NEXT LV_KEY_LEFT LV_KEY_RIGHT LV_KEY_ENTER>;/* 注意:LVGL 焦点导航用 PREV/NEXT,方向键是发给焦点控件自用的 */六、调试过程(踩坑全记录,都是真金白银)坑 1:UM 文档的"HSE 默认不使用"和实物不符 UM3601 文字说默认焊桥下 HSE 不接,按它配了 HSI——结果串口只吐一个乱码字符就死。实物上 R3/R4 明明贴着 0Ω!教训:以实物和原理图为准,文档的"默认配置"可能和出货状态不一致。 坑 2:ST Cube 头文件缺 typedefstm32h5e5xx.h 里用了 CCB_TypeDef 却没定义它(H5F5 的头文件里是有的)——上游没有任何板子编译过 H5E5,这个 bug 一直没暴露。从 H5F5 头文件抄上那段 typedef 解决。 坑 3:LVGL 焦点导航用 LV_KEY_PREV/NEXT,不是方向键摇杆方向键事件明明产生了(上=103/下=108…全对),焦点就是不动。翻 LVGL 文档:LV_KEY_UP/DOWN/LEFT/RIGHT 是派发给焦点控件的,组导航只有 PREV/NEXT。改映射,秒好。 坑 4:播放卡顿 + 整机假死 —— FPU 没开!(本次最大BOSS)症状:开播后声音慢、一顿一顿,然后控制台静默、摇杆失灵,像整机死机。 排查链:加 [stats] 计数器打印 → 打印到一半消失 → pyOCD 挂 ST-LINK 采样 PC → 发现系统其实在正常跑解码,只是控制台死了 → 控制台死因是 printk 走了 logging 线程而该线程卡死(改 LOG_PRINTK=n+LOG_MODE_IMMEDIATE=y 直出)→ 控制台恢复后看到真凶:CPU 100%,解码只有实时的 63%。 根因:Zephyr 的 H5 dtsi 把 CPU 声明为 arm,cortex-m33(没 f 后缀),CONFIG_FPU 又没默认值——上游所有 H5 板子都没开硬件 FPU,浮点全走软件库,minimp3+重采样+频谱直接吃满 CPU。开了 FPU 后 CPU 降到 20%,解码 41.8 帧/s 跑赢实时 38.3 帧/s,丝般顺滑。 坑 5:I2S 欠载中断风暴i2s_stm32 驱动的 UDR(欠载)中断在欠载期间按帧率(~48kHz)持续触发——中断风暴直接饿死所有线程。打了个小补丁:UDR 触发一次后关闭该中断(START 时会重新使能)。 坑 6:示波器骗了我半天量 BCK 发现"三个脚都是 48.1kHz"差点怀疑人生——1.5MHz 的位时钟在低采样率设备上混叠成了低频假象。寄存器回读 + 实际发声才是最终判据。 坑 7:LVGL 内存池小了直接 Bus Fault电台列表从 5 项加到 13 项后,滚动到底就崩(lv_draw_unit_draw_letter 空指针)。LVGL 池 10KB→32KB;顺便把 24KB 网络管道 + 37KB 音频缓冲挪到 SRAM2(H5E5 有 5 块 SRAM 共 1.5MB,别都挤在 SRAM1)。 七、工程效果与展示最终电台表(4 台精选 + Stop,全部 HTTPS 国内源): 菜单项内容CNR-1.ZhongGuoZS中国之声(新闻联播 19:00 转播) BJ.News339北京新闻广播 HJ.Music4804怀集音乐之声 QC.Music4915清晨音乐台 实测串口日志: *** Booting Zephyr OS build v4.4.0 ***audio: i2s started 48000Hz 16bit stereo [00:00:01.652] <inf> phy_mii: PHY (0) Link speed 100 Mb, full duplex [00:00:05.744] <inf> net_dhcpv4: Received: 192.168.3.15 radio: online, ready nst: streaming lhttp.qtfm.cn:443/live/15318317/64k.mp3 ← HTTPS 开播!八、总结与展望 一次把新板的 GPIO/UART/ETH/SPI/I2S/ADC/LVGL/TLS 全部打通,最大的收获是三个可以推给上游的修复(H5E5 头文件缺 typedef、H5 全系没开 FPU、I2S 欠载中断风暴)。H5E5 的 4MB Flash + 1.5MB SRAM 在这个项目里只用了 14%/85%——余量还很足。 |
微信公众号
手机版