开场白:
接连判别N次共同性的滤波法,是为了防止结尾小数点的数据偶然跳动。这种滤波办法的原理跟我在按键扫描中去颤动的原理是如出一辙的,被我频频地应用在很多的工控项目中。
这一节要教会我们一个常识点:接连判别N次共同性的滤波法。
详细原理:当某个采样变量发生改变时,有两种或许,一种或许是外界的一个瞬间搅扰。另一种或许是变量的确发生改变。为了有用去除搅扰,当发现变量有改变时,我会接连收集N次,假如接连N次都是共同的成果,我才以为不是搅扰。假如中心只需呈现一次不共同,我会立刻把计数器清零,这一步是精华,很要害。
详细内容,请看源代码解说。
(1) 硬件渠道.
依据朱兆祺51单片机学习板。
(2)完成功用:
本程序有2个部分显现。
第1个部分是第8,7,6,5位数码管,显现没有通过滤波处理的实践电压值。此刻能观察到未经滤波的数据不太安稳,结尾小数点数据会有跳动的现象
第2个部分是第4,3,2,1位数码管,显现通过特定算法滤波后的实践电压值。此刻能观察到通过滤波后的数据很安稳,没有跳动的现象。而且显现的电压值跟未通过滤波的电压值几乎是完全共同,不会呈现上一节用区间滤波法所留下的0.02V差错问题。
体系保存3位小数点。手动调理可调电阻时,能够看到显现的数据在改变。
(3)源代码解说如下:
#include “REG52.H”
#define const_N 8 //接连判别N次共同性滤波办法中,N的取值
#define const_voice_short 40 //蜂鸣器短叫的持续时间
void initial_myself(void);
void initial_peripheral(void);
void delay_short(unsigned int uiDelayShort);
void delay_long(unsigned int uiDelaylong);
//驱动数码管的74HC595
void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01);
void display_drive(void); //显现数码管字模的驱动函数
void display_service(void); //显现的窗口菜单服务程序
//驱动LED的74HC595
void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01);
void T0_time(void); //守时中止函数
void ad_sampling_service(void); //AD采样与处理的服务程序
sbit led_dr=P3^5; //LED灯
sbit beep_dr=P2^7; //蜂鸣器的驱动IO口
sbit dig_hc595_sh_dr=P2^0; //数码管的74HC595程序
sbit dig_hc595_st_dr=P2^1;
sbit dig_hc595_ds_dr=P2^2;
sbit hc595_sh_dr=P2^3; //LED灯的74HC595程序
sbit hc595_st_dr=P2^4;
sbit hc595_ds_dr=P2^5;
sbit adc0832_clk_dr = P1^2; // 界说adc0832的引脚
sbit adc0832_cs_dr = P1^0;
sbit adc0832_data_sr_dr = P1^1;
unsigned char ucDigShow8; //第8位数码管要显现的内容
unsigned char ucDigShow7; //第7位数码管要显现的内容
unsigned char ucDigShow6; //第6位数码管要显现的内容
unsigned char ucDigShow5; //第5位数码管要显现的内容
unsigned char ucDigShow4; //第4位数码管要显现的内容
unsigned char ucDigShow3; //第3位数码管要显现的内容
unsigned char ucDigShow2; //第2位数码管要显现的内容
unsigned char ucDigShow1; //第1位数码管要显现的内容
unsigned char ucDigDot8; //数码管8的小数点是否显现的标志
unsigned char ucDigDot7; //数码管7的小数点是否显现的标志
unsigned char ucDigDot6; //数码管6的小数点是否显现的标志
unsigned char ucDigDot5; //数码管5的小数点是否显现的标志
unsigned char ucDigDot4; //数码管4的小数点是否显现的标志
unsigned char ucDigDot3; //数码管3的小数点是否显现的标志
unsigned char ucDigDot2; //数码管2的小数点是否显现的标志
unsigned char ucDigDot1; //数码管1的小数点是否显现的标志
unsigned char ucDigShowTemp=0; //暂时中心变量
unsigned char ucDisplayDriveStep=1; //动态扫描数码管的过程变量
unsigned char ucWd1Part1Update=1; //在窗口1中,部分1的更新显现标志
unsigned char ucWd1Part2Update=1; //在窗口1中,部分2的更新显现标志
unsigned char ucTemp1=0; //中心过渡变量
unsigned char ucTemp2=0; //中心过渡变量
unsigned char ucTemp3=0; //中心过渡变量
unsigned char ucTemp4=0; //中心过渡变量
unsigned char ucTemp5=0; //中心过渡变量
unsigned char ucTemp6=0; //中心过渡变量
unsigned char ucTemp7=0; //中心过渡变量
unsigned char ucTemp8=0; //中心过渡变量
unsigned char ucAD=0; //AD值
unsigned char ucCheckAD=0; //用来做校验比照的AD值
unsigned long ulTemp=0; //参加换算的中心变量
unsigned long ulTempFilterV=0; //参加换算的中心变量
unsigned long ulBackupFilterV=5000; //备份最新采样数据的中心变量
unsigned char ucSamplingCnt=0; //记载接连N次采样的计数器
unsigned long ulV=0; //未经滤波处理的实时电压值
unsigned long ulFilterV=0; //通过滤波后的实时电压值
//依据原理图得出的共阴数码管字模表
code unsigned char dig_table[]=
{
0x3f, //0 序号0
0x06, //1 序号1
0x5b, //2 序号2
0x4f, //3 序号3
0x66, //4 序号4
0x6d, //5 序号5
0x7d, //6 序号6
0x07, //7 序号7
0x7f, //8 序号8
0x6f, //9 序号9
0x00, //无 序号10
0x40, //- 序号11
0x73, //P 序号12
};
void main()
{
initial_myself();
delay_long(100);
initial_peripheral();
while(1)
{
ad_sampling_service(); //AD采样与处理的服务程序
display_service(); //显现的窗口菜单服务程序
}
}
void ad_sampling_service(void) //AD采样与处理的服务程序
{
unsigned char i;
ucAD=0; //AD值
ucCheckAD=0; //用来做校验比照的AD值
/* 片选信号置为低电平 */
adc0832_cs_dr = 0;
/* 榜首个脉冲,开端位 */
adc0832_data_sr_dr = 1;
adc0832_clk_dr = 0;
delay_short(1);
adc0832_clk_dr = 1;
/* 第二个脉冲,挑选通道 */
adc0832_data_sr_dr = 1;
adc0832_clk_dr = 0;
adc0832_clk_dr = 1;
/* 第三个脉冲,挑选通道 */
adc0832_data_sr_dr = 0;
adc0832_clk_dr = 0;
adc0832_clk_dr = 1;
/* 数据线输出高电平 */
adc0832_data_sr_dr = 1;
delay_short(2);
/* 榜首个下降沿 */
adc0832_clk_dr = 1;
adc0832_clk_dr = 0;
delay_short(1);
/* AD值开端送出 */
for (i = 0; i < 8; i++)
{
ucAD <<= 1;
adc0832_clk_dr = 1;
adc0832_clk_dr = 0;
if (adc0832_data_sr_dr==1)
{
ucAD |= 0x01;
}
}
/* 用于校验的AD值开端送出 */
for (i = 0; i < 8; i++)
{
ucCheckAD >>= 1;
if (adc0832_data_sr_dr==1)
{
ucCheckAD |= 0x80;
}
adc0832_clk_dr = 1;
adc0832_clk_dr = 0;
}
/* 片选信号置为高电平 */
adc0832_cs_dr = 1;
if(ucCheckAD==ucAD) //查验持平
{
ulTemp=0; //把char类型数据赋值给long类型数据之前,必须先清零
ulTemp=ucAD; //把char类型数据赋值给long类型数据,参加乘除法运算的数据,为了防止运算成果溢出,我都用long类型
/* 注释一:
* 由于保存3为小数点,这儿的5000代表5.000V。ulTemp/255代表分辨率.
* 有些书上说8位AD最高分辩可到达256级(0xff+1),我以为这种说法是过错的。
* 8位AD最高分辩应该是255级(0xff),所以这儿除以255,而不是256.
*/
ulTemp=5000*ulTemp/255; //进行电压换算
ulV=ulTemp; //得到未经滤波处理的实时电压值
ucWd1Part1Update=1; //部分更新显现未经滤波处理的电压
/* 注释二:
* 以下接连判别N次共同性的滤波法,为了防止结尾小数点的数据偶然跳动。
* 这种滤波办法的原理跟我在按键扫描中的去颤动原理是如出一辙的,被我频频
* 地应用在很多的工控项目中。
* 详细原理:当某个采样变量发生改变时,有两种或许,一种或许是外界的一个瞬间搅扰。
* 另一种或许是变量的确发生改变。为了有用去除搅扰,当发现变量有改变时,
* 我会接连收集N次,假如接连N次都是共同的成果,我才以为不是搅扰。假如中心
* 只需呈现一次不共同,我会立刻把计数器清零,这一步是精华,很要害。
*
*/
if(ulTempFilterV!=ulTemp) //发现变量有改变
{
ucSamplingCnt++; //计数器累加
if(ucSamplingCnt>const_N) //假如接连N次都是共同的,则以为不是搅扰。的确有数据需求更新显现。这儿的const_N取值是8
{
ucSamplingCnt=0;
ulTempFilterV=ulTemp; //及时保存更新了的数据,便利下一次有新数据比照做准备
ulFilterV=ulTempFilterV; //得到通过滤波处理的实时电压值
ucWd1Part2Update=1; //部分更新显现通过滤波处理的电压
}
}
else
{
ucSamplingCnt=0; //只需呈现一次不共同,我会立刻把计数器清零,这一步是精华,很要害。
}
}
}
void display_service(void) //显现的窗口菜单服务程序
{
if(ucWd1Part1Update==1)//未经滤波处理的实时电压更新显现
{
ucWd1Part1Update=0;
ucTemp8=ulV%10000/1000; //显现电压值个位
ucTemp7=ulV%1000/100; //显现电压值小数点后第1位
ucTemp6=ulV%100/10; //显现电压值小数点后第2位
ucTemp5=ulV%10; //显现电压值小数点后第3位
ucDigShow8=ucTemp8; //数码管显现实践内容
ucDigShow7=ucTemp7;
ucDigShow6=ucTemp6;
ucDigShow5=ucTemp5;
}
if(ucWd1Part2Update==1)//通过滤波处理后的实时电压更新显现
{
ucWd1Part2Update=0;
ucTemp4=ulFilterV%10000/1000; //显现电压值个位
ucTemp3=ulFilterV%1000/100; //显现电压值小数点后第1位
ucTemp2=ulFilterV%100/10; //显现电压值小数点后第2位
ucTemp1=ulFilterV%10; //显现电压值小数点后第3位
ucDigShow4=ucTemp4; //数码管显现实践内容
ucDigShow3=ucTemp3;
ucDigShow2=ucTemp2;
ucDigShow1=ucTemp1;
}
}
void display_drive(void)
{
//以下程序,假如加一些数组和移位的元素,还能够紧缩容量。可是鸿哥寻求的不是容量,而是明晰的解说思路
switch(ucDisplayDriveStep)
{
case 1: //显现第1位
ucDigShowTemp=dig_table[ucDigShow1];
if(ucDigDot1==1)
{
ucDigShowTemp=ucDigShowTemp|0x80; //显现小数点
}
dig_hc595_drive(ucDigShowTemp,0xfe);
break;
case 2: //显现第2位
ucDigShowTemp=dig_table[ucDigShow2];
if(ucDigDot2==1)
{
ucDigShowTemp=ucDigShowTemp|0x80; //显现小数点
}
dig_hc595_drive(ucDigShowTemp,0xfd);
break;
case 3: //显现第3位
ucDigShowTemp=dig_table[ucDigShow3];
if(ucDigDot3==1)
{
ucDigShowTemp=ucDigShowTemp|0x80; //显现小数点
}
dig_hc595_drive(ucDigShowTemp,0xfb);
break;
case 4: //显现第4位
ucDigShowTemp=dig_table[ucDigShow4];
if(ucDigDot4==1)
{
ucDigShowTemp=ucDigShowTemp|0x80; //显现小数点
}
dig_hc595_drive(ucDigShowTemp,0xf7);
break;
case 5: //显现第5位
ucDigShowTemp=dig_table[ucDigShow5];
if(ucDigDot5==1)
{
ucDigShowTemp=ucDigShowTemp|0x80; //显现小数点
}
dig_hc595_drive(ucDigShowTemp,0xef);
break;
case 6: //显现第6位
ucDigShowTemp=dig_table[ucDigShow6];
if(ucDigDot6==1)
{
ucDigShowTemp=ucDigShowTemp|0x80; //显现小数点
}
dig_hc595_drive(ucDigShowTemp,0xdf);
break;
case 7: //显现第7位
ucDigShowTemp=dig_table[ucDigShow7];
if(ucDigDot7==1)
{
ucDigShowTemp=ucDigShowTemp|0x80; //显现小数点
}
dig_hc595_drive(ucDigShowTemp,0xbf);
break;
case 8: //显现第8位
ucDigShowTemp=dig_table[ucDigShow8];
if(ucDigDot8==1)
{
ucDigShowTemp=ucDigShowTemp|0x80; //显现小数点
}
dig_hc595_drive(ucDigShowTemp,0x7f);
break;
}
ucDisplayDriveStep++;
if(ucDisplayDriveStep>8) //扫描完8个数码管后,从头从榜首个开端扫描
{
ucDisplayDriveStep=1;
}
}
//数码管的74HC595驱动函数
void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01)
{
unsigned char i;
unsigned char ucTempData;
dig_hc595_sh_dr=0;
dig_hc595_st_dr=0;
ucTempData=ucDigStatusTemp16_09; //先送高8位
for(i=0;i<8;i++)
{
if(ucTempData>=0x80)dig_hc595_ds_dr=1;
else dig_hc595_ds_dr=0;
dig_hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器
delay_short(1);
dig_hc595_sh_dr=1;
delay_short(1);
ucTempData=ucTempData<<1;
}
ucTempData=ucDigStatusTemp08_01; //再先送低8位
for(i=0;i<8;i++)
{
if(ucTempData>=0x80)dig_hc595_ds_dr=1;
else dig_hc595_ds_dr=0;
dig_hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器
delay_short(1);
dig_hc595_sh_dr=1;
delay_short(1);
ucTempData=ucTempData<<1;
}
dig_hc595_st_dr=0; //ST引脚把两个寄存器的数据更新输出到74HC595的输出引脚上而且锁存起来
delay_short(1);
dig_hc595_st_dr=1;
delay_short(1);
dig_hc595_sh_dr=0; //拉低,抗搅扰就增强
dig_hc595_st_dr=0;
dig_hc595_ds_dr=0;
}
//LED灯的74HC595驱动函数
void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01)
{
unsigned char i;
unsigned char ucTempData;
hc595_sh_dr=0;
hc595_st_dr=0;
ucTempData=ucLedStatusTemp16_09; //先送高8位
for(i=0;i<8;i++)
{
if(ucTempData>=0x80)hc595_ds_dr=1;
else hc595_ds_dr=0;
hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器
delay_short(1);
hc595_sh_dr=1;
delay_short(1);
ucTempData=ucTempData<<1;
}
ucTempData=ucLedStatusTemp08_01; //再先送低8位
for(i=0;i<8;i++)
{
if(ucTempData>=0x80)hc595_ds_dr=1;
else hc595_ds_dr=0;
hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器
delay_short(1);
hc595_sh_dr=1;
delay_short(1);
ucTempData=ucTempData<<1;
}
hc595_st_dr=0; //ST引脚把两个寄存器的数据更新输出到74HC595的输出引脚上而且锁存起来
delay_short(1);
hc595_st_dr=1;
delay_short(1);
hc595_sh_dr=0; //拉低,抗搅扰就增强
hc595_st_dr=0;
hc595_ds_dr=0;
}
void T0_time(void) interrupt 1 //守时中止
{
TF0=0; //铲除中止标志
TR0=0; //关中止
display_drive(); //数码管字模的驱动函数
TH0=0xfe; //重装初始值(65535-500)=65035=0xfe0b
TL0=0x0b;
TR0=1; //开中止
}
void delay_short(unsigned int uiDelayShort)
{
unsigned int i;
for(i=0;i
{
; //一个分号相当于履行一条空句子
}
}
void delay_long(unsigned int uiDelayLong)
{
unsigned int i;
unsigned int j;
for(i=0;i
{
for(j=0;j<500;j++) //内嵌循环的空指令数量
{
; //一个分号相当于履行一条空句子
}
}
}
void initial_myself(void) //榜首区 初始化单片机
{
led_dr=0;//LED灯默许封闭
beep_dr=1; //用PNP三极管操控蜂鸣器,输出高电平时不叫。
hc595_drive(0x00,0x00); //封闭一切通过别的两个74HC595驱动的LED灯
TMOD=0x01; //设置守时器0为工作方式1
TH0=0xfe; //重装初始值(65535-500)=65035=0xfe0b
TL0=0x0b;
}
void initial_peripheral(void) //第二区 初始化外围
{
ucDigDot8=1; //显现未通过滤波电压的小数点
ucDigDot7=0;
ucDigDot6=0;
ucDigDot5=0;
ucDigDot4=1; //显现通过滤波后电压的小数点
ucDigDot3=0;
ucDigDot2=0;
ucDigDot1=0;
EA=1; //开总中止
ET0=1; //答应守时中止
TR0=1; //发动守时中止
}
总结陈词:
在单片机AD采样的体系中,我常用的滤波办法有求平均值法,区间法,接连判别N次共同性这三种办法。读者能够依据不同的体系特色挑选对应的滤波办法,有一些要求高的体系还能够把三种滤波办法混合在一起用。关于AD采样的常识到本节现已讲完,下一节会讲什么新内容呢?欲知概况,请听下回分解—–return句子不为人知的用法。