Cortex-M3-STM32F1 开发:(四十六)ADC 实例 4 :单通道 ADC 过采样实例,使用 DMA,两种编程方式

📅 2026/8/20 21:28:01
Cortex-M3-STM32F1 开发:(四十六)ADC 实例 4 :单通道 ADC 过采样实例,使用 DMA,两种编程方式
上一篇下一篇ADC 实例 3 多通道 ADC 采集实例使用 DMA目 录3.4单通道 ADC 过采样实例使用 DMA① 背景② 原理③ 代价④ 上限⑤ 公式与计算⑥ 举例⑦ 实验⑧ 代码与实验结果3.4单通道 ADC 过采样实例使用 DMA方程推导过程https://max.book118.com/html/2018/0506/165038217.shtm提高 ADC 分辨率一般来说有三种方法采用 H7 这种分辨率为 16 位的单片机外接专用的高分辨率 ADC 采集板然后单片机用串口或IIC这些通信接口读取过采样取平均提高等效分辨率① 背景ADC 的有效分辨率并非仅由其量化位数决定而是受到系统噪声水平的显著影响。当噪声幅度接近或超过量化步长时部分低位信息将被噪声淹没。通过降低噪声功率或采用过采样与平均等方法提高信噪比可有效降低噪声地板让更小的信号变化能“浮出噪声地板”从而提升系统的最小可分辨信号变化量即提高等效分辨率。这个多出来的是“等效分辨率”反映更小更稳定的读数而不是 ADC 真正能区分的最小电压单位。② 原理由独立随机变量方差叠加规律类似大数定理可得取均值可以降低类白噪声均匀量化噪声热噪声的方差提高 SNR 。而想要取均值的同时提高分辨率就需要在取均值之前进行过采样类白噪声均值为 0 样本间不相关。③ 代价过采样和求均值对 SNR 和测量分辨率的改善是以增加 CPU 时间和降低数据吞吐率为代价的。④ 上限过采样和取平均本质上只能消除 ADC 测量中的随机抖动。当系统噪声主要来自热噪声或量化噪声时增加平均次数可以让 ADC 的读数更细腻从而获得比标称位数更高的等效分辨率。但当随机噪声已经被压到很低之后剩下的误差主要来自低频噪声、非线性和参考电压不稳定等因素这些误差无法通过平均消除此时继续过采样只能让数值看起来更稳定而无法再分辨更小的电压变化。以 STM32F103 的 12 位 ADC 为例在 3.3 V 参考条件下其理论最小分辨电压为约 0.8 mV但在实际系统中未进行过采样时 ADC 输出抖动通常达到 2~3 LSB。通过 16~64 倍过采样与平均可将随机噪声有效降低至亚 LSB 量级使等效分辨率提升至约 11.5 bit。然而当随机噪声被充分抑制后低频噪声、非线性误差及参考电压波动成为主要限制因素此时继续增加平均次数已难以进一步提高系统的有效分辨能力。⑤ 公式与计算结合原理根据要增加的分辨率位数计算过采样频率方程f o s 4 w ⋅ f s \Large f_{os} 4^w ⋅ f_sfos​4w⋅fs​其中f o s f_{os}fos​是过采样频率w ww是希望增加的分辨率位数f s f_sfs​是原采样频率。假设原分辨率为N NN则新的等效分辨率为N w NwNw想要获得一次分辨率为N w NwNw的数据那么就要以原分辨率采样4 w 4^w4w次然后将这4 w 4^w4w个结果求和再除以2 w 2^w2w结果可能是小数在工程中我们往往是将求和结果右移w ww位以此来达到除以2 w 2^w2w并取整的效果许多技术文档都是这样写的。然后再按新的量纲最小刻度V R E F ÷ 2 N w V_{REF}÷2^{Nw}VREF​÷2Nw刻度数2 N w 2^{Nw}2Nw换算成真实电压值。【注意】求和结果除以2 w 2^w2w其实这是一种人为拉大刻度数的方法后面要换算成实际电压时要根据新的量纲来换算。其实际效果其实就等于 ”求和结果除以4 w 4^w4w用 flaot 来存储结果然后按原量纲最小刻度V R E F ÷ 2 N V_{REF}÷2^{N}VREF​÷2N换算“这种其实是学术理论上更正确的计算方法 。⑥ 举例要想让 12 位分辨率的 ADC 提高 1 位分辨率采样频率就要提高4 1 4 4^14414倍。即需要 4 次采集然后将这 4 次采集结果求和求和的结果再取平均除以2 1 2 2^12212二进制运算相当于右移 1 位就得到提高等效分辨率后的结果等效 13 位分辨率。⑦ 实验实验简要功能描述通过 ADC1 通道1PA1过采样实现 16 位分辨率采集 3.3V 的电压并显示 ADC 转换的数字量及换算后的电压值使用 DMA 传输由于过采样这个方法的上限这 16 位中其实只有 11/12 位左右的真实准确度确定最小刻度V R E F 3.3 V V_{REF} 3.3VVREF​3.3V→0 V ≤ V I N ≤ 3.3 V 0V≤V_{IN}≤3.3V0V≤VIN​≤3.3V→ 最小刻度 3.3 / 65536 V 3.3/65536V3.3/65536V确定转换时间采样时间设置为最小值 1.5 个 ADC 时钟周期可以得到转换时间为 1.17us * 256 299.52us得到 1 个有效数据模式组合连续转换模式、不使用扫描模式⑧ 代码与实验结果adc.c 文件代码#includeadc.h#includedelay.h#includeusart.h#includedma.h/* 全局变量 */ADC_HandleTypeDef hadc1;DMA_HandleTypeDef hdma_adc1;uint16_tadc_dma_buf[ADC_DMA_BUF_SIZE];volatileuint8_tadc_dma_done0;/* ADC1 初始化 */voidMX_ADC1_Init(void){ADC_ChannelConfTypeDef sConfig{0};__HAL_RCC_ADC1_CLK_ENABLE();__HAL_RCC_DMA1_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();/* PA1 → ADC1_CH1 */GPIO_InitTypeDef gpio{0};gpio.PinGPIO_PIN_1;gpio.ModeGPIO_MODE_ANALOG;HAL_GPIO_Init(GPIOA,gpio);/* DMA1_Channel1 */hdma_adc1.InstanceDMA1_Channel1;hdma_adc1.Init.DirectionDMA_PERIPH_TO_MEMORY;hdma_adc1.Init.PeriphIncDMA_PINC_DISABLE;hdma_adc1.Init.MemIncDMA_MINC_ENABLE;hdma_adc1.Init.PeriphDataAlignmentDMA_PDATAALIGN_HALFWORD;hdma_adc1.Init.MemDataAlignmentDMA_MDATAALIGN_HALFWORD;hdma_adc1.Init.ModeDMA_NORMAL;hdma_adc1.Init.PriorityDMA_PRIORITY_MEDIUM;HAL_DMA_Init(hdma_adc1);/* ADC1 */hadc1.InstanceADC1;hadc1.Init.ScanConvModeADC_SCAN_DISABLE;hadc1.Init.ContinuousConvModeENABLE;hadc1.Init.DiscontinuousConvModeDISABLE;hadc1.Init.ExternalTrigConvADC_SOFTWARE_START;hadc1.Init.DataAlignADC_DATAALIGN_RIGHT;hadc1.Init.NbrOfConversion1;HAL_ADC_Init(hadc1);__HAL_LINKDMA(hadc1,DMA_Handle,hdma_adc1);/* ADC 校准 */HAL_ADCEx_Calibration_Start(hadc1);/* 通道配置 */sConfig.ChannelADC_CHANNEL_1;sConfig.RankADC_REGULAR_RANK_1;sConfig.SamplingTimeADC_SAMPLETIME_1CYCLE_5;HAL_ADC_ConfigChannel(hadc1,sConfig);/* DMA 中断 */HAL_NVIC_SetPriority(DMA1_Channel1_IRQn,2,0);HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);}/* 启动一次 ADC DMA 采集 */voidADC_DMA_Start(void){adc_dma_done0;HAL_ADC_Start_DMA(hadc1,(uint32_t*)adc_dma_buf,ADC_DMA_BUF_SIZE);}/* DMA 传输完成回调 */voidHAL_ADC_ConvCpltCallback(ADC_HandleTypeDef*hadc){if(hadc-InstanceADC1){adc_dma_done1;}}/* DMA 中断入口 */voidDMA1_Channel1_IRQHandler(void){HAL_DMA_IRQHandler(hdma_adc1);}adc.h 文件代码#ifndef__ADC_H#define__ADC_H#includesys.h#defineADC_ADD_RESOLUTION4// ADC新增分辨率#defineADC_OVERSAMPLE_TIMES(1(2*ADC_ADD_RESOLUTION))// ADC过采样次数, 这里提高4bit分辨率, 需要256倍采样#defineADC_DMA_BUF_SIZE(10*ADC_OVERSAMPLE_TIMES)// ADC DMA采集 BUF大小, 应等于过采样次数的整数倍externADC_HandleTypeDef hadc1;externDMA_HandleTypeDef hdma_adc1;externuint16_tadc_dma_buf[ADC_DMA_BUF_SIZE];externvolatileuint8_tadc_dma_done;voidMX_ADC1_Init(void);voidADC_DMA_Start(void);#endifmain.c 文件代码#includesys.h#includedelay.h#includeusart.h#includeled.h#includekey.h#includestring.h#includedma.h#includeadc.h/* */intmain(void){uint16_ti,j;uint32_tsum;floatv;/* 初始化 */HAL_Init();Stm32_Clock_Init(RCC_PLL_MUL9);delay_init(72);LED_Init();uart_init(115200);MX_ADC1_Init();/* 启动第一次 DMA 采集 */ADC_DMA_Start();while(1){if(adc_dma_done){printf(DMA采集完成10个结果分别为);for(i0;iADC_DMA_BUF_SIZE/ADC_OVERSAMPLE_TIMES;i){for(j0;jADC_OVERSAMPLE_TIMES;j){sumadc_dma_buf[(i*ADC_OVERSAMPLE_TIMES)j];}vsumADC_ADD_RESOLUTION;vv*3.3f/65535.0f;printf(%.6fV ,v);sum0;}printf(\r\n);/* 再来一次 */ADC_DMA_Start();}LED0!LED0;delay_ms(100);}}实验结果从结果可以看出与之前的 3.299194V 还是有一些差别的。