ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

STM32公交语音报站器:原理图、PCB与代码调试全流程

STM32公交语音报站器:原理图、PCB与代码调试全流程

做公交地铁语音报站器这类STM32项目,这几年在课程设计和毕业设计里一直很常见。功能链路非常清楚:按键触发进站出站,语音模块播报实时站名,屏幕同步显示站点信息,软硬件都有展示点,做出来的实物效果又很直观。我最近把整套系统从头到尾完整走了一遍,交付物包含程序源码、原理图、PCB和演示视频。这篇文章不打算堆砌重复的工程说明,重点讲清楚我在器件选型、原理图设计、PCB排布和代码调试中遇到的关键取舍和实际踩过的坑,给正在做类似项目的同学一份能直接参考的实操路径。

这套系统的核心并不复杂,但要做好需要把几个环节都打通:先梳理报站逻辑,再定器件方案,然后画原理图和PCB,最后写代码调试。任何一个环节出现问题,都会在演示时翻车。下面我就按实际开发顺序,把每一步的关键细节展开说。

1. 系统整体设计:先想清楚报站器到底要干什么

1.1 需求拆解:从真实公交报站逻辑说起

做硬件项目最容易犯的毛病就是一上来就画原理图,结果做到一半发现功能定义不清楚。我先花了一点时间把真实公交车上的报站流程梳理了一遍,再用单片机去模拟它。

真实的报站逻辑大概是这样的:车辆起步后,播报“欢迎乘坐XX路公交车,本车开往终点站XX”;行驶途中在距离下一站还有一段距离时,播报“下一站XX,请提前做好下车准备”;进站停稳后,播报“XX站到了,请从后门下车”;出站后再播报下一站的预告。周而复始,直到终点站。

放在这个STM32项目里,我们不需要真的去定位车辆位置,核心是用按键来模拟“行驶到站”这个动作。所以我把功能需求拆成了三块:

  • 按键操作:按键A模拟“已到站”,按键B模拟“已离站/开启下一段行程”,必要时加一个复位按键回到始发站。
  • 语音播报:中文合成播报站名和提示语,这是整个项目的核心亮点。
  • 信息显示:OLED屏显示当前站序、当前站名、下一站名和剩余站数,让演示时不仅“听得见”,还能“看得见”。

除了这三个基础功能,后续还可以扩展自动报站、环境信息显示等项目增强点,这个放到最后再说。

1.2 核心器件选型:语音模块是最大变量

主控芯片我选的STM32F103C8T6。这颗芯片在同类项目里几乎是默认答案:64KB Flash、20KB RAM,跑一个报站器绰绰有余;GPIO、串口、I2C、定时器这些外设也足够丰富;最小系统板成本很低,资料多到随手就能搜到。相比51单片机,它的Flash空间更从容,后面增加站点、扩展传感器时不用频繁担心容量不够。

语音模块是整个项目最需要斟酌的部分,我对比过三种常见方案:

方案原理优点缺点适用场景
SYN6288语音合成模块通过串口发送GBK中文文本,芯片内部TTS合成语音站名动态可变,不需要预先录音;串口控制简单中文必须用GBK编码,容易踩编码坑;语音是合成音,不够自然各种站名、动态文本,推荐首选
DFPlayer Mini + TF卡播放TF卡中预存的MP3文件音质接近真人录音;播放稳定需要提前生成音频文件;串口协议带校验和,稍繁琐需要真人语音播报时
JQ8900语音模块支持按键和串口控制,可播报MP3集成度高,外围电路简单内部资源不太透明,灵活性和扩展性一般功能固定、不想写代码控制的场合

我最后选了SYN6288。原因是报站器要求的是“动态文本播报”——站名是可以配置的,用合成芯片只需要改代码里的字符串,不用每次录一段新的MP3。SYN6288自身带功放,可以直接接喇叭,这也能少做一个功放电路。后面我会专门讲它最坑的GBK编码问题,这个坑几乎每个人都会踩一次。

显示模块用0.96寸OLED,SSD1306主控,I2C接口。I2C比SPI少两根线,用GPIO模拟也很稳定,适合在原理图和PCB上省事。如果想做大一点的信息展示,也可以换1.3寸或者2.4寸TFT屏,但演示需求下0.96寸OLED足够清晰,还省电。

