1.ADC采样
1.1 ADC采样原理
我们知道单片机是数字芯片,只认识由0和1组成的逻辑序列,但是我们现实当中有很多模拟的物理量,如温度,这些模拟量该如何被单片机系统处理呢,这就会用到AD转换,AD转换的英文就是Analog to Digital ,由模拟量转化为数字量;而DA,则为Digital to Analog,数字量转化为模拟量。那单片机是如何实现转化的呢?
AD转换芯片上的数据手册上都会告诉你一个信息,这个芯片是8位的,这个芯片是10位,还有12位的,16的。这就代表着单片机AD采样后所能得到的最大值。如何AD采样时8位的那对应的值就为0-255,10位对应的值为0-1023,12位对应的值就为0-4095。对于一个12位AD转换芯片而言,如果输入电压为0,那单片机读到的就是0,如果输入电压是VCC,那单片机读到的就是4095,已经知道了这两个点(0,0),(VCC,4095),根据两点就可以确定一个公式,这时候你就可以求出任意电压值所对应的数字量是多少了
1.2 STM32F4 ADC简介
来自原子哥STM32开发指南HAL库版本。
STM32F4xx 系列一般都有 3 个 ADC,这些 ADC 可以独立使用,也可以使用双重/三重模式(提高采样率)。 STM32F4 的 ADC 是 12 位逐次逼近型的模拟数字转换器。 它有 19 个通道,可测量 16 个外部源、 2 个内部源和 Vbat 通道的信号。 这些通道的 A/D 转换可以单次、连续、扫描或间断模式执行。 ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中。 模拟看门狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。
ADC 电源要求:全速运行时为 2.4 V 到 3.6 V,慢速运行时为 1.8 V。但是一定别接到 5V 上面去,否则可能烧坏 ADC! 数电小知识: ADC 是12位逐次逼近型模数转换器,输出数值范围是 0 ~ 2^12 -1(0 ~ 4095),满量程是 3.3V ,分辨率就是最低有效位(LSB)的对应输入电压值。分辨率 =3300/4095 ≈ 0.806mV
STM32F407ZGT6 包含有 3 个 ADC。 STM32F4 的 ADC 最大的转换速率为 2.4Mhz,也就是转换时间为 0.41us (在 ADCCLK=36M,采样周期为 3 个 ADC 时钟下得到),不要让 ADC 的时钟超过 36M,否则将导致结果准确度下降。
STM32F4 将 ADC 的转换分为 2 个通道组:规则通道组和注入通道组。规则通道相当于你正常运行的程序,而注入通道呢,就相当于中断。在你程序正常执行的时候,中断是可以打断你的执行的。同这个类似,注入通道的转换可以打断规则通道的转换, 在注入通道被转换完成之后,规则通道才得以继续转换。 关于模式配置,过于复杂,而且平时基本的使用不会涉及到太多,所以没有做过多理解。
1.3 工程配置
配置工程读取摁键KEY_UP按下时的电压值。 1.开启ADC1的IN1 可以看ADC的IN1对应的IO口是PA1。
2.开启串口1来接收读取到的数据。 3.使能GPIO_PA0。 并将采样通道PA1,接入到PA0。
2. 滤波代码的编写
对于读取的模拟量肯定要进行滤波,不然模拟量变化过快无法使用。 滤波的作用就是减少噪声与干扰对数据测量的影响。
2.0 重写printf函数
#include “stdio.h”
int fputc(int c,FILE *stream)
{
uint8_t ch[1]={c};
HAL_UART_Transmit(&huart1,ch,1,0xFFFF);
return c;
}
2.1 未添加滤波代码和现象
uint16_t ADC_value;
HAL_ADC_Start(&hadc1);
ADC_value=HAL_ADC_GetValue(&hadc1);
HAL_Delay(10);
printf("ADC_value = %d\r\n", ADC_value);
HAL_Delay(500);
串口助手读取到的数据: 可见没有按键摁下时,依然会读到较高电压值。可能是存在上拉电阻、电磁场干扰或者按键抖动之类的问题干扰读数。 特别注意: STM32F4 的 ADC 精度貌似不怎么好, ADC 引脚直接接 GND,都可以读到十 几的数值,相比 STM32F103 来说,要差了一些,在使用的时候,请大家注意下这个问题。
摁下摁键后,也不全读数为4095,可见ADC采样精度一般。
2.2 添加均值滤波和现象
均值滤波的思路就是将一段时间内读取到的数据求和取平均,可以有效的消去抖动。
uint16_t Last_ADC_Value;
uint16_t ADC_Value;
HAL_ADC_Start(&hadc1);
Last_ADC_Value = HAL_ADC_GetValue(&hadc1);
HAL_Delay(10);
for(uint8_t i = 10;i>0;i--)
{
ADC_Value = HAL_ADC_GetValue(&hadc1);
HAL_Delay(10);
Last_ADC_Value = ADC_Value + Last_ADC_Value;
}
Last_ADC_Value = Last_ADC_Value/10;
printf("ADC_value = %d\r\n", Last_ADC_Value);
HAL_Delay(500);
串口助手读取到的数据: 可以看到均值滤波对接地的情况并没有很好的处理效果。
2.3 添加中值滤波和现象
均值滤波的思路就是取一段时间内读取到的数据中间值,可以有效的消去异常值。
uint16_t ADC_Value;
HAL_ADC_Start(&hadc1);
uint16_t buf[20]={0};
for(uint8_t i=0;i<20;i++)
{
buf[i]=HAL_ADC_GetValue(&hadc1);
HAL_Delay(10);
}
ADC_Value = buf[10];
printf("ADC_value = %d\r\n", ADC_Value);
HAL_Delay(500);
串口助手读取到的数据: 其实看到这,很多人都想到了,无论是接高电平还是接地,这组数据都会有很多的波动,无论是均值滤波还是中值滤波的话,都不能有效的消除这些波动。但通过对这组数据的观察,我们发现接地时零出现的次数较多,接高电平时4095出现的次数较多。 由此,我们很容易想到通过重数滤波来实现效果。
2.4 添加众数滤波和现象
均值滤波的思路就是取一段时间内读取到的数据中出现次数最多的数。
uint16_t a=0;
uint16_t Most_num[4095]={0};
uint16_t i=0;
uint16_t max=0;
while中代码:
for(i=0;i<20;i++)
{
HAL_ADC_Start(&hadc1);
a=HAL_ADC_GetValue(&hadc1);
HAL_Delay(10);
Most_num[a]++;
}
max = Most_num[0];
uint16_t k = 0;
for(i=0;i<4096;i++)
{
if(max<Most_num[i])
k = i;
}
printf("ADC_value = %d\r\n", k);
HAL_Delay(500);
for(i=0;i<4095;i++)
Most_num[i]=0;
串口助手读取到的数据: 可以看到滤波效果非常好
3.卡尔曼滤波
|