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 N+wN+w,想要获得一次分辨率为N + w N+wN+w的数据,那么就要以原分辨率采样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^{N+w}VREF+÷2N+w,刻度数=2 N + w 2^{N+w}2N+w)换算成真实电压值。
【注意】求和结果除以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^1=441=4倍。
即需要 4 次采集,然后将这 4 次采集结果求和,求和的结果再取平均(除以2 1 = 2 2^1=221=2,二进制运算相当于右移 1 位),就得到提高等效分辨率后的结果(等效 13 位分辨率)。
⑦ 实验:
实验简要:
- 功能描述:
- 通过 ADC1 通道1(PA1)过采样实现 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 文件代码:
#include"adc.h"#include"delay.h"#include"usart.h"#include"dma.h"/* 全局变量 */ADC_HandleTypeDef hadc1;DMA_HandleTypeDef hdma_adc1;uint16_tadc_dma_buf[ADC_DMA_BUF_SIZE];volatileuint8_tadc_dma_done=0;/* 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.Pin=GPIO_PIN_1;gpio.Mode=GPIO_MODE_ANALOG;HAL_GPIO_Init(GPIOA,&gpio);/* DMA1_Channel1 */hdma_adc1.Instance=DMA1_Channel1;hdma_adc1.Init.Direction=DMA_PERIPH_TO_MEMORY;hdma_adc1.Init.PeriphInc=DMA_PINC_DISABLE;hdma_adc1.Init.MemInc=DMA_MINC_ENABLE;hdma_adc1.Init.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD;hdma_adc1.Init.MemDataAlignment=DMA_MDATAALIGN_HALFWORD;hdma_adc1.Init.Mode=DMA_NORMAL;hdma_adc1.Init.Priority=DMA_PRIORITY_MEDIUM;HAL_DMA_Init(&hdma_adc1);/* ADC1 */hadc1.Instance=ADC1;hadc1.Init.ScanConvMode=ADC_SCAN_DISABLE;hadc1.Init.ContinuousConvMode=ENABLE;hadc1.Init.DiscontinuousConvMode=DISABLE;hadc1.Init.ExternalTrigConv=ADC_SOFTWARE_START;hadc1.Init.DataAlign=ADC_DATAALIGN_RIGHT;hadc1.Init.NbrOfConversion=1;HAL_ADC_Init(&hadc1);__HAL_LINKDMA(&hadc1,DMA_Handle,hdma_adc1);/* ADC 校准 */HAL_ADCEx_Calibration_Start(&hadc1);/* 通道配置 */sConfig.Channel=ADC_CHANNEL_1;sConfig.Rank=ADC_REGULAR_RANK_1;sConfig.SamplingTime=ADC_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_done=0;HAL_ADC_Start_DMA(&hadc1,(uint32_t*)adc_dma_buf,ADC_DMA_BUF_SIZE);}/* DMA 传输完成回调 */voidHAL_ADC_ConvCpltCallback(ADC_HandleTypeDef*hadc){if(hadc->Instance==ADC1){adc_dma_done=1;}}/* DMA 中断入口 */voidDMA1_Channel1_IRQHandler(void){HAL_DMA_IRQHandler(&hdma_adc1);}adc.h 文件代码:
#ifndef__ADC_H#define__ADC_H#include"sys.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 文件代码:
#include"sys.h"#include"delay.h"#include"usart.h"#include"led.h"#include"key.h"#include"string.h"#include"dma.h"#include"adc.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(i=0;i<ADC_DMA_BUF_SIZE/ADC_OVERSAMPLE_TIMES;i++){for(j=0;j<ADC_OVERSAMPLE_TIMES;j++){sum+=adc_dma_buf[(i*ADC_OVERSAMPLE_TIMES)+j];}v=sum>>ADC_ADD_RESOLUTION;v=v*3.3f/65535.0f;printf("%.6fV ",v);sum=0;}printf("\r\n");/* 再来一次 */ADC_DMA_Start();}LED0=!LED0;delay_ms(100);}}实验结果:
从结果可以看出,与之前的 3.299194V 还是有一些差别的。