电源部分用USB 5V输入,经过AMS1117-3.3稳压给STM32供电。语音模块和功放部分用5V直供,因为TTS芯片和功放对电压要求更高,直接吃5V可以保证输出功率。要注意OLED模块的供电选择,很多0.96寸OLED板载LDO,3.3V到5V都能工作,但接线时一定要确认模块的VCC标识。

1.3 架构思路:为什么用状态机管理报站流程

报站逻辑如果用“按下按键就播报一段语音”这种线性写法,代码会越写越乱。比如用户连续按了三次按键,语音还没播完,这时候下一个状态应该是什么?如果每次都直接打断上一次播报,会出现语音错乱;如果不打断,又会堆叠。

我采用的是经典有限状态机思路,定义了四个状态:

  • 待机状态:系统上电初始,显示起始站信息,等待启动。
  • 行驶状态:语音播报“本车开往XX方向,下一站XX”,屏幕显示站序。
  • 到站状态:播放“XX站到了,请下车”,屏幕高亮显示当前站。
  • 离站状态:播放“下一站XX,请提前准备”,随后回到行驶状态。

每次按键只负责“产生一个事件”,具体进入什么状态、播报什么内容,都由状态机统一决定。这样带来的直接好处是:无论按键怎么乱按,系统状态都不会紊乱;后续想改成自动报站,只需要把“按键事件”替换成“传感器触发事件”,状态机主体完全不用动。这个设计思路虽然听起来简单,但在实际调试里帮我省了大量时间。

2. 硬件设计:原理图与PCB的实操要点

2.1 STM32最小系统的原理图设计

STM32F103C8T6的最小系统主要由电源、晶振、复位、BOOT和下载接口组成。我用嘉立创EDA标准版画原理图,元件库直接搜“STM32F103C8T6”“AMS1117-3.3”“SWD”就能找到封装。

电源部分:USB座输入5V,经过AMS1117-3.3输出3.3V。AMS1117输入端放一个100μF电解电容和一个100nF瓷片电容,输出端放一个10μF钽电容再加100nF,这样能有效抑制电源纹波。每个VDD引脚旁边都要放100nF去耦电容,VDDA引脚放1μF电容和10nF电容组合。

晶振部分:STM32可以用内部HSI振荡器跑8MHz,很多例程甚至不画晶振也能工作。但我建议还是加上8MHz外部晶振加两个20pF负载电容,这样串口波特率更准确。语音模块对时序要求不算苛刻,但串口波特率如果偏差超过2%,偶尔就会出现乱码或丢字,外部晶振能让人更省心。

复位电路用经典的10kΩ上拉电阻加100nF对地电容,接到NRST引脚。BOOT0引脚必须通过一个10kΩ电阻下拉到地,如果悬空,芯片可能启动到系统存储器而不是Flash中的程序,表现为下载后运行不了。SWD下载接口引出4个引脚:3.3V、GND、SWDIO、SWCLK,建议再串联一个1kΩ电阻在SWDIO上做简单保护。

2.2 语音模块和OLED的接口设计

SYN6288模块和STM32之间是串口连接。我用的USART1,PA9做TX连模块RX,PA10做RX连模块TX。这里有一个很容易被忽略的点:SYN6288模块的串口电平通常是3.3V TTL,和STM32兼容,直接连接没问题;但如果买的模块是5V电平版本,就必须做电平转换,否则长期使用会损伤引脚。

语音模块的电源和喇叭接口要留足空间。SYN6288模块板载功放,喇叭接口一般是2P的排针或接线端子。选喇叭时我用了8Ω、2W的圆形小喇叭,声音清晰够用。喇叭离MCU要有一定距离,功放输出大电流走线也尽量不要和串口信号线平行贴在一起,否则播报语音时,串口容易受到干扰,导致后续指令偶尔丢失。

OLED用软件模拟I2C,选两个普通GPIO,我用PA5做SCL、PA7做SDA。为什么不选硬件I2C引脚PB6/PB7?原因很现实:STM32F103的硬件I2C稳定性一直被人诟病,软件I2C虽然多占用CPU,但在这个项目里完全够用,而且可以随便换引脚。对初学者来说,软件I2C遇到问题也好排查,逻辑分析仪一抓就知道时序对不对。

整个硬件接线可以整理成一张速查表:

功能STM32引脚外设引脚备注
语音模块TXPA10RX仅播报时可省略
语音模块RXPA9TX播报数据发送
OLED SCLPA5SCL软件I2C
OLED SDAPA7SDA软件I2C
按键APA0KEY1下拉/外部中断
按键BPA1KEY2下拉/外部中断
喇叭模块输出接口喇叭正负8Ω/2W

