本文记录在 STM32H7S78-DK 上实现完整网络收音机的过程:以太网 DHCP 联网 → HTTP 拉取 MP3 直播流 → minimp3 软解码 → I2S6 输出到 WM8904 codec 出声,800×480 LVGL 界面(电台列表/播放控制/音量/实时频谱/网络状态栏)。
1. 架构
电台服务器 ──HTTP/TCP──► net_stream(接收线程,24KB pipe) │ ▼ net_radio(解码线程): minimp3 组帧解码 + 线性插值重采样到 48117Hz + Goertzel 频谱 │ (pcm_q, 阻塞反压) ▼ audio_sai(发送线程): 音量缩放/静音填补 │ GPDMA1 循环链表(双半区, 永不停止) │ I2S6(主机模式, MCK 供给 codec FLL) │ WM8904 codec → 耳机口
复用自 EK-RA6M5 收音机工程:net_stream.c(socket 流)、net_radio.c(解码+重采样+频谱)、minimp3.h 原样可用;音频后端 audio_sai.c 和界面 main.c 为新写。
2. 关键实现
自研音频输出驱动(绕开 Zephyr i2s 驱动的逐块 DMA 重载模型——在 GPDMA 上会周期性掉链):
/* GPDMA 循环链表模式: DMA 永不停止,半完成/全完成中断回填对应半区 */
static void dma_tx_callback(const struct device *dev, void *user_data,
uint32_t channel, int status)
{
if (status == DMA_STATUS_BLOCK) {
k_event_post(&refill_evt, 0x1); /* 前半播完 */
} else if (status == DMA_STATUS_COMPLETE) {
k_event_post(&refill_evt, 0x2); /* 后半播完(回卷) */
}
}
blk_cfg.source_reload_en = 1; /* dma_stm32u5 用链表模拟循环模式的关键 */
blk_cfg.dest_reload_en = 1;反压流控:解码线程投递用阻塞写,速率自动对齐播放速率;网络抖动由 24KB pipe 吸收:
/* 队列满就阻塞等——丢弃会造成音频空洞(杂音),这是实测教训 */ k_msgq_put(&pcm_q, &cur_blk, K_FOREVER);
codec 先于 I2S 配置:实测 I2S 时钟跑起来后 i2c1 上 WM8904 事务会 NACK(I2S 引脚与 I2C1 相邻引脚疑似串扰),所以寄存器写在 I2S 启动前的安静期完成。
3. 踩坑记录
| i2s_buf_write API 误用 | 发送线程 100% 空转(300 万次/秒),日志线程饿死、控制台假死 | 该 API 内部自己从 slab 取块拷贝,调用方不要再分配 |
| START 前队列必须非空 | 首启 queue_get FAILED | 触发前垫 4 块静音 |
| 垫块后等了 50ms 才放馈数线程 | 垫片播完欠载,驱动进入 ERROR 态 | 触发 START 后立刻放开馈数线程 |
| 解码无反压 | 解码比播放快 1/3,PCM 被静默丢弃,声音"不成形" | 投递改阻塞(K_FOREVER) |
| Zephyr i2s 驱动 + GPDMA | 播几秒后 TC 中断丢失,队列满但 DMA 停摆 | 弃用,自研循环 DMA 驱动 |
| k_event 没初始化 | MPU FAULT,空指针 | 用 K_EVENT_DEFINE 静态定义 |
| 主线程优先级过高 | 800×480 软件渲染饿死解码线程 | 主线程降到 prio 10,音频链路 5/6 |
| 采样率失配 | 约每 8 秒一次缓冲抽空 | 实测驱动分频:271MHz/22/256=48117Hz,重采样目标对齐它 |
4. 实测数据
播放稳定后的健康指标(每 10 秒打印):
stats: pcm_q=4 silence=615 drop=0 pipe_empty=0
pcm_q=4(反压生效)、drop=0(无丢弃)、pipe_empty=0(网络无断粮)、silence 不增长(无欠载)。
5. 电台表与验证方法
蜻蜓 FM 免费流(64k)国内稳定,添加新台前先验证可达性:
curl -s -m 5 -o /dev/null -w "%{http_code} %{content_type}\n" \
-r 0-1023 "http://lhttp.qingting.fm/live/<ID>/64k.mp3"返回 200 audio/mpeg 即可用。蜻蜓免费流没有 128k 档,高码率需换源。
6. 硬件与构建
板载 WM8904 codec(I2C 地址 0x1a),I2S6 引脚 MCK=PA3/WS=PA4/CK=PA5/SDO=PB5
注意板级 dts 里 timers2/pwm2 默认占用 PA3,与 I2S6 MCK 冲突,overlay 里已禁用
必须有 sram1 NOCACHE overlay(第五篇的以太网修复),否则网络必挂
west build -b stm32h7s78_dk/stm32h7s7xx/ext_flash_app --sysbuild -s app\net_radio -d app\net_radio\build -p west flash -d app\net_radio\build
7. 效果

我要赚赏金
