ARTICLE DETAIL

资讯详情

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

STM32F4+INMP441数字麦克风I2S音频采集与双缓冲DMA实现

STM32F4+INMP441数字麦克风I2S音频采集与双缓冲DMA实现 做音频采集这个方向我把市面上常见的模拟麦克风方案都试了个遍电路噪声、运放增益、偏置电阻每一步都在跟模拟电路搏斗。后来换成INMP441这颗I2S数字MEMS麦克风一下就清爽了——音频数据直接以数字信号从I2S接口送进STM32F4不需要运放、不需要调理电路再配合双缓冲DMA机制CPU几乎不用管底层采样能做到连续不断、无等待地采集音频数据。这篇就把我完整跑通的方案记录下来包括硬件接线、CubeMX配置、双缓冲DMA的核心原理、完整代码以及我在调试过程中踩过的大小坑。1. 项目概述我要解决的到底是什么问题1.1 音频采集方案的演变从模拟到数字以前做语音识别或者声控小项目最常用的是驻极体麦克风加上运放放大再用STM32内置ADC采样。听起来简单实际做起来坑非常多驻极体麦克风输出只有几毫伏到几十毫伏需要至少几十倍放大而运放电路一旦布局不合理就会引入50Hz工频干扰、电源纹波噪声增益调大了直接自激啸叫调小了又采不到有效语音再加上ADC采样率受限于定时器和DMA配置想做到16kHz甚至48kHz的稳定采样还要自己折腾很久。后来我接触到INMP441这类数字MEMS麦克风它内部集成了MEMS传感单元、放大器和模数转换器直接输出符合I2S协议的24位数字音频数据。MCU端只需要用I2S外设接收不需要关心模拟信号的处理。整条链路从“模拟信号采集”变成了“数字信号流接收”开发难度和调试工作量都大幅下降音频质量和一致性反而更好。1.2 为什么是INMP441 STM32F4 双缓冲DMA很多人会问STM32F4跑音频采集选择方案时第一反应可能是“ADC DMA”为什么偏偏选I2S接口的INMP441原因有三点。第一STM32F4内部自带I2S外设而且SPI接口可以复用为I2S不需要额外扩展芯片。F4全系列基本都有2个I2S外设主模式、从模式、全双工都可以支持硬件上非常合适。第二INMP441的性价比高信噪比能做到61dB灵敏度为-26dBFS支持从8kHz到48kHz采样率用在语音识别、噪声检测、频谱分析这些场景完全够用。更关键的是它只有一颗芯片大小贴片封装占板面积非常小。第三双缓冲DMA可以把“采集”和“处理”彻底解耦。DMA在后台把INMP441发出的音频数据源源不断地搬进内存搬完半块缓冲区就触发一次中断CPU在中断里知道哪半个缓冲区的数据是新鲜的拿过来处理就行。这样采集过程不阻塞CPU主循环还能干别的事真正实现边采边处理。1.3 这篇文章适合谁能获得什么如果你是下面这几类人可以重点参考这份实战记录正在做语音识别、声控开关、声音频谱分析项目的嵌入式工程师刚接触STM32的I2S外设和DMA想找一个能直接跑通的完整示例想把音频采集做到低延迟、高稳定不想在模拟电路上反复调试的同学。这篇文章会从原理、硬件、软件、代码、排障五个维度完整展开文中的代码基于STM32CubeMX生成在STM32F407VET6上实测通过你可以直接移植到其他F4系列芯片上。2. 把原理先讲透INMP441、I2S和双缓冲DMA2.1 INMP441麦克风的关键参数INMP441是楼氏电子推出的数字MEMS麦克风采用I2S接口输出下面是几个关键参数做项目前最好心里有数。参数典型值说明信噪比SNR61dB语音采集够用低于高端的模拟MEMS麦但数字输出抗干扰能力强灵敏度-26dBFS在94dB SPL声压输入时输出约为满量程的1/20输出数据格式24位二进制补码高位在前MSB-first支持采样率8kHz - 48kHz配合I2S主时钟可灵活配置电源电压1.8V - 3.3V常用3.3V功耗1.4mA左右低功耗场景也适用封装3.35mm × 2.5mm × 0.88mm LGA体积很小适合贴片生产数据手册上还有一个重要信息INMP441支持L/R引脚选择输出声道当L/R引脚接地时数据在I2S的WS低电平期间输出也就是左声道当L/R引脚接VDD时数据在WS高电平期间输出也就是右声道。这个细节在接线时经常被忽略后面的排障章节会专门展开。2.2 I2S协议SCK、WS、SD三条线如何配合I2S是飞利浦制定的数字音频传输协议总线上一共三条主要信号线SCK也叫BCLKBit Clock位时钟每一位数据对应一个SCK周期WS也叫LRCKWord Select声道选择低电平表示左声道高电平表示右声道SD也叫DOUT串行数据线按位输出音频采样值。在飞利浦标准I2S格式下WS信号比数据提前一个时钟周期变化也就是D触发器延迟一拍数据从WS变化后的第二个SCK上升沿开始传输MSB先出。INMP441完整支持飞利浦I2S标准所以使用它的默认配置时STM32F4的I2S外设也必须选Philips标准。一个I2S帧包含一个左声道数据和一个右声道数据每个声道的数据宽度可以配置为16位、24位或32位。我们以16kHz采样率、24位数据格式举例SCK频率大约是采样率乘以声道再乘以位深也就是16kHz × 2 × 64 2.048MHz这里64是考虑了左右声道各占32位时隙。WS频率则等于采样率即16kHz。2.3 双缓冲DMA到底“双”在哪里很多初学者看到“双缓冲DMA”这个名字以为STM32F4的DMA会提供一个原生双缓冲模式。实际上F4的DMA确实支持真正的双缓冲寄存器模式但工程实践中更常见的做法是用DMA的循环模式配合半传输中断和全传输中断逻辑上实现双缓冲。具体来说我申请一整块DMA缓冲区比如256个采样点把它从中间切开前半部分128个点后半部分128个点。DMA以循环模式连续接收I2S数据当它写完前半部分时触发半传输中断继续写后半部分写完时触发全传输中断。这样两个中断交替触发每次触发都代表“有一半缓冲区的数据是刚刚采集好的”。为什么这种机制能做到“零延迟”可以类比接力比赛DMA就像一个不停奔跑的运动员每跑完半圈就吹一声哨子CPU听到哨子就知道“上一半跑道的数据可以拿走了”。DMA继续跑下一半CPU处理上一半两边互不等待。对比单缓冲区方案必须等一整块缓冲区写满DMA才停这段时间里CPU只能干等采集链路就卡住了。双缓冲机制让DMA永远有地方写数据CPU永远有新鲜数据可处理所以叫零延迟——更准确地说是无卡顿、无空档的连续采集。这里要诚实说明一下“零延迟”不等于采样瞬间CPU就能拿到数据整个链路里还是有半缓冲区长度的时间延迟但这是连续的、可计算的固定延迟不会出现由于DMA停止带来的额外丢帧和等待。实际系统中这个延迟通常在几毫秒到十几毫秒量级完全可接受。3. 硬件准备与接线别让电源噪声毁了你3.1 元器件清单做这个项目需要的元器件非常少这也是数字麦克风方案比模拟方案舒服的地方。STM32F4开发板一块我用的是STM32F407VET6核心板INMP441麦克风模块或裸芯片买模块的话板上已经焊好电容电阻直接用就可以ST-Link或J-Link调试器用于下载和调试程序杜邦线若干建议用短线减少外部干扰一台能输出音频的手机或信号发生器用于产生测试音频电脑端的串口助手方便把采集到的数据发回电脑分析。这里建议采购“INMP441模块”因为裸芯片引脚间距很小手工焊接不方便模块上一般会引出一排2.54mm间距的排针还能顺便带上电源滤波电容对新手友好很多。3.2 接线步骤和引脚对照我使用的是STM32F407VET6它有两个I2S外设I2S2挂在APB1总线上引脚对应关系如下INMP441引脚功能STM32F4引脚VDD电源正极3.3VGND电源地GNDSCK位时钟BCLKPB13I2S2_CKWS声道选择LRCKPB12I2S2_WSSD串行数据输出PB14I2S2_SDL/R左右声道选择GND选择左声道接线顺序建议先从电源接起再接三条信号线最后接L/R选择引脚。如果使用其他F4型号I2S外设的引脚映射可能不同请以数据手册的引脚复用表为准。用CubeMX配置时图形化的引脚分配也可以帮你自动锁定正确的引脚。3.3 电源滤波和布局上的注意事项INMP441本身功耗不大但它内部集成了ADC对电源纹波比较敏感。实测下来直接从开发板的3.3V引脚取电通常没问题但如果在同一个电源网络上还有电机、继电器这些大电流设备建议单独给麦克风供电或者在麦克风的VDD和GND之间加上100nF和10uF两个滤波电容一颗靠近VDD引脚一颗靠近GND引脚。另外INMP441的SCK和WS是由STM32F4的I2S主模式生成的SD线上的数据随SCK同步变化属于典型的同步数字接口抗干扰能力比模拟信号强得多。但三条信号线如果走线太长也会出现时序问题杜邦线控制在5厘米到10厘米以内比较稳。我之前试过用20厘米的杜邦线连接SCK频率到2MHz以上时偶发数据错位换成短线后问题消失。还有一点容易被忽略INMP441模块的SD引脚默认情况下是高阻态输出建议加一颗10kΩ左右的上拉电阻到VDD确保空闲时电平稳定。很多成品模块已经集成自己画板时务必加上。4. 软件配置CubeMX初始化I2S与DMA4.1 时钟配置与PLLI2SSTM32F4的I2S外设时钟来源比较特殊它不会直接用APB1总线的时钟作为I2S位时钟而是需要通过PLLI2S或系统时钟分频得到。原因很简单音频采样率需要非常精确的时钟比如16kHz、44.1kHz、48kHz只用APB1分频很难得到准确的频率。在CubeMX的Clock Configuration页面里需要做两件事第一确认PLLI2S被使能一般会设置PLLI2SR的值。以我使用的8MHz外部晶振为例CubeMX会自动根据目标采样率计算出合适的PLLI2SN、PLLI2SQ、PLLI2SR组合。这块不用过于纠结具体数值CubeMX会自动保证I2SCLK是音频采样率的整数倍。第二在I2S2的配置里设置Audio Frequency为目标采样率。我这里是16kHzCubeMX自动计算分频系数后在系统初始化代码里会看到PLLI2S相关的时钟初始化逻辑。如果以后要跑44.1kHz或48kHz采样率直接在CubeMX里改Audio Frequency重新生成代码即可不用手动算分频这是用CubeMX最大的便利。但如果你习惯手写寄存器就需要参考参考手册里的I2S时钟分频公式自己算I2SDIV和I2SODD这部分比较繁琐容易出低概率错误不推荐在这个项目里手算。4.2 I2S外设参数配置在CubeMX的左侧Pinout Configuration里找到SPI2把它的工作模式切换为I2S2。注意不是选SPI模式而是I2S模式。我使用的I2S参数如下配置项值说明ModeMaster Receive主模式接收由F4生成SCK和WSStandardPhilips Standard匹配INMP441的I2S协议Data Format24-bitINMP441输出24位数据Audio Frequency16kHz项目需要的采样率MCLK OutputDisableINMP441不需要MCLK时钟Clock PolarityLow与INMP441时序匹配这里有一个特别容易踩的坑INMP441不需要MCLK信号如果你在CubeMX里把MCLK Output设为EnableI2S会在PB15引脚上输出一个MCLK时钟并不会影响INMP441的采样但会让I2S的时钟分频计算变复杂而且MCLK信号本身可能通过布线耦合到SD或WS线上造成噪声。实测下来关闭MCLK输出后数据稳定性更好所以这个选项要设为Disable。4.3 DMA双缓冲的配置方法CubeMX中配置DMA是在I2S2的DMA Settings页面。给I2S2_RX添加一个DMA通道参数设置如下DMA配置项值说明DirectionPeripheral To MemoryI2S数据寄存器到内存PriorityVery High音频数据实时性要求高给最高优先级ModeCircular循环模式DMA自动连续搬运Increment AddressMemory内存地址自增外设地址不变Data WidthWord32位宽度匹配24位数据寄存器数据宽度为什么选32位因为STM32F4的I2S数据寄存器是32位的即使设置24位数据格式每个声道的数据也是存储在32位寄存器的最高24位。DMA如果按16位宽度搬运数据会错位。这里务必选Word宽度。Mode选Circular是双缓冲机制的前提。循环模式下DMA搬运到缓冲区末尾后会自动回卷到起始地址同时触发传输完成中断在搬完一半时触发半传输中断。正是这两个中断构成了双缓冲的“双”。需要注意的是DMA的中断必须在NVIC中使能CubeMX一般在生成代码时会自动打开但如果手动改过NVIC设置记得检查I2S2_RX对应的DMA中断是否被勾选。否则采集不到数据时你会怀疑人生半天才发现中断压根没进。4.4 串口和定时器的扩展配置为了把采样数据传回电脑分析我同时用USART1作为调试输出口。CubeMX里配置USART1为异步模式波特率1152008位数据、无校验、1位停止位即可。但这里有个很关键的问题如果你的采样率是16kHz、每个样本16位那么音频数据的原始速率是16kHz × 16bit 256kbps而115200波特率连这个速度的一半都不到。所以用串口传原始PCM数据时必须把波特率提高到460800或921600否则数据根本发不完缓冲会不断溢出。我测试时用的是921600波特率可以稳定传输16kHz双字节样本。如果用更高速率或更多声道建议直接用USB虚拟串口或者外接USB转串口芯片。定时器在这个项目里不是必须的但如果你需要精确的音频帧计时或者做时间戳标记F4的定时器位数就要留意了。STM32F4的TIM2和TIM5是32位定时器其他大多数是16位定时器。16位定时器在72MHz或84MHz时钟下计数上限只有几毫秒用做长周期计时必须配合预分频否则溢出会带来时间戳错误。这个细节单独拿出来讲是因为我曾在做音频采集的定时触发功能时踩过这个坑后面在扩展小节里展开。5. 核心代码实现完整跑通零延迟采集5.1 缓冲区划分与启动流程CubeMX生成好基础工程后核心代码需要我们自己写主要分三块缓冲区定义、DMA回调、主循环处理。我先定义缓冲区并启动DMA接收/* 缓冲区大小256个32位采样点 */ #define AUDIO_BUF_SIZE 256 /* 双缓冲机制中前半区是buf[0]~buf[127]后半区是buf[128]~buf[255] */ static uint32_t audio_buf[AUDIO_BUF_SIZE]; volatile uint8_t buf_half_ready 0; /* 前半区数据就绪标志 */ volatile uint8_t buf_full_ready 0; /* 后半区数据就绪标志 */ volatile uint32_t dma_transfer_count 0; /* 记录DMA触发次数用于调试 */启动比较简洁HAL_I2S_Receive_DMA(hi2s2, (uint32_t *)audio_buf, AUDIO_BUF_SIZE);注意HAL_I2S_Receive_DMA的第二个参数是uint16_t还是uint32_t取决于你的HAL库版本。新版HAL库的I2S接收函数第二个参数类型是uint16_t*但传入的缓冲区实际是32位对齐的所以强转一下即可。第三个参数是采样点数不是字节数HAL库内部会根据DataFormat自动换算成字节数配成24位数据格式时内部会乘以4。启动之后DMA就会自动开始搬运数据不需要主循环干预。需要读取音频数据时只需要检查两个标志位。5.2 DMA中断回调与主循环配合DMA的中断最终会落到HAL库的回调函数里。在main.c中重写两个弱函数void HAL_I2S_RxHalfCpltCallback(I2S_HandleTypeDef *hi2s) { if (hi2s-Instance I2S2) { /* 前半区填充完成 */ buf_half_ready 1; dma_transfer_count; } } void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s) { if (hi2s-Instance I2S2) { /* 后半区填充完成 */ buf_full_ready 1; dma_transfer_count; } }主循环里这么处理while (1) { if (buf_half_ready) { buf_half_ready 0; process_audio_data(audio_buf[0], AUDIO_BUF_SIZE / 2); } if (buf_full_ready) { buf_full_ready 0; process_audio_data(audio_buf[AUDIO_BUF_SIZE / 2], AUDIO_BUF_SIZE / 2); } }process_audio_data函数就是你的业务逻辑比如做FFT、语音特征提取、通过串口发送数据。这里有个性能要求process_audio_data必须在下一个半区填满之前完成否则新的半传输中断到来时上一个数据还没处理完标志位就会被覆盖造成丢数据。对于16kHz采样率、半区128个采样点CPU可用的处理时间是128/16000 8ms。8ms对做FFT、滤波、特征提取来说完全够用但如果你的算法耗时超过8ms就需要增大缓冲区或者优化算法速度。5.3 24位原始数据的格式转换INMP441输出的是24位二进制补码数据在I2S外设的32位寄存器里高24位有效低8位全为0。所以拿到的audio_buf元素需要做数据转换具体取决于后续算法需要什么格式。如果你只需要16位PCM数据直接取高16位void process_audio_data(uint32_t *buf, uint32_t len) { int16_t pcm16[128]; for (uint32_t i 0; i len; i) { int32_t sample_32 (int32_t)buf[i]; /* 去掉低8位无效位再取高16位 */ pcm16[i] (int16_t)(sample_32 16); } /* 后续对pcm16做处理 */ }为什么要用(int32_t)强转再右移因为音频数据是带符号的必须按有符号数处理如果直接用uint32_t右移正负数会错乱全是“撕拉”的噪声声。如果你想保留24位动态范围就右移8位得到int32_t数据只是用的时候注意FFT输入等场景一般还需要再缩放一下避免溢出。INMP441在满量程时24位数据接近±8388608直接用32位整型算没问题转浮点时记得除以8388608.0归一化到-1.0到1.0区间。5.4 数据读取时间窗口的计算很多人在双缓冲上翻车是因为没算明白“什么时候该读哪半个缓冲区”。我画了一个很直观的时间线T0时刻DMA开始搬运目标地址audio_buf[0]缓冲区前半区T0 8ms半区128点 16kHz前半区写满触发半传输中断CPU读取audio_buf[0]~[127]同一时刻DMA继续搬运到后半区aud_buf[128]~[255]T0 16ms后半区写满触发全传输中断CPU读取audio_buf[128]~[255]同一时刻DMA回卷到audio_buf[0]开始覆盖前半区。关键点在于CPU读取前半区时的8ms处理时间内DMA正在写后半区两者互不干扰。如果CPU在16ms内只处理完前半区后半区就会在下次全传输中断前被覆盖数据就丢了。所以缓冲区大小和采样率的搭配本质上决定了你的可用处理时间窗口。可以按公式计算窗口时间 半区点数 / 采样率。改采样率或缓冲区大小时先把这个时间算出来心里有数再动手调。6. 实测效果波形、频谱和性能观察6.1 测试环境搭建代码下到板子里之后我用一台手机播放1kHz正弦波作为声源放在距离INMP441大约10厘米的位置。然后通过串口把采集到的PCM数据按16位格式发送到电脑用Python脚本接收并绘制波形图。串口发送部分的代码很简单把刚才的process_audio_data里加一个发送分支HAL_UART_Transmit(huart1, (uint8_t *)pcm16, sizeof(pcm16), 100);这里要注意大小端序。STM32是小端模式int16_t的低字节在前高字节在后。Python端用numpy的frombuffer解析时要指定dtypei2表示小端有符号16位否则波形会呈现奇怪的锯齿状。6.2 波形验证与FFT结果实测1kHz正弦波时收到的波形是非常标准的正弦曲线没有明显削波。用numpy计算FFT频谱在1kHz处出现尖锐的峰值底噪大约在-60dB以下和INMP441官方61dB信噪比基本吻合。尝试过用48kHz采样率采集同一信号SCK频率明显变快数据同样稳定半区256点下处理时间窗口约5.3msCPU占用率大约10%左右跑FFT毫无压力。测试中我还对比了开不开MCLK输出对波形的影响。开启MCLK时波形上偶尔出现高频毛刺关闭后毛刺消失。虽然毛刺不一定是MCLK直接引起的但无论如何INMP441不需要MCLK关掉它是最省心的。6.3 CPU负载和延迟数据用调试器观察在半区128点、16kHz采样率配置下每8ms触发一次中断中断里只做标志位置位和计数器累加主循环FFT处理128点数据大约耗时200微秒左右。算下来CPU占用率非常低采集链路本身几乎不占用CPUI2S和DMA硬件自动完成了绝大部分工作。延迟数据上从声音发出到CPU拿到半区数据理论延迟等于8ms半区等待时间再加上约几十微秒的中断响应和业务处理时间。这个数字对语音识别、唤醒词检测等应用已经非常够用了。如果你需要更低延迟可以把缓冲区缩小到128点但CPU处理时间窗口会缩短到4ms业务代码必须非常精简才能应付。7. 常见问题排查与避坑指南7.1 采到的数据全是0或全是噪声这个是最容易碰到的问题。先查接线再看配置按照下面顺序逐个排除INMP441的L/R引脚是否接对。L/R接地时数据在左声道如果你在CubeMX里配了右声道接收或者WS极性反了就可能采到全0或者反向的数据I2S模式是否是Master Receive。如果误配成了Master TransmitI2S外设在发送数据而不是接收SD引脚自然拿不到数据DMA的数据宽度是不是Word。配成HalfWord的话32位数据寄存器高低16位会被拆开数据完全错乱INMP441是否正常上电。用手碰一下麦克风表面的小孔用万用表量VDD引脚的电压如果是3.3V但数据依然全0换一颗麦克风试试毕竟是贴片物料偶尔会遇到次品。7.2 只有单声道或者左右声道颠倒INMP441是单麦克风输出本身只占用一个声道。如果你在I2S的飞利浦标准下WS低电平读到的是左声道数据高电平是右声道数据。L/R接GND时数据在左声道L/R接VDD时数据在右声道。实际使用时经常遇到“采到数据了但声音忽大忽小或者是嘈杂噪声”大概率是左右声道配置和你的数据处理函数对不上。比如麦克风配置为左声道输出但你在处理时把右声道数据也算进来了就会把没有数据的半帧当成0值或者噪声混入。解决办法很简单保持L/R接地并且在接收时只处理左声道的数据也就是WS低电平对应的数据位。7.3 DMA中断不触发或波形周期性跳变排查顺序从软件到硬件确认NVIC中DMA中断已经使能。CubeMX生成代码后如果手动清理过NVIC中断极容易被关掉确认DMA模式是Circular。Mode选Normal模式下DMA搬运完一遍就停永远不会触发第二次中断确认DMA的回调函数没有被别的文件重复定义。HAL库的弱函数如果有两个地方实现链接时会随机选中一个这是比较隐蔽的问题波形周期性跳变比如每隔16ms出现一次幅度异常说明半区和全区的数据衔接有问题。多半是process_audio_data处理时间超过了半区窗口导致后半区数据被覆盖了一部分。解决办法是缩短处理时间或者增大缓冲区。7.4 串口输出音频数据的带宽问题把数据发到电脑观察波形时很多人直接用115200波特率结果波形看起来像是随机噪声。其实计算一下就明白16kHz采样率、16位单声道数据理论数据率是256kbps115200波特率只有它的45%数据根本传不完。我的建议是把波特率配置到460800或921600USB转串口芯片必须支持这些高波特率常见CH340G可以某些老款PL2303在高波特率下不稳定慎用串口发送时用DMA还是阻塞发送取决于你的处理时间是否充裕。921600波特率下128个字节发送时间大约1.1毫秒相比8ms窗口是完全来得及的。7.5 定时器位数、Class B时钟自检等扩展话题如果你想把采样和定时器结合比如每固定时间窗口处理一帧音频建议优先用TIM2或TIM5这两个32位定时器避免16位定时器溢出带来时间戳错乱。F4的TIM2和TIM5是32位的其他多是16位这在配置时就要分辨清楚。如果你在车载或工业项目上做音频采集后续还会遇到安全诊断需求比如Class B等级的时钟自检、外设寄存器回读、DMA传输完整性校验。这些属于功能安全认证范畴STM32F4的RCC和DMA模块其实提供了相应的状态位和中断标志可以在启动阶段做时钟自检在运行阶段周期性检查DMA的错误标志。这个话题展开又是一大篇我这里先提个引子等有条件了单独出一篇聊。8. 我的扩展想法和收尾提醒做完这个项目最大的体会是音频采集链路的选择往往决定了整个项目后续的开发体验。模拟麦克风方案不是不行但工程调试成本高而数字MEMS麦克风加I2S加DMA这套组合真正做到了“硬件简单、软件可控、性能够用”。如果你有精力可以考虑在这个基础上做三个方向的扩展一是把采集到的数据直接在MCU上做FFT频谱分析做一个实时频谱显示或者声音控制开关。F4的主频跑256点FFT非常轻松。二是加一个I2S音频播放通路实现录音回放或语音提示。F4的I2S是全双工的同一套DMA机制可以反向输出音频数据代码结构几乎对称。三是把采集的数据流通过USB虚拟串口传给上位机配合Python库做实时波形显示和模型推理。这一步做出来基本就具备了一个简易的智能语音前端。最后再分享一个容易忽略的小经验调试音频项目时先用手机外放1kHz正弦波做测试源比对着麦克风喊话稳定得多能快速把数据链路调通然后再上真实语音测试。这样可以大幅缩短调试周期把精力聚焦在核心链路上。
返回列表