2.3 嘉立创EDA绘制流程与PCB布局布线避坑

原理图在嘉立创EDA里画完后,直接“转换为PCB”,然后进入布局布线环节。学生项目基本都是两层板,这个规模两层板绰绰有余,打样价格也便宜。

布局的关键原则是从“使用体验”反推。按键放在板子边缘,方便演示时用手指按压;OLED排针座放在板子上方,屏幕可以正面朝外;语音模块的插座放在一侧,喇叭线从这里引出;SWD接口集中放一排,方便烧录时插线。STM32放在板子中央,晶振紧贴MCU放置,距离控制在5mm以内,两个负载电容对称放在晶振和MCU之间。

布线时我习惯先布电源和地,再布信号线。电源线加宽到1mm以上,信号线使用0.3mm到0.5mm。铜箔厚度1oz的情况下,1mm线宽大约能过1A电流,这个项目的语音模块瞬时电流可能到500mA以上,所以语音模块供电线不能太细。晶振走线要短,尽量包地;串口线和I2C线远离喇叭输出走线;整板铺地铜,并打一圈过孔连接底层地。

关于机械固定孔,PCB四角可以放M2螺丝孔,方便后面装亚克力外壳或3D打印外壳。M2螺丝孔的钻孔直径一般是2.0mm到2.2mm,如果做金属化过孔,过孔外径做到2.8mm到3.0mm,也就是环宽留0.4到0.5mm即可。立创EDA生成Gerber后直接下单,板厚选1.6mm,颜色看个人喜好,一般绿色打样最便宜。

2.4 焊接与硬件调试要点

PCB到手后不要急着全焊,我习惯按模块分批焊接、逐级验证。先焊电源部分:USB座、AMS1117、滤波电容,上电测3.3V是否正常,同时确认没有短路。再焊STM32最小系统,SWD接口接上ST-Link,尝试下载一个GPIO翻转的测试程序,确认芯片能跑。最后再焊OLED排针、语音模块插座和按键。

焊接过程中最容易出问题的是AMS1117的引脚方向。丝印上一般有标记,但如果板子是嘉立创EDA自动生成的封装,还是建议对照数据手册确认输入输出引脚。有一个我踩过的低级错误:第一次画板时电解电容极性画反,上电后电容直接鼓包,排查了半天才发现是封装引脚定义和实际元件不一致。热风枪和电烙铁配合使用,焊接QFN封装芯片之前先在焊盘上涂好助焊剂,用热风枪吹焊,能大大提高成功率。

硬件调试时,万用表主要量三处:3.3V是否稳定、GND是否连通、SWDIO/SWCLK引脚是否确实连到MCU。示波器不是必须的,但如果有,可以看晶振引脚有没有起振波形。没有示波器也不慌,用ST-Link读芯片ID能确认JTAG/SWD链路通不通,这是最直接的判断方法。

3. 软件实现:报站逻辑、语音播报与站点显示

3.1 按键消抖与站点状态机实现

按键用了PA0和PA1,外部下拉电阻,按下为高电平。单片机按键最经典的问题是抖动,机械按键按下瞬间会有几毫秒到十几毫秒的不稳定电平,如果不处理,一次按下可能触发多次事件。

我的处理方法是“延时消抖加状态机事件”。在按键中断里先判断电平,然后调用一个10ms延时,再次确认电平状态,确认后再发事件。这个做法虽然简单,但在报站器这种低频操作场景下足够可靠。更工程化的方案是用定时器每10ms扫描一次按键,维护按键释放、按下、长按三个状态,不过对课程设计来说,中断加延时消抖已经够用。

状态机核心代码逻辑是这样的:

