打印
[应用相关]

STM32 ADC采样详解

[复制链接]
1493|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
0x00 前言
在单片机开发过程中,常常涉及到ADC的使用,市面上大部分便宜的传感器都是采用的ADC来获取其数据,如MQ-2 烟雾传感器、光敏传感器等等。





此类传感器工作原理为根据所采集到的数据变化,造成其电阻率、电导率等参数变化,从而改变输出电压的大小。而我们通过采集传感器模块的输出电压,即可分析到所需要的信息,这个过程是由模拟信号(因为数据并不是直接以数字体现,而是以电压形式给出,所以称模拟)转化为数字信号(我们需要的是一个数值:浓度,强度),所以这个外设被称为ADC(Anology to Digital),其中C表示的Convert,为转换器的意思。

那么通俗的来说,ADC就是将模拟信号转换为数字信号的一个外设。通过ADC,可以获取到以电压形式体现的信息。还是以烟雾传感器传感器为例,假设烟雾传感器的电压输出范围是0-5v,其中显而易见0代表的是烟雾浓度为0%,5v代表的是烟雾浓度为100%,2.5v代表的烟雾浓度为50%。

我们为获取当前环境的烟雾传感器浓度,只需要采集到 烟雾传感器输出的电压即可。

0x01 ADC配置
现在需要确定一下你的引脚选择,既然是要使用ADC,那么所选择的引脚必须是具有ADC功能才对,现在可以在以下表中选择ADC引脚,例如我选择PA1引脚,那么我接下来应该配置ADC1的通道1。



具体的配置参数细节都已经在注释之中了:

void ADC_Init()
{
        ADC_InitTypeDef ADC_InitStructure;
        GPIO_InitTypeDef GPIO_InitStructure;
       
        // 使能ADC1外设时钟,以及将PA1引脚设置为模拟输入模式。
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1, ENABLE );
        // 设置ADC的时钟频率,也就是STM32的主频 72M/分频因子6 = 12M, 不能超过14M,否则会造成ADC采样精度下降
        RCC_ADCCLKConfig(RCC_PCLK2_Div6);
       
        // 配置为模拟输入模式 Anology Input                    
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
        GPIO_Init(GPIOA, &GPIO_InitStructure);

        ADC_DeInit(ADC1);  //复位ADC1
        ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;        //ADC工作模式:ADC1和ADC2工作在独立模式
        // 开启扫描模式,则会依次扫描通道,例如你有多个烟雾传感器连接在通道1,通道2,通道3 ,通道4
        ADC_InitStructure.ADC_ScanConvMode = DISABLE;        //模数转换工作在单通道模式
        // 单次转换模式或连续转换模式,如果开启了连续转换模式就会自动连续进行转换,否则需要自己需要的时候开启转换
        // 使用DMA时开启自动转换也会更加方便
        ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;        //模数转换工作在单次转换模式
        ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;        //转换由软件而不是外部触发启动
        //左对齐,数据范围0-65535,右对齐,数据范围0-4065(精度一样都是12位,只是12位数据以不同的对齐方式放在了uint16_t变量中)
        ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Left;        //ADC数据右对齐
        ADC_InitStructure.ADC_NbrOfChannel = 1;//顺序进行规则转换的ADC通道的数目
       
        ADC_Init(ADC1, &ADC_InitStructure);        //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器

        // 注意这两个函数是不一样的,先复位校准再进行初始校准
        ADC_Cmd(ADC1, ENABLE);        //使能指定的ADC1
        ADC_ResetCalibration(ADC1);        //使能复位校准  
        while(ADC_GetResetCalibrationStatus(ADC1));        //等待复位校准
        ADC_StartCalibration(ADC1);         //开启AD校准
        while(ADC_GetCalibrationStatus(ADC1));         //等待初始校准
}


至此ADC校准就结束了,此时就可以编写ADC的单次转换函数了

uint16_t GetAdcValue(u8 ch)   
{
          // 对ADC1的通道1 进行单次转换
        ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );
        //使能指定的ADC1的软件转换启动功能       
        ADC_SoftwareStartConvCmd(ADC1, ENABLE);               
        while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
        return (ADC_GetConversionValue(ADC1));        // 返回ADC采样值
}


0x02 滤波处理
一般由于ADC采样到的值是不稳定的,并且电压变化的速率以及大于了ADC的采样速率,并且现实中也会有各种的干扰情况发生,所以单次采样的值并不能代表实际采样的值。

为减少这种情况的发生,所以就要进行一定的滤波处理了。

初中老师以及教过,对于化学、物理、生物实验,为了避免实验的偶然性,应当多做实验。ADC 采样也不例外,可以多次测量,取平均值,在短时间内进行多次ADC采样,取得平均值结果。那么这个就是叫做平均值滤波。这个一般只是一个很简单的处理,通常也是误差比较大的,因为加入你采到了一个非常离谱的数据,贸然取平均值便会导致整体采样值偏移。

如图,以下是由180个采样点,插入一些随机的噪声,贸然取平均值得到的数据,整体大致偏移了2000,这算是很大的误差了



那么还有一个方法是参考其他的采样数据,从而剔除不合理的数据,例如采用方差
等数据剔除不合理的数据,筛选一遍过后,计算得到平滑数据,如下:




当然,并非只有一种滤波算法可选,也可以选择一套动态的滤波算法如FFT,适用且可以应用于大多数场景。
————————————————

                            本文为博主原创文章,未经博主允许转载,仅限学习交流使用。如有问题,欢迎指正。

原文链接:https://blog.csdn.net/qq_53381910/article/details/141817805

使用特权

评论回复
沙发
AdaMaYun| | 2024-9-9 16:11 | 只看该作者
滤波算法非常重要,尤其是高精度计算

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

85

主题

4090

帖子

3

粉丝