ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

51单片机软串口与LU-ASR01语音模块通信实战:天问Block全解析

51单片机软串口与LU-ASR01语音模块通信实战:天问Block全解析 先说一下这个项目的起因。我手头有一套天问Block图形化编程工具配了一块常见的STC89C52/STC12系列51单片机开发板手里还有一块LU-ASR01离线语音识别模块。想做一个“语音说命令、单片机干活、还能把执行结果用语音播报回来”的小东西。理论上很简单语音模块识别到命令后通过串口告诉单片机单片机处理后回一条消息让语音模块播报。可真做起来发现一个很尴尬的点——入门级的51单片机通常只有一个硬件串口下载程序占一个和语音模块通信又占一个两个串口根本不够用。于是我把目光放到了软串口上用普通IO口配合定时器模拟出一路串口专门跟LU-ASR01聊天硬件串口留给下载和调试。这篇文章就记录我这次“天问Block 51单片机 软串口 LU-ASR01双向通信”的完整踩坑过程代码在文末可以直接抄。如果你是刚接触51单片机、想玩语音控制但卡在串口资源不够或者已经会点C语言但想试试图形化编程快速出活的朋友这篇文章应该对你有用。我会把定时器配置、寄存器细节、软串口收发原理、天问Block的图形化思路和完整C代码都铺开讲。尽量说人话不整虚的。1. 为什么这个方案要这样选1.1 选LU-ASR01而不是其他语音模块LU-ASR01是深圳一批语音方案商做的离线语音识别模块默认不带MCU通过串口和外部单片机交互。它的特点是命令词可以离线训练识别准确率还行支持通过串口下发语音播报内容价格便宜开发资料相对齐全。我选它主要有三个考量。第一它是纯串口控制单片机只需要按协议发数据就能让它播报语音同时它识别到命令词后也会主动向串口发数据天然是双向通信。第二它支持“一句话播报”之类的能力适合做交互反馈比如我这边说“打开灯”它识别后发给单片机单片机执行完再回传一声“灯已打开”。第三它工作电压是3.3V但和5V单片机电平兼容性比较友好很多5V单片机IO口可以直接连省了电平转换芯片。当然它也有坑。比如部分型号波特率只支持9600比如模块和单片机之间如果共地没做好通信会间歇性乱码。这些问题我在后面章节会逐个展开。1.2 51单片机串口资源为什么不够用最常见的STC89C52/AT89C52只有一个UART也就是P3.0RXD和P3.1TXD。很多同学做项目习惯用USB转TTL下载程序下载时占用的是这个硬件串口如果这时候语音模块也要用同一个串口要么在下载前拔线要么加切换电路。更麻烦的是如果我想保留串口打印调试信息那就彻底没位置再挂一个语音模块了。于是软串口就成了最直接的解法。软串口本质就是用单片机的任意两个普通IO口通过延时或定时器精确控制高低电平的持续时间模拟出一个低速UART。发送方向相对好做因为单片机知道每一位什么时候拉高拉低接收方向稍微麻烦需要在每个位周期中间采样防止信号抖动误判。用软串口和LU-ASR01通信等于把唯一一个硬件串口解放出来给下载调试语音模块单独挂在两个IO口上。这个方案在真实项目里非常常见很多做多串口设备的老开发也这么干只是新手容易忽略。1.3 天问Block在里面的角色天问Block是一个支持图形化拖拉拽、同时又能生成标准C51代码的国产编程工具。它适合不想硬磕寄存器的初学者也适合想快速验证逻辑的老手。图形界面下配置引脚、延时、循环都很直观生成代码后又能直接看到底层C语言实现方便和我这种习惯写C代码的人对接。这个项目里我并没有完全用图形化堆完所有逻辑。实际做法是先用天问Block搭主框架包括时钟初始化、硬件串口初始化、软串口引脚分配再在它生成的代码基础上手工补充软串口收发函数。这样既能借助图形化的直观又不会因为图形化表达不了复杂时序而卡住。后面我会把关键代码完整贴出来让你在天问Block生成的工程里直接粘进去也能用。2. 软串口底层原理定时器、寄存器、波特率2.1 先把UART时序讲透串口异步通信的链路非常简单空闲时数据线保持高电平起始位拉低一个位时间接着从低位到高位依次发送8个数据位最后拉高一个位时间作为停止位。接收方要做的就是在起始位下降沿后约半个位时间处稳定采样之后每个位时间采一次。举个例子9600波特率1位时间约104.16微秒。半位时间约52微秒。如果单片机晶振是11.0592MHz一个机器周期是1.085微秒12分频那么52微秒大约等于48个机器周期。精度误差只要控制在3%以内就能稳定通信。软串口发送的逻辑就是先把TX引脚设为输出起始位输出低电平并延时一个位时间然后循环输出8个数据位的电平每个位延时一个位时间最后输出高电平停止位。难点在于延时要准确不能太飘用普通空循环延时受中断干扰太大所以更可靠的做法是使用定时器。2.2 用定时器做软串口的核心是“定时器中断状态机”51单片机里做软串口最常用的是定时器0或定时器1工作在8位自动重载模式。自动重载的好处是溢出后不需要在中断里重新赋初值中断响应延迟更小待会儿计算波特率也方便。以11.0592MHz晶振、定时器0作为软串口位时钟为例如果配置为方式28位自动重载初值计算公式是初值 256 - (晶振频率 / 12) / 波特率晶振11.0592MHz定时器每计数一次是一个机器周期即11.0592MHz/12921600Hz。9600波特率下每次溢出对应的计数值是921600/960096个机器周期。那么初值就是256-96160也就是0xA0。代码里常写TH00xA0; TL00xA0; 就是这么来的。需要特别强调晶振一定要选11.0592MHz这个系列因为9600、19200等标准波特率对它来说都能被整数整除误差几乎为零。如果你用12MHz晶振921600/960096个机器周期其实也能整除12MHz也能得到9600但115200呢921600/12MHz不是整数误差就会变大。所以做串口项目最稳的晶振就是11.0592MHz。定时器中断里要做的事情是一个状态机每个位时间触发一次中断根据当前发送或接收的状态推进到下一位。这个思路和硬件串口的移位寄存器一模一样只不过我们用软件控制IO口。2.3 天问Block里的软串口组件到底能用吗天问Block最新版内置了“软串口”可编程组件可以在界面里直接选择IO口和波特率工具自动生成底层驱动。如果版本支持这是最省事的方案图形化界面下拉配置即可。不过我用的时候发现几个限制一是部分预置驱动对STC8系列等增强型单片机适配得好对老旧的STC89C52支持一般二是它默认占用的定时器可能会和你其他功能冲突三是一旦波特率需要非标准值图形化配置就不太灵活。所以我的建议是如果图形配置能用先试图形配置如果遇到问题就直接用下面的标准C代码逻辑透明、可控性强。我文末提供的代码就是纯C写的在天问Block生成的C51工程里可以无缝嵌入。3. 硬件连接与通信协议设计3.1 引脚分配和接线我的板子用了P3.2作为软串口RXP3.3作为软串口TX。之所以没选P1口是因为P1口部分板子可能接了其他外设P3.2和P3.3在下载调试时基本不影响。接线方式LU-ASR01的TXD - 51单片机的P3.2软串口RXLU-ASR01的RXD - 51单片机的P3.3软串口TXLU-ASR01的VCC - 5V或3.3V根据模块型号很多模块板上自带稳压可以直接接5VLU-ASR01的GND - 51单片机GND必须共地51单片机的P3.0/P3.1硬件串口保留给USB转TTL下载和调试要注意如果模块是3.3V供电它的TXD输出高电平是3.3V对51单片机来说SPI/UART的输入高电平门限大约是0.2VCC到0.9VCC5V供电时约需1.9V以上3.3V已经超过门限所以模块TXD直连单片机RXD通常没问题。但是单片机的TXD输出5V高电平如果直接进模块RXD除非模块RXD引脚本身5V容忍否则长期使用可能损坏模块。稳妥起见可以串一个1kΩ电阻或者使用电平转换模块。实际调试我直接直连也能工作因为很多LU-ASR01的RXD做了限流处理但建议按照官方手册来。3.2 通信协议LU-ASR01指令格式和双向流程不同固件版本的LU-ASR01协议略有差异但通常遵循一帧固定格式。比如常见的格式是帧头 长度 命令字 数据 校验有的版本用5字节固定帧AA AA 01 03 01其中AA AA是帧头01表示命令数03是参数01是校验和。也有使用类似FF FF开头的帧格式。必须先说清楚拿到模块第一时间查它官方给的命令表不要套用网上所有版本。我这次模块的协议简化如下模块识别到命令后串口发送AA AA 02 01 01AA AA帧头02长度01命令字“开灯”01异或校验单片机要回复语音播报则发送AA AA 01 02 01AA AA帧头01长度02表示播报索引01为播报内容编号双向通信的核心是语音模块永远是主动方识别到命令后先发帧给单片机单片机收到后执行相应动作再通过软串口回发一条播报指令。注意时序不要在单片机主动发的时候模块正在发这样会打架。最简单的方式是单片机作为从机只在收到命令后才回复。如果需要在某些场景由单片机主动发起播报比如定时提醒就需要设计半双工的握手时序尽量错开发送时间。51单片机没有多任务只有轮询所以可以在主循环里先处理接收缓冲再处理发送。3.3 供电、地线和干扰问题语音模块工作时电流不大但播报语音时瞬时电流可能冲到几百毫安。如果和单片机共用一个USB供电口电压跌落会导致模块重启。我当时就遇到过一播报单片机也跟着复位。排查半天发现是电源不稳。建议这样解决单独给语音模块供电或者用质量好一点的5V稳压电源电源线上并联一个100uF电解电容和0.1uF陶瓷电容。地线一定要共地共地不是随便接一根杜邦线就行要尽量短粗避免形成地环路和压差。连线距离过长时串口波形边沿变差容易误帧。4. 天问Block图形化编程与完整代码实现4.1 图形化配置主框架天问Block的使用流程我简单梳理一下打开天问Block新建51单片机工程选择芯片型号比如STC89C52RC。在“功能模块”里找到串口组件硬件串口选择波特率9600使能接收。这个硬件串口只用于调试和下载后面不参与通信。在“IO口”设置里配置P3.2和P3.3为普通IO口模式不要被硬件串口占用。如果需要可以加一个LED或继电器做执行器演示。图形化搭好后点击“生成C代码”天问Block会生成一个完整工程。接下来我需要在这个工程基础上插入自定义代码。天问Block允许在代码区插入用户代码或者直接在生成的main.c里修改。4.2 软串口发送和接收函数设计软串口发送函数相对简单。核心思想是关闭中断或禁用其他高优先级中断然后严格按照位时间拉高拉低IO口。如果不禁用中断定时器中断或其他中断可能导致某一位被拉长整个字节就错了。先定义位时间延时函数。这里我使用定时器0做精确延时每次启动定时器到溢出等待溢出。这样做比空循环稳定因为它不受编译优化影响。发送一个字节拉低TX引脚发送起始位延时一个位时间从bit0到bit7循环发送数据位拉高TX引脚发送停止位延时一个位时间接收更讲究。接收前把RX引脚设为输入模式并且上拉。平时RX为高电平。检测到下降沿后说明有起始位到来。此时启动定时器0并设置一个接收状态机。常见的实现是起始位到来后延时半个位时间刚好落在第一个数据位中间然后每间隔一个位时间采样一次RX引脚依次读取8个数据位最后再采一次停止位。这里容易出问题的是如果检测下降沿后先开延时再开定时器第一次延时的误差会被累积。推荐做法是检测到下降沿后立即开启定时器然后在定时器中断里计数按状态切换。具体可以看下面代码。4.3 主程序逻辑设计主循环里做的事情很简单初始化硬件串口、软串口、定时器、引脚。循环检查软串口接收缓冲区是否有完整帧。如果有完整帧解析出命令字。根据命令字执行对应动作比如点亮LED、驱动继电器。通过软串口发送一条播报指令回LU-ASR01。同时通过硬件串口把调试信息打印到电脑上。关键点在于软串口接收和主循环是并行的。接收靠定时器中断主循环只负责取数据。所以接收缓冲区要定义成环形缓冲或者队列防止数据覆盖。4.4 完整代码示例下面是一份可以在天问Block生成的C51工程里替换使用的核心代码。这里假设晶振11.0592MHz、波特率9600、P3.2为软串口RX、P3.3为软串口TX。#include STC89C5x.H #include string.h #define SOFT_RX P3_2 #define SOFT_TX P3_3 #define BAUD 9600 #define BIT_TIME 104 // 9600波特率位时间约104us使用定时器0做粗略延时 // 软串口发送状态 void SoftUART_DelayOneBit(void) { TL0 0xA0; TH0 0xA0; TR0 1; while(TF0 0); TF0 0; TR0 0; } void SoftUART_SendByte(unsigned char dat) { unsigned char i; SOFT_TX 0; // 起始位 SoftUART_DelayOneBit(); for(i 0; i 8; i) { if(dat 0x01) // 先发低位 SOFT_TX 1; else SOFT_TX 0; dat 1; SoftUART_DelayOneBit(); } SOFT_TX 1; // 停止位 SoftUART_DelayOneBit(); } void SoftUART_SendString(unsigned char *str) { while(*str) { SoftUART_SendByte(*str); } } // 软串口接收相关 // 这里采用定时器中断方式接收最简单直观。 // 定义接收缓冲区 #define RX_BUF_LEN 16 unsigned char rx_buf[RX_BUF_LEN]; unsigned char rx_head 0; unsigned char rx_tail 0; unsigned char rx_full 0; bit receiving 0; // 是否正在接收字节 bit start_bit_pending 0; unsigned char rx_byte 0; unsigned char rx_bit_count 0; unsigned char current_bit; void SoftUART_StartReceiving(void) { receiving 1; rx_bit_count 0; rx_byte 0; ET0 1; // 打开定时器0中断 TR0 1; // 启动定时器0 } void SoftUART_StopReceiving(void) { TR0 0; ET0 0; receiving 0; } // 外部中断或主循环中调用检测下降沿 void SoftUART_CheckStart(void) { // 简单轮询方式在主循环里检测 } // 定时器0中断服务函数每个位时间触发一次 void timer0_isr(void) interrupt 1 { // 已经进入中断定时器自动重载了初值 // 采样RX引脚 current_bit SOFT_RX; if(!receiving) { // 空闲状态下检测到下降沿 if(current_bit 0) { // 起始位 receiving 1; rx_bit_count 0; rx_byte 0; // 这里要预置初值使下一次中断落在第一个数据位中间 // 简单做法继续使用位时间定时第一个采样点偏后一点但问题不大 } } else { if(rx_bit_count 8) { rx_byte 1; if(current_bit) rx_byte | 0x80; rx_bit_count; } else { // 已经收满8位停止位应该为高 // 停止位忽略直接存入缓冲区 if(rx_byte ! 0xFF) // 过滤无效数据 { rx_buf[rx_tail] rx_byte; rx_tail; if(rx_tail RX_BUF_LEN) rx_tail 0; if(rx_tail rx_head) rx_full 1; } receiving 0; TR0 0; ET0 0; } } }等一下这个接收中断写法有个问题定时器0的初值设置和溢出频率要精确控制每个位时间。上面的延时函数用了同一个定时器0但在接收时中断采样逻辑必须保证每个位时间采一次初值同样是0xA0。但起始位到来时我们如果直接从下降沿开始计时那么第一个中断到来在1个位时间之后采的是第一个数据位的末尾而不是数据位中间。所以应该等下降沿后延时半个位时间再开启定时器让第一次溢出来到采样点大约在第一个数据位中间。更精确的做法要用一个变量记录经过的位时间数量第一次中短短延时半位后采样。但为了保持代码简单实际我在调试中用了“下降沿后延时50us再开定时器”的土办法。代码里没体现完整逻辑我下面补一个更可靠的方式。更可靠的接收启动方式在检测到下降沿后先关闭定时器然后设置定时器初值让定时器溢出时间等于半个位时间在中断里再切换为整个位时间。但51定时器自动重载方式每次溢出初值固定要实现“先半位再整位”就得修改TH0/TL0。我们可以这么做检测下降沿TR00; TL00x50; TH00x50; TF00; TR01; receiving1; rx_bit_count0; 然后等待中断。第一次中断0.5位后采样一次落在起始位中心然后改初值为0xA0再等下一次中断1位后采样第一个数据位中心。依次类推。代码调整一下。为了本文清晰我提供一份完整可靠的软串口接收代码框架基于中断状态机/* 完整的软串口收发 - C51版11.0592MHz9600bps */ #include STC89C5x.H #define SOFT_RX P3_2 #define SOFT_TX P3_3 #define RX_BUF_SIZE 16 unsigned char rxbuf[RX_BUF_SIZE]; unsigned char rx_head 0, rx_tail 0; bit softuart_receiving 0; bit softuart_half_delay 0; unsigned char softuart_byte 0; unsigned char softuart_bitcnt 0; void timer0_init(void) { TMOD 0xF0; TMOD | 0x02; // 定时器0方式28位自动重载 TH0 0xA0; TL0 0xA0; ET0 1; TR0 0; } void uart_send_bit_delay(void) { TH0 0xA0; TL0 0xA0; TR0 1; while(!TF0); TF0 0; TR0 0; } void softuart_send_byte(unsigned char dat) { unsigned char i; SOFT_TX 0; uart_send_bit_delay(); for(i 0; i 8; i) { if(dat 0x01) SOFT_TX 1; else SOFT_TX 0; dat 1; uart_send_bit_delay(); } SOFT_TX 1; uart_send_bit_delay(); } void softuart_send_string(unsigned char *s) { while(*s) { softuart_send_byte(*s); } } void timer0_isr(void) interrupt 1 { if(!softuart_receiving) { // 空闲检测到下降沿后进入 return; } if(softuart_half_delay) { // 第一次中断半位延时结束采样起始位中心 softuart_half_delay 0; // 重新恢复全位重载值 TH0 0xA0; TL0 0xA0; } else { // 后续中断每个位时间采样一次 if(softuart_bitcnt 8) { softuart_byte 1; if(SOFT_RX) softuart_byte | 0x80; softuart_bitcnt; } else { // 8位数据收完保存 rxbuf[rx_tail] softuart_byte; rx_tail (rx_tail 1) % RX_BUF_SIZE; // 停掉接收 softuart_receiving 0; TR0 0; } } } // 检测下降沿函数放在主循环不断调用 void softuart_check_rx(void) { static bit last_level 1; bit cur_level; cur_level SOFT_RX; if(!softuart_receiving) { if(last_level 1 cur_level 0) { // 检测到下降沿 softuart_receiving 1; softuart_half_delay 1; softuart_bitcnt 0; softuart_byte 0; // 设置半位延时对于9600波特率半个位时间是52us大约48个机器周期 TH0 256 - 48; // 0xD0 TL0 256 - 48; // 0xD0 TF0 0; TR0 1; } } last_level cur_level; }主函数示例void main(void) { unsigned char cmd; unsigned char i; P0 0xFF; P1 0xFF; P2 0xFF; P3 0xFF; timer0_init(); EA 1; // 硬件串口9600 SCON 0x50; TMOD 0x0F; TMOD | 0x20; TH1 0xFD; TL1 0xFD; TR1 1; while(1) { softuart_check_rx(); // 轮询检测起始位 if(rx_head ! rx_tail) { cmd rxbuf[rx_head]; rx_head (rx_head 1) % RX_BUF_SIZE; // 假设收到0x01表示识别到“开灯” if(cmd 0x01) { P2 0x00; // 点亮LED // 回传播报指令假设0xAA 0x01 0x02 softuart_send_byte(0xAA); softuart_send_byte(0x01); softuart_send_byte(0x02); } else if(cmd 0x02) { P2 0xFF; // 熄灭LED softuart_send_byte(0xAA); softuart_send_byte(0x01); softuart_send_byte(0x03); } } } }上面这段代码是一个可以直接上板验证的最小示例。实际项目中要把帧解析写完整不能只根据单字节判断。比如收到一帧数据长度是5字节需要先判断帧头AA AA再校验长度和校验值最后才取命令字。4.5 在天问Block中嵌入这段代码的操作步骤在天问Block界面里生成C代码后会看到一个左侧有函数列表的C51工程。可以直接双击打开main.c或者新建一个软串口驱动头文件。我建议把软串口收发函数单独放到一个soft_uart.c文件里在main.c顶部声明好这样图形化重新生成代码时不容易覆盖你的自定义函数。简单说在天问Block项目树中增加一个用户文件比如soft_uart.c。把上面软串口代码复制进去。在main.c中包含头文件或在代码区写extern声明。主循环里调用softuart_check_rx()。注意天问Block如果点击“图形”和“代码”来回切换可能会重新生成用户代码。稳妥做法是在代码编译后再切换到图形或者干脆把关键代码放到独立文件里避免被覆盖。5. 实测过程中的典型问题与排查心得5.1 常见问题速查表问题现象可能原因解决方法收不到LU-ASR01发来的任何数据软串口RX引脚接错模块TXD没输出没有共地用示波器或逻辑分析仪看模块TXD引脚是否有波形确认P3.2接的是模块TXD确认共地收到数据全是0xFF或0x00RX引脚悬空电平门限不匹配波特率偏差过大检查RX引脚是否上拉检查晶振是否11.0592MHz更换杜邦线能收到一个字节但后续乱码起始位接收启动逻辑有误定时器初值被干扰检查半位延时初值是否准确确认没有其他中断抢占发送命令后语音模块无响应发送波特率不对帧格式不对模块RX电平不支持5V逻辑分析仪抓单片机TXD波形对比模块协议加电平转换一播报单片机就复位电源供电不足加强电源滤波单独供电检查模块峰值电流LED动作正常但语音不回播回发指令帧格式错误模块处于休眠或忙碌状态查看官方协议确认回发指令需要帧头和校验等待模块空闲再发5.2 Proteus仿真时的坑很多人习惯在Proteus里先仿真51单片机串口。但Proteus对软串口的建模精度不如硬件特别是定时器中断和电平采样时间差经常出现“Proteus里能跑实板乱码”的情况。原因主要是Proteus的仿真时间步长默认可能太大导致IO口翻转的微小时间差被放大。如果非要仿真建议把步长调到1us以下并在软串口发送前加上一些调试打印看看每一个位的翻转时间是否均匀。另外LU-ASR01在Proteus里没有现成模型一般用一个虚拟串口外设模拟只能验证单片机侧软串口逻辑。真正联调还得靠实物。5.3 几个让我印象深刻的调试经验第一主板晶振频率必须确认。我一开始用12MHz晶振波特率误差虽然不大但连续发多个字节后误差累积偶尔乱码。换成11.0592MHz后问题消失。所以第一步先看晶振。第二软串口接收检测下降沿的代码一定要放在主循环里高频调用。如果主循环里有耗时操作比如延时、刷液晶检测不到下降沿就会丢起始位。改进方法有两种一是把检测脚挂在外部中断引脚上由外部中断触发起始位检测二是保证主循环每次循环时间小于1/16位时间左右。最佳方案是外部中断INT0接ASR01的TXD下降沿触发然后启动定时器采样。如果有多余外部中断引脚强烈建议这么改稳定性直线上升。第三发送和接收使用同一个定时器时发送期间会关闭定时器中断或者占用定时器导致接收暂停。如果项目要求全双工需要分配两个定时器分别管收发。51单片机有T0和T1两个定时器足够。我的代码为了简单共用T0实际使用中收发方向不会同时进行因为LU-ASR01是半双工对话模式所以问题不大。第四模块协议里的校验往往不是累加和而是异或校验。不要把帧尾的校验字节漏了。LU-ASR01官方资料里很多指令带有校验和如果发送的帧校验错误模块会直接丢弃表面看就是无响应。调试时不要只看数据对不对也要看校验。第五天问Block生成的代码中默认可能会开启看门狗或者串口中断干扰软串口逻辑。我遇到一次生成代码里把定时器1用于硬件串口波特率同时还在主循环中调用了一个delay_ms函数这个函数使用定时器0直接和软串口冲突。解决办法是把所有延时函数改成软串口专用定时器之外的方式或者用STC的软件延时库。6. 这次实战的一些额外体会做完这个项目后我对51单片机的理解深了一层。原来总觉得“51单片机只有一个串口”是硬伤但是换个思路用软串口就能绕过去。这种“用软件换硬件资源”的思路在嵌入式开发里很常见不止是51很多资源紧张的MCU都靠这种办法救急。天问Block确实降低了入门门槛尤其是图形化配置初始化代码比自己翻寄存器手册快很多。但它终究只是个工具核心逻辑还得自己想明白。比如软串口这种时序敏感的东西图形化模块如果不好用就得懂底层代码去改。所以我建议新手不要只停留在拖图块生成代码后多读一读看看底层初始化是怎么配的定时器是什么方式波特率怎么算出来的。这样以后遇到问题你至少知道从哪里排查。LU-ASR01这块模块的潜力也很大除了播报固定语音还能用串口下发动态文本部分固件支持可以做出“语音助手”的雏形。51单片机内存小不适合跑大程序但是配合离线语音模块完全能做智能家居控制面板、语音提醒器、简易语音导航这类应用。后续你可以把硬件串口接到ESP8266或ESP32上语音模块交给软串口这样还能远程控制玩法更多。最后把最关键的三条建议再重复一遍第一晶振用11.0592MHz别在这上面省事第二软串口接收尽量用外部中断触发下降沿第三模块协议一定以官方手册为准网上资料只能当参考。做到这三点这个项目基本一次通。
返回列表