typedef enum { ST_IDLE, // 待机 ST_DRIVING, // 行驶中 ST_ARRIVED, // 已到站 ST_DEPARTED // 已离站 } BusState; volatile BusState bus_state = ST_IDLE; volatile uint8_t cur_station = 0; void OnArriveKey(void) { switch (bus_state) { case ST_DRIVING: case ST_DEPARTED: bus_state = ST_ARRIVED; Break_Station_Play(cur_station); // 播放“XX站到了” OLED_ShowStation(cur_station); break; default: break; } } void OnDepartKey(void) { if (bus_state == ST_ARRIVED) { bus_state = ST_DEPARTED; Next_Station_Play(cur_station + 1); // 播放“下一站XX” OLED_ShowNextStation(cur_station + 1); } }

站点数据用结构体数组统一管理,后续增加站点只改数组就行:

typedef struct { const char *name_gbk; // 站名,GBK编码字符串 } StationInfo; const StationInfo station_list[] = { {"\xB8\xA3\xD6\xDD\xC2\xB7"}, // 福州路(GBK编码示例) {"\xC8\xCB\xC3\xF1\xB9\xE3\xB3\xA1"}, // 人民广场 // 更多站点... }; #define STATION_COUNT (sizeof(station_list) / sizeof(station_list[0]))

3.2 SYN6288语音播报的串口指令格式

SYN6288通过串口接收固定格式的指令帧。合成播报的帧格式是:帧头0xFD,后面跟两个字节的数据长度,长度值等于“命令字1字节加文本字节数”,再跟命令字0x01,最后是GBK编码的中文文本。

举个例子,播放文本“你好”,GBK编码是C4 E3 BA C3四个字节,长度就是1+4=5,完整帧是FD 00 05 01 C4 E3 BA C3。向模块发送这段数据,模块就会合成播报“你好”。

实际发送函数可以这样写:

void Syn6288_PlayText(const char *gbk_text) { uint16_t len = strlen(gbk_text) + 1; // 命令字占1字节 uint8_t buf[128]; uint8_t idx = 0; buf[idx++] = 0xFD; buf[idx++] = (len >> 8) & 0xFF; buf[idx++] = len & 0xFF; buf[idx++] = 0x01; // 合成播放命令 while (*gbk_text) { buf[idx++] = *gbk_text++; } UART1_SendBuffer(buf, idx); }

发送完一帧后要留一点延时,让模块有时间完成合成播报。我实测在9600波特率下,合成一句约20个字的提示语大约需要2到3秒,所以每帧播报结束后要等播报完成再发下一帧,否则模块可能丢弃指令。这一点在连续播报“欢迎乘坐XX路,本车开往XX,下一站XX”这种长句子时尤其重要。

3.3 DFPlayer语音文件播放的备用方案

如果你的项目更看真人声轨,或者希望语音更自然,可以换成DFPlayer Mini加TF卡。DFPlayer的串口指令帧固定10字节,以0x7E开头、0xEF结尾,中间包括版本号、命令、参数和校验和。指定曲目使用命令0x03,音量设置使用命令0x06,校验和是对指令中间若干字节累加后取反加一。

我建议直接参考成熟的开源DFPlayer库函数,不用自己从头写协议。语音文件用Edge浏览器的朗读功能或手机上的文字转语音APP生成MP3,命名按track001.mp3、track002.mp3这样递增,放入TF卡根目录。一个站点一个文件,文件多了以后,代码里用站点序号和MP3序号直接一一对应。

DFPlayer方案的缺点是每改一个站名都要重新生成音频文件,比较麻烦;优点是语音自然、播放稳定、功放音质好。如果追求演示效果,这个方案很值得考虑。我在实际调试时两款模块都试过,最后为了站名可以灵活修改,还是回归到了SYN6288,但DFPlayer方案作为备选写在说明文档里,方便别人按需求替换。

3.4 GBK转UTF-8:语音中文乱码的根治方法

SYN6288这个模块最折磨人的就是中文编码。模块要求GBK编码的中文文本,而现代Keil MDK默认把.c文件保存为UTF-8编码,代码里直接写的中文字符串在内存中是UTF-8字节。把UTF-8字节发过去,模块收到的是非法文本,表现就是播报乱码、静默或者提示音异常。

我一开始也在这里卡了很久,后来整理了三种可靠的解决方法。

第一种,改源文件编码。在Keil MDK的Edit->Configuration->Editor->Encoding里,把默认编码改成GB2312或者Chinese Simplified,再把.c文件另存为ANSI格式。这样源码里的汉字字符串,编译器保存时就是GBK字节,直接发送即可。这个方案最省事,但要注意整个工程里所有包含中文的.c文件都要统一编码,一旦某个文件还是UTF-8,单独一个站点名就会乱。

第二种,离线转码成字节数组。用在线工具或者本地脚本,把每个站名先转成GBK十六进制,例如“福州路”转成“B8 A3 D6 DD C2 B7”,直接写进const uint8_t数组。这样代码不依赖文件编码,无论用什么工具打开都是稳定的。缺点是写站名时要额外做一次转码,不能直接写中文。

第三种,运行时UTF-8转GBK。写一个转换函数,在代码里处理编码转换。这种方法适合动态文本,但转换逻辑复杂,还会消耗Flash和RAM,对报站器这种固定站点列表的项目没有必要。

我最推荐的是第二种。虽然多一步转码操作,但一劳永逸,而且烧录后行为完全可预期。调试时先用“你好”这种固定字符串把通路测通,再换真实站名,能大幅减少变量。

3.5 OLED站点显示与串口调试日志

OLED显示用SSD1306的I2C驱动,地址通常为0x78或0x7A。中景园电子的OLED例程非常经典,库函数简洁,直接移植到STM32工程里就行。需要注意I2C地址那行宏定义,不同模块有区别,白屏先查地址总是没错。

显示内容我做了三行布局:第一行显示“NEXT:下一站名”,第二行显示“NOW:当前站名”,第三行显示“剩余:X站”。由于0.96寸OLED显示汉字需要额外字库取模,为了省事,第一版我只用英文加拼音显示站名,比如“NEXT: RENMING SQ”。演示视频里语音播报中文,屏幕显示拼音,观众也能理解,而且避免了自己做字模的额外工作量。想要显示中文时,用PCtoLCD2002为需要的汉字逐字取模,再做一个简单的字库数组,功能上完全支持。

串口调试日志是整个调试过程的好帮手。我用USART2输出日志,每次状态机切换时打印一行:当前状态、站点序号、即将播报的内容。调试时用USB转TTL接PA2和PA3,电脑串口助手看输出。这个看似简单的操作,在定位“按键没反应”“语音和显示不同步”等问题时,效率比用示波器还高。

4. 常见问题排查与调试实录

4.1 语音模块不播报或乱码

先确认模块是不是坏了:单独给模块供电,用USB转TTL直接发FD 00 05 01 C4 E3 BA C3,如果模块播报“你好”,说明模块正常。随后再检查STM32一侧,用万用表测PA9到模块RX之间是否导通,确保焊接没问题。乱码基本就是编码问题,按上一节的方法把文本转成GBK字节再发。还有一个小细节容易被忽略:SYN6288上电后默认波特率是9600,如果STM32串口初始化成115200,模块收不到任何指令,必须先设置波特率或者保持一致。

4.2 程序下载失败的三种原因

下载失败是STM32项目里最常见的问题。第一种是SWD接口没接好,SWDIO和SWCLK松动或者线序接反;第二种是芯片的BOOT0被拉高,芯片复位后进入ISP模式不响应SWD;第三种是目标板供电不足,ST-Link自带的3.3V输出带不动整个板子。我的排查顺序是:先测目标板3.3V,再测SWD引脚电压,最后检查BOOT0电平。把这三项排除后,90%的下载问题都能解决。

4.3 OLED白屏

OLED白屏直接查I2C地址、SCL/SDA接线和供电电压。我先用万用表确认OLED模块的VCC脚确实是3.3V,而不是误接5V把屏幕背光烧了。然后用逻辑分析仪抓SCL和SDA波形,如果能看到正常的I2C起始条件和地址帧,说明硬件没问题,问题在初始化时序或者地址宏定义上。如果SCL上有波形但SDA一直高,八成是OLED模块地址不对,把0x78改成0x7A再试。

4.4 按键误触发和状态错乱

按键误触发的根源是抖动,我一开始用纯中断方式,按一次结果播报了三遍,后来改成“中断加10ms延时确认”才稳定。状态错乱往往是因为在语音播报还没有完成时又按了按键。解决方式是在状态机里加一个“busy”标志,语音模块正在播报时忽略进站和出站事件,等播报完成后才允许进入下一个状态。这个机制模仿的是真实公交车的“播放中不响应下一指令”,在实际演示中效果很好。

我把一些容易遇到的问题整理成速查表,方便参考:

现象最可能原因排查顺序
语音完全无声模块电源或喇叭线先测模块供电,再测喇叭
语音播报乱码中文编码不符合GBK先发固定文本确认通路
语音播到一半停波特率不匹配或指令过长确认模块波特率,分段发送
OLED白屏I2C地址不对先查地址,再查接线
按键一次触发多次未消抖加延时或定时器消抖
下载失败SWD线序/BOOT0先测供电,再测引脚

4.5 视频演示时的现场避坑

项目标题里包含视频演示,这一点实际上非常关键。我在录演示视频时发现,语音播报和OLED显示存在一件事:如果同时按下进站键和离站键,或者间隔太短,状态机虽然不会乱,但语音会连续播报两句,观感很不好。为了演示效果,我专门在按键事件里加了2秒的防重入保护,每次播报结束后才允许下一次事件触发。

另一个演示时容易翻车的地方是环境噪音。SYN6288的合成语音音量默认值不是最大,我用串口指令把音量调到较高档位,同时把喇叭朝向摄像头和观众。视频里最忌讳的是屏幕反光,拍摄时把OLED屏幕摆正角度,亮度调到合适值,画面才清晰。这些细节不影响功能,但直接影响项目评价,值得认真对待。

5. 从“能演示”到“像产品”:扩展方向

5.1 自动报站:用传感器替代按键

按键模拟报站能完成功能演示,但“智能”属性还不够突出。我建议做一个简单的自动报站扩展:在公交车站牌模型两侧装一对红外对管,模拟车门位置。当“车辆”驶过站牌,遮挡红外对管,触发一个外部中断,代替人工按键,实现自动触发报站。这个扩展只需要加一对红外模块,一个GPIO中断,状态机本身不需要改,代码改动量很小,但演示效果立刻不一样。

如果想更接近真实系统,可以加GPS模块,比如ATGM336H,通过串口输出NMEA数据。维护一个站点经纬度数组,解析GGA语句里的经纬度,计算与下一站的距离,小于阈值就触发到站播报。这个方案的工程量明显上来了,但也是这个项目最有技术含量的扩展方向。

5.2 增加车载环境信息显示

报站器如果顺带显示车厢环境信息,会显得更完整。加一个DHT11温湿度传感器,接任意GPIO,用单总线协议读取温湿度,显示在OLED的第二行或第三行。DHT11价格便宜、库函数丰富,单总线时序需要仔细写,但代码量不大。实测下来,DHT11的湿度响应比较慢,读取间隔不能太短,一般2秒读一次就够了。

还可以用TIM定时器输出PWM驱动无源蜂鸣器,在报站前播放一小段“叮咚”提示音,模拟真实公交的报站前提示音。PWM频率调在2kHz左右,声音穿透力强又不刺耳,播报前鸣响0.5秒,用户注意力会被吸引过来,整个系统更有层次感。

5.3 引入FreeRTOS优化任务划分

如果主控换成STM32F103ZET6,或者你想把系统做得更工程化,可以引入FreeRTOS。把功能拆成几个独立任务:按键扫描任务、语音控制任务、OLED刷新任务、站务逻辑任务。任务之间通过消息队列传递事件,比如按键任务检测到按下后,向站务任务发送“进站事件”,站务任务再把播报内容发给语音任务。这样做的好处是每个任务逻辑简单清晰,系统扩展多个传感器时也不会互相阻塞。

但我要提醒一句:对于这套报站器,裸机状态机已经完全够用,FreeRTOS更多是学习价值。如果是毕设想展示操作系统能力,加FreeRTOS是加分项;如果只是为了把项目跑通,没必要为了用而用。

5.4 外壳、供电与整车模型

硬件做稳定之后,外观是决定演示效果的另一半。我见很多同学用一个裸板加几根杜邦线做演示,功能都对,但视觉上很吃亏。建议用3D打印一个简单的公交车外壳,或者至少用亚克力板做一个小盒子,把PCB、电池、喇叭都装进去,OLED屏从外壳正面露出,按键和USB口开孔。

供电方面,演示现场没有稳定电源时,用18650电池加锂电池充电模块是最靠谱的方案。5V输出可以用传统的“升压模块+电池保护板”,但更建议直接买带5V输出的移动电源方案,安全稳定。车载12V输入可以再接一个DC-DC降压模块到5V,让整个装置也可以接在模型轨道电源上。

我在给装置做外壳时发现一个经验:喇叭不要紧贴外壳壁,那样播报时会产生共振,声音发闷。在喇叭和外壳之间留2到3毫米空隙,或者贴一块泡棉胶,声音立刻清晰很多。这种细节在演示视频里非常明显。

这个项目做完之后,最大的收获不是把原理图画得多漂亮,而是理解了一个完整系统的软硬件配合关系。我建议大家拿到开发板后先不要急着写代码,更不要急着画PCB,先把方案选型、状态划分、接口定义在纸上写清楚。做到后面你就会发现,前期这半小时的规划,能帮你省下好几天改板子和调代码的时间。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进