简介:智能语音垃圾桶是一套融合语音识别、嵌入式控制与硬件设计的综合项目资料,面向物联网、嵌入式及电子设计方向的初学者和进阶开发者,可用于课程设计、毕业设计或智能家居原型开发。压缩包共241个文件,体积约15.46MB,主要包含源程序(.c/.h)、原理图(.schdoc)、PCB设计文件(.pcbdoc)、PDF文档以及.axf/.hex等编译固件;源码覆盖STM32外设驱动、语音识别模块调用与电机开合控制逻辑,原理图和PCB则清晰展示微控制器、语音模块、音频播放器及电源管理等单元的信号连接与布局布线。目前已有941人学习关注。借助该资源可完整经历从电路设计、代码编写到固件烧录的硬件产品开发流程;对照原理图可排查硬件连接问题,阅读源码可掌握语音指令解析与电机控制算法,分析PCB可理解量产级的元件布局规范,实践指导价值较为突出。
1. 智能语音垃圾桶:从原理图、源程序到PCB文件的完整交付链
“智能语音垃圾桶(包含原理图、源程序、PCB文件)”这个标题放在嵌入式项目里,最有价值的不是“垃圾桶”外壳,而是括号里的三份交付物。它们分别回答三个问题:硬件怎么连通、逻辑怎么跑、实物怎么落。对刚入门的新手,语音模块和舵机的接法最容易写错;对有几年经验的工程师,电源网络和信号回流才是真正体现功力的地方。本文直接按一个可复现的工程来拆,以STM32F103C8T6做主控,语音识别在LD3320和SU-03T两条路线里二选一,把器件选型、原理图设计、C语言固件框架、PCB绘制到交付文件检查逐步讲透。方案整体走离线识别,不依赖云服务,响应速度和可靠性都更好控制。
2. 智能语音垃圾桶的系统架构与器件选型思路
智能语音垃圾桶的主信号链并不复杂:麦克风拾音,语音识别模组把声音转成命令,通过串口或IO通知主控,主控输出PWM控制舵机带动桶盖开合,同时用红外避障传感器做防夹保护。难点在于每个环节的器件选型都会影响另外两部分的代码怎么写。比如语音模组输出的是串口帧还是普通电平,直接决定源程序里是写UART解析还是读GPIO;舵机是数字舵机还是模拟舵机,又决定定时器PWM的周期和占空比范围。所以我一般先定语音方案,再定主控,最后才排舵机和电源。
2.1 语音识别方案:LD3320和SU-03T怎么选
市面上做离线语音控制常见两条路:一是用LD3320这类语音识别芯片,二是用SU-03T这类离线语音模组。LD3320是真正的识别芯片,能本地完成FFT和特征匹配,词条列表由MCU通过寄存器写入,控制逻辑完全自己写,适合想深入理解识别流程的人。SU-03T则把识别和词条训练都做进了模组,用厂商配套工具录词条、选命令,烧录后通过串口输出识别到的命令ID,主控只负责收帧和动作。
| 选型维度 | LD3320(识别芯片) | SU-03T(离线语音模组) |
|---|---|---|
| 主控负担 | 需配置寄存器、写入词条 | 只需解析串口帧 |
| 词条管理 | 靠程序动态写入 | 靠厂商工具烧录 |
| 上手难度 | 中高,要看数据手册时序 | 低,按工具向导走即可 |
| 典型场景 | 课程设计、算法研究 | 产品快速原型、竞赛作品 |
我的建议是:想要完全可控、愿意啃时序文档就选LD3320;如果目标是几天内把“语音开盖”跑通,SU-03T更稳,因为它的固件已经把降噪和唤醒处理好了。
2.2 主控选型:STM32F103C8T6还是STC89C52RC
主控决定源程序的写法和外设资源。常见做法是STM32F103C8T6配LD3320,或者STC89C52RC配SU-03T。很多人拿STC89C52RC原理图当课程设计底子,但它的512字节RAM跑不了太复杂的词条管理,也没有硬件PWM,舵机控制得用定时器中断去翻转IO,角度抖动明显。STM32F103C8T6有64KB Flash、20KB RAM,支持UART和定时器PWM,状态机写起来从容得多。
| 对比项 | STM32F103C8T6 | STC89C52RC |
|---|---|---|
| 内核/主频 | Cortex-M3 / 72MHz | 8051 / 12MHz |
| 工作电压 | 3.3V | 5V |
| UART数量 | 3路 | 1路 |
| 舵机PWM | 硬件定时器输出 | 需软件模拟 |
| 语音方案兼容性 | LD3320、SU-03T均兼容 | 建议SU-03T |
语音模组输出一般是3.3V TTL电平,直接对接STM32的UART引脚不用做电平转换;如果坚持用5V的STC89C52,则需要在RX线上串电阻分压,避免反向灌入3.3V侧电路。
2.3 舵机、传感器与电源的边界考虑
SG90是这类项目最常用的舵机,PWM周期20ms,脉冲宽度0.5ms对应0度、2.5ms对应180度。但它堵转时电流能到几百毫安,USB口直供时必须留足裕量。我一般把电源分成两路:5V直接给舵机,5V再经AMS1117-3.3降到3.3V给主控和语音模组。两路在LDO输入端汇合,不要在舵机后面再往后级串电,否则舵机启动瞬间会把整个3.3V拉垮。红外避障模块用普通数字输出型就行,接一个GPIO输入,检测到遮挡就不执行关盖动作。
2.4 为什么不用ESP32走在线识别
有个替代思路是ESP32加云平台ASR,看起来“更智能”,实际体验反而不如离线方案。垃圾桶放在桌边或走廊,网络抖动和识别延迟会直接暴露在开盖动作上,而且每次语音都要上传也涉及隐私。标题里的“智能语音垃圾桶”通常指本地语音命令控制,不是大模型对话。离线方案两次开盖之间延迟可以压在200ms以内,这对机械结构来说已经足够流畅了。
3. 智能语音垃圾桶原理图里必须检查的五个硬模块
原理图是这个项目的“连接枢纽”,源程序里的每个引脚定义都要在原理图上有对应网络标号。很多新手画完图就开始写代码,等PCB打样回来才发现某引脚悬空、某网络标号拼错。下面五个模块是智能语音垃圾桶原理图里最容易被检查出问题的地方。
3.1 最小系统不能只对着最小系统板原理图抄
STM32F103C8T6的最小系统包含8MHz晶振、两个20pF左右负载电容、NRST上拉电阻和复位电容、BOOT0下拉电阻,以及每个电源引脚旁边的100nF去耦电容。即便你是照着常见的STM32最小系统板原理图抄,也要重新核对每个网络标号是否真正落到了电源树或者GND网络上。
| 网络标号 | 连接要求 | 常犯错误 |
|---|---|---|
| VDDA / VSSA | 3.3V与地,靠近引脚放1uF+10nF | 悬空不接,导致ADC采样漂移 |
| NRST | 10kΩ上拉到3.3V,100nF对地 | 直接用导线接3.3V,无法复位 |
| BOOT0 | 10kΩ下拉到GND | 悬空后偶发进入ISP模式 |
| VCAP | 2.2uF电容到GND | 漏放则芯片无法启动 |
VCAP这一步最容易漏。F103系列内部稳压器需要外部电容,少了它芯片上电后不会稳定运行,而很多最小系统板原理图会把它藏在电源符号附近,不逐网络核对根本发现不了。
3.2 电源设计里的5V转3.3V稳压电路原理图细节
AMS1117-3.3的典型接法是输入端放10uF电解电容并联100nF陶瓷电容,输出端同样放10uF加100nF。输入输出电容不能只放一个,电解电容吸收低频纹波,陶瓷电容处理高频噪声,两者配合才能让语音模块在识别瞬间不出现电压跌落。这几乎就是一份完整的5v转3.3v稳压电路原理图模板,照着接就行,但要注意AMS1117的输入输出压差需要1V以上,输入5V输出3.3V刚好在裕量边缘,USB线过长时5V会掉到4.6V左右,3.3V输出就会跟着往下偏。
在布局上,舵机的5V电源线和信号地不要从MCU的GND网络下面穿过。舵机启停时地电位会跳动,轻则让语音识别误触发,重则让串口收到乱码。我的做法是模拟地AGND和数字地DGND在原理图上先分开画,最后在LDO输出电容处用0Ω电阻单点连接。
3.3 语音模块与MCU的接口电平匹配
LD3320支持并行和串行两种接口方式,课程设计里建议直接用UART模式,3.3V TTL电平与STM32的USART引脚同电平,不需要额外转换。SU-03T同样是TTL串口输出,但不同批次模组的电平标称值略有差异,接之前确认模块供电是3.3V还是5V版本。如果手头只有DB9座子和RS232电平设备,就得注意RS232串口通信原理图中的负逻辑:RS232用-3V到-15V表示逻辑1,跟TTL的正逻辑完全不兼容,必须加MAX3232做电平转换,直接接会把两边都烧掉。
另外LD3320的INTB中断输出引脚不能悬空,要加10kΩ上拉到3.3V。语音识别结束后芯片通过这个引脚通知MCU读取结果,如果原理图里忘了画上拉,程序里就会一直收不到识别完成标志。
3.4 舵机驱动不需要驱动芯片,但要处理电源去耦
舵机的信号线可以直接接MCU的GPIO,不需要额外驱动芯片,SG90内部已经带了控制电路。容易出问题的是电源:舵机启动瞬间电流会拉升几十毫秒,我一般在舵机座子旁边放一个100uF电解电容,再并一个100nF陶瓷电容,让启动电流就近取电,而不是从5V主线硬拉。信号线上串一个22Ω电阻能抑制走线过长带来的振铃,PCB上舵机插座离主控越近越好,线长超过10cm就可能出现PWM波形畸变。
3.5 多页原理图的页码和网络标号一致性检查
当原理图被拆成电源页、主控页、接口页之后,最容易犯的错就是不同页里同一网络的标号写得不一致。比如5V电源在电源页叫“VCC_5V”,到了主控页又写成“VCC5”,DRC查不出这类问题,但PCB生成网表后这路电源就是断的。用OrCAD Capture画多页图时还要逐页检查PageNumber,有2张或以上原理图页面时如果page number都设成了1,页码重复了,生成BOM和装配图后根本没法按页码定位器件位置。画完图后我会用Tools菜单下的Annotate重新排一次页码,再导出网表做一次全网络核对。
4. 智能语音垃圾桶源程序框架:语音命令解析与舵机PWM控制
源程序部分要解决三件事:稳定收到语音命令、把命令翻译成舵机角度、在关盖过程中不卡死串口。我用一个简单的状态机来组织主循环,配合串口中断接收语音模组上报的帧数据,这样代码结构清晰,也不会因为舵机转动时的延时阻塞而丢掉后来的语音指令。
4.1 状态机结构:不要把开盖动作写成一长串延时
很多初版程序会这么写:收到开盖指令,延时500ms,舵机转到开盖角度,再延时500ms……问题是舵机转动期间串口照样来数据,阻塞延时会让后续指令积压在缓冲区,人连着喊两次“开盖”时程序根本反应不过来。我习惯把整个过程拆成IDLE、OPENING、OPEN、CLOSING、BLOCKED五个状态,主循环只做状态判断,开盖和关盖过程用非阻塞方式推进,比如检查“当前舵机角度是否到达目标角度”。
IDLE -> 收到开盖命令 -> OPENING OPENING -> 舵机到达开盖位 -> OPEN OPEN -> 收到关盖命令 或 超时10秒 -> CLOSING CLOSING -> 红外被遮挡 -> BLOCKED BLOCKED -> 遮挡消失 -> CLOSING CLOSING -> 舵机到达关盖位 -> IDLE状态机的好处是每个状态都能独立处理超时和异常,后续要加“手动按键开盖”或者“满载提醒”都只需要在对应状态里加分支。
4.2 串口接收语音命令帧的标准写法
下面代码以“帧头0xAA + 命令字 + 校验”的短帧为例,不同语音模组的帧格式不一样,但接收思路是通用的:在中断里按字节收,帧头重置缓冲,收满一帧交给主循环处理。
#define FRAME_HEADER 0xAA #define FRAME_LEN 6 uint8_t rx_buf[FRAME_LEN]; uint8_t rx_idx = 0; uint8_t frame_ok = 0; void USART1_IRQHandler(void) { uint8_t ch = USART1->DR; // 读数据寄存器后硬件自动清RXNE if (ch == FRAME_HEADER) { rx_idx = 0; // 抓到帧头,重新开始缓存 } if (rx_idx < FRAME_LEN) { rx_buf[rx_idx++] = ch; } if (rx_idx == FRAME_LEN) { frame_ok = 1; // 一帧收满,交给主循环校验 } }主循环里处理frame_ok时,先确认帧头字节确实是0xAA,再根据命令字映射动作。比如命令字0x01对应开盖、0x02对应关盖、0x03对应停止。要注意校验和的计算方式必须和语音模组烧录工具里设置的保持一致,常见的是“除帧头外所有字节累加取低8位”。如果你用的是SU-03T,这个帧格式是在配套工具里自定义的,模块烧录后要和代码里的宏定义逐字节对一遍。
4.3 舵机PWM初始化与角度映射
使用STM32的TIM2通道1,配置成PWM模式1,输出周期20ms,占空比由CCR1寄存器控制。下面这段初始化代码基于寄存器直接操作,便于理解时钟和分频关系。
void TIM2_PWM_Init(void) { RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // 打开TIM2时钟 RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; // 打开GPIOA时钟 GPIOA->CRL &= ~(0xF << (0 * 4)); // 清空PA0配置 GPIOA->CRL |= (0xB << (0 * 4)); // PA0复用推挽输出 TIM2->PSC = 72 - 1; // 72MHz / 72 = 1MHz,计数周期1us TIM2->ARR = 20000 - 1; // 20ms周期,对应50Hz TIM2->CCR1 = 1500; // 默认1.5ms脉宽,约90度 TIM2->CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2; // PWM模式1 TIM2->CCER |= TIM_CCER_CC1E; // 使能通道1输出 TIM2->CR1 |= TIM_CR1_CEN; // 启动定时器 } void servo_set_angle(uint8_t angle) { if (angle > 180) angle = 180; TIM2->CCR1 = 500 + (uint32_t)angle * 2000 / 180; // 0度对应500us,180度对应2500us,线性映射 }PSC设为71之后,计数器每1us加1,CCR1的值直接就是脉冲宽度微秒数,这样便于换算角度。500到2500对应脉冲宽度0.5ms到2.5ms,角度与脉宽按线性关系映射即可。别忘了一个细节:SG90的脉宽范围因厂家而异,有些标称0.5ms到2.5ms,实际在两端会有咔咔声,说明已经顶到机械限位,这时应把角度映射范围缩到比如5度到175度,避免长期憋电流。
4.4 防夹逻辑与超时自动关盖
红外避障模块接一个GPIO,检测到遮挡时强制切到BLOCKED状态。这个状态要单独处理:不能直接关闭PWM输出,否则舵机失去保持力,桶盖会靠重力砸下来;正确做法是把目标角度维持在当前角度,等待遮挡解除后再恢复关盖流程。同时开盖状态持续超过10秒就自动执行关盖,防止人走开后桶盖一直敞着。这几个分支都在主循环的状态机里处理,逻辑上比中断里加延时干净得多。
5. 智能语音垃圾桶PCB文件布局、叠层与投板前检查
PCB文件是项目从“能跑”到“能生产”的分界线。原理图和源程序再完整,PCB布局混乱一样会让语音识别不稳、舵机工作异常。这一章从布局、叠层、设计规则到文件输出,讲清楚一块语音垃圾桶板子怎么画才不容易出问题。
5.1 布局顺序与关键器件摆放
我的布局顺序是先定接口方向:USB座、舵机排针、红外传感器排针全部朝板子同一侧,方便装入桶体后走线。然后放电源部分,再放主控,最后放语音模组。语音模组的麦克风位置要靠近板边并朝向外部,如果被板内走线或外壳遮挡,识别距离会明显缩短。舵机排针放板边还有个原因:舵机线是3Pin规格,插头带卡扣,如果放板子中间,桶盖开合时线束会顶到其他元件。
5层以上板子在常规智能小产品里很少见,普通双层板就能搞定这个项目。如果对信号质量有要求,可以选四层板,中间一层完整GND,语音模组下方的走线不穿越地平面,识别稳定度会好一个档次。网上有人问“5层板子”做这种项目行不行,实际上常规打样厂能稳定提供的是2层、4层、6层,5层是非对称压合,成本高且容易翘曲,没必要选。
5.2 设计规则给多少合适
| 设计项 | 推荐值 | 说明 |
|---|---|---|
| 信号线宽 | 0.2mm(约8mil) | 语音串口、PWM、GPIO走线 |
| 电源线宽 | 0.5mm以上 | 5V和GND主干道 |
| 线间距 | 0.2mm | 满足常规工艺,降低短路风险 |
| 过孔内径/外径 | 0.3mm / 0.6mm | 常规打样可做的最小规格之一 |
| 铜厚 | 1oz(35um) | 舵机瞬时电流足够 |
电源线宽度按电流估算:1oz铜厚下,1mm线宽约能走1A电流,SG90堵转瞬时电流虽然能到几百毫安,但那是短时值,0.5mm到1mm的电源线都扛得住。地网络尽量用铺铜覆盖,舵机插座下方不要用细走线拉地,直接通过过孔打到完整地平面。
5.3 Gerber文件和PCB文件的关系要提前搞清楚
投板前必须导出Gerber,但Gerber是只读的制造交付格式,不是可编辑文件。网上经常看到“gerber文件转pcb文件”“怎么通过gerber文件修改pcb”这类问题,实际做法是不要从Gerber反向转PCB,即使有工具能转,转出来的铜箔也全部丢失网络信息,没有DRC没有网表,改一个走线就得推倒重来。要改板、改丝印、加测试点,都回到自己手里的PCB源文件操作,Gerber只是最终输出物。
导出Gerber时至少检查这几层:Top Layer、Bottom Layer、Top Overlay丝印、Top/Bottom Solder阻焊、TopPaste助焊(需要贴片时选)、Drill Drawing/NCDrill钻孔文件。缺了钻孔文件,工厂连过孔都打不了,这种返工只要发生一次就足够让人长记性。
5.4 投板前的PCB文件检查清单
| 检查项 | 达标标准 |
|---|---|
| DRC检查 | 无断路、短路、间距报错 |
| 原理图与PCB对比 | 网络数量一致,无未连接引脚 |
| 丝印位号 | 不压在焊盘上,方向统一 |
| BOM清单 | 与原理图器件一一对应 |
| 元器件封装 | 麦克风、USB座、舵机排针方向正确 |
| 版图尺寸 | 能装进桶体结构,定位孔位置对得上 |
这些检查项里最容易忽略的是封装方向。USB座的封装画反、麦克风拾音孔朝向板内,都是拿到样品才发现的问题。我的习惯是PCB画完后把器件三维预览开起来,和实体结构件比对一遍再送打样。
6. 智能语音垃圾桶三份交付文件的相互校验技巧
拿到一套“智能语音垃圾桶”的交付包,不管是自己做的还是接手别人给的,先别急着烧程序。用下面这个顺序做一次三方校验,能省下大量后面排错的时间。这三份文件是互相咬合的:原理图定义引脚连接,源程序使用引脚,PCB落地引脚。任何一份和另外两份对不上,最后都得回到原理图重新查。
先对版本。我每次发版都会给三份文件统一命名,比如V1.0_SCH、V1.0_FW、V1.0_PCB,避免出现“原理图改了但源程序没改”这种半新半旧的交付包。如果版本号对不上,后面的校验没有意义。
再对引脚。源程序里的宏定义和原理图网络标号做一次文本比对,用脚本能快速找出不一致。
# 提取源程序里PA/PB引脚宏 grep -E "define.*(PA|PB|PC)[0-9]+" firmware/main.h | sort > /tmp/fw_pins.txt # 提取原理图导出网表里的网络标号 grep -E "PA[0-9]+|PB[0-9]+|PC[0-9]+" sch/trash_can.net | sort -u > /tmp/sch_pins.txt # 对比差异 diff /tmp/fw_pins.txt /tmp/sch_pins.txt执行完后看diff输出:如果只有宏注释行有差异,说明源程序和原理图实际引脚一致;如果同一功能的两边引脚号对不上,比如原理图里舵机接在PA0而代码里初始化的是PB1,那就要以原理图为准修正代码再烧录。这种问题用眼睛很难发现,但脚本几秒钟就出来了。
然后做一次快速上电验证。先不接语音模组,用串口调试助手手动发一帧“开盖命令字”,观察舵机是否按预期转动。这一步通过后,再接语音模组实测喊词条,最后用手遮挡红外探头,确认防夹分支生效。整套验证下来控制在20分钟内,超过这个时间就说明交付文件之间还有没对齐的地方,先回到引脚比对那一步重查。
最后把PCB文件打开做一次视觉检查:确认舵机插座旁边有电容、语音模组下方无长平行走线、丝印上标注了J1/J2这种接口编号。给每个接口标注名称看起来是小改动,但焊接和排查时能少翻几次原理图。做到这一步,三份交付物才算真正闭环。
本文还有配套的精品资源,点击获取