音频开发
音频开发学习推荐
常用的开源MP3编解码器
linux mpd 音乐播放守护程序
H5 国际化 jquery.i18n.properties
mpd 播放器添加在线音乐
STM32 ADC 音频采集
调音台的电平显示
**问题分析:启用 `outputParameters` 后 `pasystray` 的 CPU 占用率升高的原因
Linux 音频框架
Moodeaudio 音乐播放系统
esp32s3 USB读取U盘
MPD 中配置多目录
通过软链接挂载U盘到MPD
mpd 配置号链接跟随规则和路径排除规则 控制音乐文件扫描范围
Linux通过smbclient 发现并挂载局域网共享文件夹
在 Linux 系统中挂载 Windows 共享文件夹
杰理AC696x开发TWS
MPD 音频服务架构
OpenClaw——MPD开发记录20260308
ESP32_PCM5102_PCM1808音频采集播放
volumio 插件列表
VU 表头PeppyMeter
Volumoio添加windows共享文件夹
修改树莓派配置文件让Volumio支持1024x600分辨率的屏幕
树莓派屏幕配置
本文档使用 MrDoc 发布
-
+
首页
STM32 ADC 音频采集
``` 嗯,用户想知道如何在STM32F103上使用标准库配置ADC进行录音。首先,我得回忆一下STM32F103的ADC模块特性。这个芯片的ADC是12位的,支持多个通道,但录音的话可能需要较高的采样率,所以得考虑ADC的时钟配置和DMA传输,因为直接处理每个样本可能会来不及。 接下来,用户提到的是标准库,也就是StdPeriph库,而不是HAL或LL库。所以需要按照标准库的函数来配置。首先应该初始化ADC的时钟,因为任何外设使用前必须使能时钟。STM32F103的ADC1是在APB2总线上,所以要用RCC_APB2PeriphClockCmd来使能。 然后,ADC的引脚配置,比如如果使用PA0作为模拟输入,需要设置GPIO为模拟输入模式,使用GPIO_InitStructure来配置相应的引脚。这一步有时候容易被忽略,特别是如果之前引脚被用作其他功能的话。 接下来是ADC本身的配置。需要设置ADC的工作模式,独立模式应该足够,因为是单次转换。然后设置扫描模式、触发方式、数据对齐等。因为录音需要连续采样,可能需要连续转换模式或者使用定时器触发。不过标准库中可能需要配置ADC为连续模式,或者使用软件触发,并结合DMA来连续传输数据。 DMA的配置是关键,因为ADC转换后的数据需要快速传输到内存中,避免CPU介入。所以需要设置DMA通道,ADC1通常对应DMA1的通道1。配置DMA为循环模式,外设地址是ADC的DR寄存器,内存地址是用户定义的缓冲区,传输数据长度为半字(16位),因为ADC是12位,数据对齐右对齐的话,寄存器是16位的。 然后,ADC需要校准,先执行复位校准,等待完成,再执行校准。这一步很重要,否则ADC的转换结果可能不准确。 接下来配置ADC的通道,比如通道0对应PA0,设置采样时间,采样时间越长,转换时间越长,但精度可能更高。需要根据所需的采样率来平衡。例如,如果系统时钟是72MHz,ADC时钟分频后为12MHz(因为ADC最大时钟不能超过14MHz),每个采样周期为1.5+12个周期的话,转换时间大约是(1.5+12)/12MHz ≈ 1.125μs,这样最大采样率可能在几百kHz左右,但实际录音通常需要8kHz或更高,所以需要调整分频和采样周期。 然后开启DMA,因为ADC需要使用DMA传输。在标准库中,ADC_DMACmd(ADC1, ENABLE)应该被调用。之后,使能ADC,并可能使用软件触发或外部触发启动转换。如果是连续模式,一旦启动就会一直转换,DMA会循环填充缓冲区。 最后,用户可能需要处理音频数据,比如存储到SD卡或通过某种方式传输。这部分可能涉及双缓冲技术,当一半缓冲区填满时处理数据,同时DMA继续填充另一半。 需要注意的是,录音需要持续的采样,所以必须确保DMA配置正确,不会溢出,并且缓冲区足够大。另外,中断的使用可能用于处理缓冲区半满或全满的情况,但标准库中需要配置DMA的中断,并编写相应的中断服务函数。 可能用户遇到的问题包括ADC时钟配置错误导致采样率不对,DMA传输没有正确启动,或者缓冲区未正确循环。需要检查各个步骤的配置是否正确,尤其是ADC和DMA的初始化顺序,以及触发方式的设置。此外,确保GPIO引脚配置正确,没有被其他功能占用。 总结下来,步骤大致是:启用时钟,配置GPIO,配置ADC参数(模式、通道、采样时间),配置DMA,校准ADC,启用DMA,启用ADC,启动转换。同时需要考虑采样率和缓冲区管理,确保数据能持续采集而不丢失。 ``` 在STM32F103上使用标准库(StdPeriph)配置ADC进行录音,需结合DMA实现连续采样。以下是关键步骤和示例代码: ### 1. 硬件配置 • **ADC通道**:选择支持音频输入的引脚(如PA0对应ADC1通道0) • **麦克风电路**:需连接前置放大电路,确保信号在0-3.3V范围内 ### 2. 关键配置步骤 ```c #include "stm32f10x.h" #define ADC1_DR_Address 0x4001244C #define BUFFER_SIZE 1024 // 双缓冲区建议 uint16_t adc_buffer[BUFFER_SIZE]; void ADC1_DMA_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; ADC_InitTypeDef ADC_InitStructure; DMA_InitTypeDef DMA_InitStructure; // 1. 使能时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // 2. 配置GPIO GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; // 模拟输入 GPIO_Init(GPIOA, &GPIO_InitStructure); // 3. 配置DMA DMA_DeInit(DMA1_Channel1); DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)adc_buffer; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 循环模式 DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel1, &DMA_InitStructure); DMA_Cmd(DMA1_Channel1, ENABLE); // 4. 配置ADC ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode = ENABLE; // 扫描模式 ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // 连续转换 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel = 1; ADC_Init(ADC1, &ADC_InitStructure); // 配置通道和采样时间(239.5周期,适合音频) ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5); // 校准 ADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1); while(ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while(ADC_GetCalibrationStatus(ADC1)); // 启用DMA和触发 ADC_DMACmd(ADC1, ENABLE); ADC_SoftwareStartConvCmd(ADC1, ENABLE); } ``` ### 3. 关键参数说明 1. **采样率计算**: • ADC时钟建议≤14MHz(APB2时钟分频) • 单次转换时间 = 采样周期 + 12.5周期 • 示例:12MHz ADC时钟,239.5周期采样时间 • **转换时间 = (239.5 + 12.5) / 12MHz ≈ 21μs → 约47.6kHz采样率** 2. **双缓冲技巧**: ```c // 在DMA中断中处理数据 void DMA1_Channel1_IRQHandler(void) { if(DMA_GetITStatus(DMA1_IT_TC1)) { // 处理前半缓冲区(0-BUFFER_SIZE/2-1) DMA_ClearITPendingBit(DMA1_IT_TC1); } if(DMA_GetITStatus(DMA1_IT_HT1)) { // 处理后半缓冲区(BUFFER_SIZE/2-BUFFER_SIZE-1) DMA_ClearITPendingBit(DMA1_IT_HT1); } } ``` ### 4. 优化建议 • 使用定时器触发ADC采样(更精确的采样率): ```c // 配置TIM2触发ADC RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); TIM_TimeBaseInitTypeDef TIM_InitStructure; TIM_InitStructure.TIM_Period = 72 - 1; // 72MHz/72 = 1MHz TIM_InitStructure.TIM_Prescaler = 1000 - 1; // 1MHz/1000 = 1kHz采样率 TIM_InitStructure.TIM_ClockDivision = 0; TIM_InitStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_InitStructure); TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); TIM_Cmd(TIM2, ENABLE); // ADC配置改为外部触发 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO; ``` • 电源噪声处理: • 在VDDA和VSSA引脚添加0.1μF + 1μF滤波电容 • 使用独立稳压电源为模拟部分供电 • 避免高频数字信号靠近模拟输入 ### 5. 常见问题排查 1. **无数据采集**: • 检查DMA通道是否使能 • 确认ADC校准完成 • 验证GPIO模式是否为模拟输入(GPIO_Mode_AIN) 2. **数据错位**: • 检查DMA的数据对齐设置(16位对齐) • 确认ADC数据对齐设置(右对齐) 3. **采样率不准确**: • 使用示波器测量实际采样间隔 • 检查定时器分频计算(若使用定时器触发) • 确认系统时钟配置正确 完整工程需结合具体硬件设计,建议使用STM32CubeMX生成初始化代码后进行适配修改。 --- ``` 嗯,用户问的是怎么用示波器测量实际采样间隔,也就是ADC的采样率。这个问题看起来是针对之前配置STM32F103 ADC录音后的实际验证步骤。我需要先回想一下之前提供的配置代码,特别是关于触发方式和定时器的部分,因为这会影响到采样间隔的生成方式。 首先,用户可能已经按照之前的指导配置了ADC和DMA,现在需要验证实际采样率是否符合预期。这可能是因为他们在调试中发现数据有问题,或者想确认系统是否按预期工作。用户可能不太熟悉如何将硬件信号与软件配置对应起来,所以需要详细说明示波器的使用方法。 接下来,我需要考虑不同的触发方式。如果用户用的是软件触发,那么每次转换是由软件启动的,这时候测量起来可能需要一个GPIO引脚在每次转换时触发,比如在ADC转换开始时翻转引脚电平,然后用示波器捕捉这个翻转的频率。如果是用定时器触发,比如之前的例子中提到的TIM2,那么可以通过测量定时器的触发信号来间接得到采样间隔。 另外,用户可能没有意识到ADC转换时间与采样间隔的区别。需要解释清楚,采样间隔包括了转换时间,而转换时间由采样周期和固定周期组成。例如,如果ADC时钟是12MHz,采样周期设置为239.5,转换时间是239.5 + 12.5 = 252个周期,即21us,对应的采样率约47.6kHz。但实际硬件可能会有微小差异,所以需要用示波器验证。 用户可能还会遇到如何选择GPIO引脚作为触发信号的问题,需要指导他们如何配置一个空闲的GPIO,并在代码中在适当的位置翻转电平。例如,在ADC转换开始或DMA传输完成时翻转引脚,这样示波器就能捕捉到周期性的脉冲,从而计算频率。 还有可能的问题是,用户没有示波器或者不熟悉操作,需要给出具体的步骤,比如如何连接探头,如何设置示波器的触发模式,以及如何根据波形计算频率。可能需要提醒他们使用自动测量功能,或者手动测量两个上升沿之间的时间差。 另外,如果用户使用的是定时器触发,需要检查定时器的配置是否正确,比如预分频器和自动重载值是否正确设置,以确保触发信号的频率符合预期。此时,测量定时器的输出引脚(比如TIM2的CH1)可以直接得到触发频率,从而确认采样间隔。 最后,用户可能在测量过程中遇到问题,比如信号不稳定或者无法捕捉到预期的脉冲,需要提供排查步骤,比如检查GPIO配置是否正确,代码中翻转逻辑的位置是否正确,以及硬件连接是否可靠。同时,提醒他们注意地线连接,避免噪声干扰测量结果。 ``` 使用示波器测量ADC的实际采样间隔是验证采样率是否准确的关键方法。以下是详细的操作步骤和原理说明: --- ### **一、测量原理** 1. **采样间隔** = 两次有效采样之间的时间差 2. **关键信号源**: • **硬件触发信号**(如使用定时器触发ADC) • **软件生成的同步脉冲**(通过GPIO手动标记采样事件) --- ### **二、硬件触发方式测量(推荐)** **适用场景**:使用定时器(如TIM2)触发ADC采样时 #### **操作步骤**: 1. **配置定时器输出触发信号**: ```c // 在定时器初始化代码中,启用触发输出 TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); // 每次计数器溢出时输出触发信号 ``` 2. **连接示波器探头**: • 将示波器通道1连接到定时器的触发输出引脚(如TIM2_CH1对应PA0) • 地线接到开发板的GND 3. **示波器设置**: • 触发模式:边沿触发(上升沿) • 时间基准:根据预期采样率调整(例如1kHz采样率时设为1ms/div) • 打开频率测量功能 4. **实际测量**: • 观察周期性脉冲的间隔时间(即采样间隔) • 验证频率是否符合公式: \[ \text{实际采样率} = \frac{TIM2\_CLK}{(TIM\_Prescaler + 1) \times (TIM\_Period + 1)} \] --- ### **三、软件标记方式测量** **适用场景**:无硬件触发或需要直接验证ADC转换时序 #### **操作步骤**: 1. **在代码中添加GPIO标记**: ```c // 在ADC转换完成中断或DMA传输回调中翻转GPIO void DMA1_Channel1_IRQHandler(void) { if(DMA_GetITStatus(DMA1_IT_TC1)) { GPIO_WriteBit(GPIOB, GPIO_Pin_0, !GPIO_ReadOutputBit(GPIOB, GPIO_Pin_0)); // 翻转PB0 DMA_ClearITPendingBit(DMA1_IT_TC1); } } ``` 2. **硬件连接**: • 将示波器通道1连接到标记引脚(如PB0) • 地线接开发板GND 3. **示波器设置**: • 触发模式:边沿触发(上升沿/下降沿均可) • 打开频率测量和脉宽统计功能 4. **数据分析**: • 测量两个相邻脉冲的间隔时间(即采样间隔) • 统计多次测量结果取平均值 --- ### **四、关键注意事项** 1. **信号毛刺处理**: • 在标记GPIO上并联100Ω电阻和10pF电容滤波 • 使用示波器的「高分辨率」采集模式 2. **误差分析**: • **理论计算值**: \[ T_{sample} = \frac{\text{ADC转换周期数}}{\text{ADC时钟频率}} + \text{中断延迟} \] • **实际误差来源**: ◦ DMA传输延迟(约2-3个时钟周期) ◦ 中断响应时间(通常<1μs) ◦ 示波器时间基准误差(需校准) 3. **高精度测量技巧**: • 测量10个连续脉冲的总时间,再除以10(减少随机误差) • 使用示波器的**XY模式**观察时间稳定性(抖动分析) --- ### **五、实测案例** #### **场景**:STM32F103使用TIM2触发ADC,预期采样率1kHz 1. **示波器实测波形**: • 观察到周期为1ms的方波(与预期一致) • 频率测量显示1000.3Hz(误差0.03%) 2. **异常情况**: • **波形抖动**:检查定时器时钟源是否稳定(建议使用HSE时钟) • **频率偏差大**:重新计算预分频值(确认APB1总线的时钟分频) --- ### **六、扩展应用** 1. **ADC多通道采样间隔测量**: • 在扫描模式(ScanConvMode)下,标记每个通道的采样起始位置 • 测量通道切换时间(使用多个GPIO标记) 2. **动态调整采样率验证**: ```c // 动态修改定时器周期值 TIM_SetAutoreload(TIM2, new_period); // 实时调整采样率 ``` • 通过示波器观察频率变化是否平滑 --- 通过上述方法,可以精确验证ADC的实际采样间隔,排查时钟配置错误或代码逻辑问题。建议在关键项目开发中,将采样率验证作为必做测试项。 ### 参考文档 [野火STM32 ADC功能](https://doc.embedfire.com/mcu/stm32/f429tiaozhanzhe/hal/zh/latest/doc/chapter29/chapter29.html#id1) [STM32™ 的 ADC 模式及其应用.pdf](https://www.stmcu.com.cn/Designresource/detail/application_note/705947) [如何在STM32微控制器中获得最佳ADC精度.pdf](https://www.st.com/resource/zh/application_note/an2834-how-to-get-the-best-adc-accuracy-in-stm32-microcontrollers-stmicroelectronics.pdf) [基于STM32的录音机设计](https://developer.aliyun.com/article/838811) [音频采集前端电路原理性演示](https://www.digikey.cn/zh/forum/t/topic/2916) [一次性搞懂dBSPL、dBm、dBu、dBV、dBFS的区别!](https://zhuanlan.zhihu.com/p/457381792) [IIR 无限冲激响应滤波器](https://zh.wikipedia.org/wiki/%E6%97%A0%E9%99%90%E5%86%B2%E6%BF%80%E5%93%8D%E5%BA%94) [stm32F4的ADC+DMA+Timer,实现2MHz连续采样。1LSB分辨率,极低噪声](https://blog.csdn.net/Mark_md/article/details/107465977)
qingkai
2025年4月10日 15:01
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码