当前位置: 首页 > article >正文

STM32 ADC采样详解

Content

  • 0x00 前言
  • 0x01 ADC配置
  • 0x02 滤波处理

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,适用且可以应用于大多数场景。


http://www.kler.cn/a/287288.html

相关文章:

  • 【代码随想录|回溯算法排列问题】
  • 视觉SLAM相机——单目相机、双目相机、深度相机
  • C++线程基础使用方法
  • SpringBoot源码解析(四):解析应用参数args
  • 主机型入侵检测系统(HIDS)——Elkeid在Centos7的保姆级安装部署教程
  • 华为ensp实验二--mux vlan的应用
  • verilog bug记录-修改信号线频率
  • zookeeper分部式锁
  • ES6----练习题
  • 如何在S7-200 SMART CPU断电后保持高速计数器的当前值
  • Unity学习路线
  • 打造一流的研发型企业--- 金发科技研发驱动力初探
  • MATLAB学习笔记3
  • 使用 OpenSSL 进行 RSA 密钥生成与加解密操作(命令行方式)
  • 用MATLAB 画一个64QAM的星座图
  • gitlab使用
  • easyPOI生成的excel添加水印
  • Spark MLlib模型训练—分类算法Multinomial Logistic Regression
  • 【生活英语】2、喜欢与讨厌
  • 四足机器人控制算法——建模、控制与实践(unitree_guide配置)
  • ​14:00面试,14:06就出来了,问的问题有点变态。。。
  • FreeRTOS中任务通知的使用
  • 两句话解决ChatGPT 502 Bad Gateway问题
  • 前后端分离项目遇到的跨域问题解决方案(后端为主)
  • uniapp按钮点击过快导致请求数据混乱
  • 【学习笔记】卫星通信NTN 3GPP标准化进展分析(五)- 3GPP Release19 研究计划