找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 641|回复: 16
打印 上一主题 下一主题
收起左侧

51单片机单个IO口ADC检测4个按键不稳定

[复制链接]
跳转到指定楼层
楼主
ID:705846 发表于 2023-11-23 19:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
15W401AS单片机
单个IO口ADC检测4个按键,采用ADC查询方式。

1,按键检测很不稳定,如何增加稳定性;
2,如何做到松键有效的功能;

        P1M1 = 0x83;
        P1M0 = 0x00;

                while(set_state ==0)
                {        
                //CTR按键检测---------------
                                
                        key_vol = GetADCResult_key()*100; //按键电压放大100倍
                        //reset = 375
                        if(key_vol>365 && key_vol<385) //reset按键
                                {
                                        delay_us(10);
                                        if(key_vol>365 && key_vol<385)
                                        {        CTR = CTRoff;
                                                state_cur = 0;
                                        }
                                }         

                        //set按键检测
                        if(key_vol>0 && key_vol<100)
                        //set = 0
                                {
                                        delay_us(10);
                                        if(key_vol>0 && key_vol<100)
                                        {
                                        set_state=1;
                                        setplace=0;
                                        }
                                }
                        //while(!key_vol>1 && key_vol<20);
                }  //默认状态结束

                while(set_state==1)//设置状态
                        {
                        key_vol = GetADCResult_key()*100; //按键电压放大100倍
                        //k2-=335,k1+=252,set=0        

                                if(setplace==0)
                                {
                                        DisplayData[0]=0x31;//时间设置T00分钟
                                        DisplayData[1]=DIG_CODE[set1/10%10];
                                        DisplayData[2]=DIG_CODE[set1%10];
                                }
                                if(setplace==1)
                                {
                                        DisplayData[0]=0x73;//检测功率阀值设置P00
                                        DisplayData[1]=DIG_CODE[set2/10%10];
                                        DisplayData[2]=DIG_CODE[set2%10];
                                }
                                if(setplace==2)
                                {
                                        DisplayData[0]=0x39;//下降比例设置C00
                                        DisplayData[1]=DIG_CODE[set3/10%10];
                                        DisplayData[2]=DIG_CODE[set3%10];
                                }
                                if(setplace==3)
                                {
                                        DisplayData[0]=0x3F;//过流功率设置
                                        DisplayData[1]=0x3E;
                                        DisplayData[2]=0x73;
                                        DisplayData[3]=DIG_CODE[set4/100];
                                        DisplayData[4]=DIG_CODE[set4/10%10];
                                        DisplayData[5]=DIG_CODE[set4%10];
                                }
        
                                if(key_vol>240 && key_vol<265)        //K1+按键设置        
                                {
                                        delay_us(10);
                                        if(key_vol>240 && key_vol<265)
                                        {
                                                if(setplace==0)
                                                {
                                                        set1++;
                                                        if(set1>10)
                                                        set1=10;
                                                }
                                                if(setplace==1)
                                                {
                                                        set2++;
                                                        if(set2>20)
                                                        set2=20;
                                                }
                                                if(setplace==2)
                                                {
                                                        set3++;
                                                        if(set3>99)
                                                        set3=99;
                                                }
                                                if(setplace==3)
                                                {
                                                        set3++;
                                                        if(set3>150)
                                                        set3=150;
                                                }
                                        }        
                                        save();
                                        while(key_vol>240 && key_vol<265);
                                }        
        
                                if(key_vol>325 && key_vol<345)                //K2-按键设置
                                {
                                        delay_us(10);
                                        if(key_vol>325 && key_vol<345)
                                        {
                                                if(setplace==0)
                                                {
                                                        set1--;
                                                        if(set1<1)
                                                        set1=0;
                                                }
                                                if(setplace==1)
                                                {
                                                        set2--;
                                                        if(set2<1)
                                                        set2=1;
                                                }
                                                if(setplace==2)
                                                {
                                                        set3--;
                                                        if(set3<1)
                                                        set3=1;
                                                }
                                                if(setplace==3)
                                                {
                                                        set4--;
                                                        if(set4<1)
                                                        set3=1;
                                                }
        
                                        }        
                                        save();
                                        while(key_vol>325 && key_vol<345);
                                }        
        
                                if(key_vol>0 && key_vol<100)               
                                {
                                        delay_us(10);
                                        if(key_vol>0 && key_vol<100)
                                        {
                                                setplace++;
                                                if(setplace>=4)
                                                {
                                                        setplace=0;
                                                        set_state=0;//返回
                                                }
                                        }        
                                        while(key_vol>0 && key_vol<100);
                                }        
                        } //参数设置结束

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏2 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:213173 发表于 2023-11-23 21:12 | 只看该作者
ADC检测按键不稳定与软、硬件设计密切相关。按键电压放大100倍没有任何实际意义。如果4个分压电阻等值,4个按键的判断值就是0、1/2、2/3、3/4 VCC。一般以10ms周期经3次以上ADC检测取平均值。允许一定的误差。10位ADC的±误差一般不超过10个字。
回复

使用道具 举报

板凳
ID:705846 发表于 2023-11-23 23:02 | 只看该作者
如何做到松按键有效的功能;
回复

使用道具 举报

地板
ID:213173 发表于 2023-11-24 07:30 | 只看该作者
samxon 发表于 2023-11-23 23:02
如何做到松按键有效的功能;
  1. void KeyScan()//按键扫描函数放在约10ms周期的环境运行
  2. {
  3.         static unsigned char count=0;
  4.         static bit sign=0;
  5.         key_vol = GetADCResult_key();//10位ADC,最大值1023
  6.         if(key_vol<1015)//有键按下
  7.         {
  8.                 if(++count>3 && !sign)//丢掉前3次检测,第4次检测值基本稳定
  9.                 {
  10.                         sign=1;
  11.                         if(key_vol<=10)Key_value=1;//xx的值由实际采用的分压电阻计算
  12.                         else if(key_vol>10 && key_vol<=xx)Key_value=2;
  13.                         else if(key_vol>xx && key_vol<=xx)Key_value=3;
  14.                         else if(key_vol>xx && key_vol<=xx)Key_value=4;
  15.                 }
  16.         }
  17.         else //key_vol>=1015表示松手或没有键按下
  18.         {
  19.                 count=0;
  20.                 sign=0;
  21.         }
  22. }
  23. void key_service()                //按键服务程序
  24. {
  25.         switch(Key_value)
  26.         {
  27.                 case 1: /*任务1*/Key_value=0; break;
  28.                 case 2: /*任务2*/Key_value=0; break;
  29.                 case 3: /*任务3*/Key_value=0; break;
  30.                 case 4: /*任务4*/Key_value=0; break;
  31.                 default: break;
  32.         }
  33. }       
复制代码
回复

使用道具 举报

5#
ID:123289 发表于 2023-11-24 09:24 | 只看该作者
1、先调试:每个按键按下去,分别记下各自的按键值作为参考。
2、定标准:以此四个值为中心,给个公差,作为各自按下的界定标准。
这样就可以了。
回复

使用道具 举报

6#
ID:705846 发表于 2023-11-24 19:39 | 只看该作者
double GetADCResult_key()
{

        ADC_CONTR = 0x8F; //按键检测电压采集10001111 P1.7采集器
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        while(!(ADC_CONTR & 0x10)); //等待ADC转换完成
        ADC_CONTR &= ~0x10; //CLOSE ADC
        return (ADC_RES*256+ADC_RESL)*(5.05)/1024; // 根据电压判定按键
}
回复

使用道具 举报

7#
ID:705846 发表于 2023-11-25 10:27 | 只看该作者

请教,把key_scan() 放到1毫秒计时器2内,整个程序都堵死了,是什么原因呢。

void Init_Timer0(void)
{

        AUXR |= 0x80;                        //定时器时钟1T模式
        TMOD &= 0xF0;                        //设置定时器模式
        TL0 = 0xCD;                                //设置定时初始值
        TH0 = 0xD4;                                //设置定时初始值
        TR0 = 1;                                //定时器0开始计时
        ET0 = 1;                                //使能定时器0中断

        AUXR |= 0x04;                        //定时器时钟1T模式
        T2L = 0xCD;                                //设置定时初始值
        T2H = 0xD4;                                //设置定时初始值
        AUXR |= 0x10;                        //定时器2开始计时
        IE2 |= 0x04;                        //使能定时器2中断

}

void Timer2_isr(void) interrupt 12           //定时器2中断入口
{
        time_cur++;
        if(time_cur == Time1s)
        {
        time_cur=0;
        }
        keyadc++;
        if(keyadc == 10)
        {       
                KeyScan();
                keyadc=0;
        }
}
回复

使用道具 举报

8#
ID:192020 发表于 2023-11-25 10:36 | 只看该作者
                        if(key_vol>365 && key_vol<385) //reset按键
                                {
                                        delay_us(10);
                                        if(key_vol>365 && key_vol<385)
                                        {        CTR = CTRoff;
                                                state_cur = 0;
                                        }
                                }     
这个delay_us(10);我猜应该是为了消抖才加的吧?但从程序看是没有消抖作用,key_vol的值延时前后都没有变,应该把延时去掉,多次循环来消抖
回复

使用道具 举报

9#
ID:705846 发表于 2023-11-25 10:52 | 只看该作者
qq475878026 发表于 2023-11-25 10:36
if(key_vol>365 && key_vol365 && key_vol

没有达到目的。key_scan()设置1秒刷新,程序可以正常,10ms就堵死。另外key_vol, key_value似乎也读取不到值。调整了ADC的采样速度也没有改善,注释掉定时器2的key_scan(),程序可以运行起来。
回复

使用道具 举报

10#
ID:213173 发表于 2023-11-25 14:00 | 只看该作者
samxon 发表于 2023-11-25 10:27
请教,把key_scan() 放到1毫秒计时器2内,整个程序都堵死了,是什么原因呢。

void Init_Timer0(void)
...

中断中不宜运行较多代码,只要做个时间标志,代码放在主函数中运行。
void Init_Timer0(void)
{
        AUXR |= 0x80;                        //定时器时钟1T模式
        TMOD &= 0xF0;                        //设置定时器模式
        TL0 = 0xCD;                                //设置定时初始值
        TH0 = 0xD4;                                //设置定时初始值
        TR0 = 1;                                //定时器0开始计时
        ET0 = 1;                                //使能定时器0中断
       
        AUXR |= 0x04;                        //定时器时钟1T模式
        T2L = 0xCD;                                //设置定时初始值
        T2H = 0xD4;                                //设置定时初始值
        AUXR |= 0x10;                        //定时器2开始计时
        IE2 |= 0x04;                        //使能定时器2中断

}

void Timer2_isr(void) interrupt 12           //定时器2中断入口
{
        static unsigned char count=0;
        time_cur++;
        if(time_cur == Time1s)
        {
                time_cur=0;
        }
        if(time_cur%10==0)//10ms
        {
                flag=1;
        }
}

void main()
{
        P1M0 = 0x00;
        P1M1 = 0x01;//P1.0高阻
        Init_Timer0();
        InitADC();
        while(1)
        {
                if(flag)       
                {
                        flag=0;
                        KeyScan();
                        key_service();
                }       
                //其它子程序
        }
}
回复

使用道具 举报

11#
ID:192020 发表于 2023-11-25 14:18 | 只看该作者
samxon 发表于 2023-11-25 10:52
没有达到目的。key_scan()设置1秒刷新,程序可以正常,10ms就堵死。另外key_vol, key_value似乎也读取不 ...

这不是说卡死的原因,是说程序有优化的空间,整个流程下来的delay_us(10)貌似是消抖,实则只检测了一次ad就判断,很容易误触发其他按键。
回复

使用道具 举报

12#
ID:705846 发表于 2023-11-25 18:49 | 只看该作者

能不能解释下 if(++count>3 && !sign)//丢掉前3次检测,第4次检测值基本稳定,
!sign是什么意思
回复

使用道具 举报

13#
ID:213173 发表于 2023-11-25 20:28 | 只看该作者
samxon 发表于 2023-11-25 18:49
能不能解释下 if(++count>3 && !sign)//丢掉前3次检测,第4次检测值基本稳定,
!sign是什么意思

这样写的依据是一旦ADC检测结果小于1015(经验值)就判断有键按下,但由于按键会有抖动,并且ADC检测值也没有稳定,需要延时10~20ms等待稳定。当按键扫描函数在约10ms周期的环境运行时,经过前3次采样(当然可以更多次),ADC检测值基本处于稳定状态。第4次检测值就可以作为判断依据(为简化没有采用多次采样取平均值)。sign位变量既可以反映按键当前状态,也是自锁。长时间持续按住不松手或其它键此时被误按也不会有响应。在此期间count无论加到什么数都不会再次触发。只有等待松手 count、sign清0后按键扫描函数才恢复初始状态。
回复

使用道具 举报

14#
ID:213173 发表于 2023-11-25 21:06 | 只看该作者
void KeyScan()//按键扫描函数放在约10ms周期的环境运行
{
        static unsigned char count=0;
        static bit sign=0;
        key_vol = GetADCResult_key();//10位ADC,最大值1023
        if(key_vol<1015)//有键按下
        {
                if(++count>3 && !sign)//丢掉前3次检测,第4次检测值基本稳定
                {
                        sign=1;
                        //ADC值以4个等值(10K)分压电阻计算
                        if(key_vol<16)                     Key_value=1;//  0 VCC   0 +/- 16
                        else if(key_vol>496 && key_vol<528)Key_value=2;//1/2 VCC 512 +/- 16
                        else if(key_vol>666 && key_vol<698)Key_value=3;//2/3 VCC 682 +/- 16
                        else if(key_vol>752 && key_vol<784)Key_value=4;//3/4 VCC 768 +/- 16
                }
        }
        else //key_vol>=1015表示松手或没有键按下
        {
                count=0;
                sign=0;
        }
}

void key_service()                //按键服务程序
{
        switch(Key_value)
        {
                case 1: /*任务1*/Key_value=0; break;
                case 2: /*任务2*/Key_value=0; break;
                case 3: /*任务3*/Key_value=0; break;
                case 4: /*任务4*/Key_value=0; break;
                default: break;
        }
}

回复

使用道具 举报

15#
ID:1034262 发表于 2023-11-25 21:25 | 只看该作者
STC15W408AS系列的ADC是10位,我做过比较多的ADC按键是8~32个键,32个键时每个键占读数32,而408AS的误差最大10个字,抖动一般小于4个字,足够的。
回复

使用道具 举报

16#
ID:705846 发表于 2023-11-26 09:27 | 只看该作者
wulin 发表于 2023-11-25 21:06
void KeyScan()//按键扫描函数放在约10ms周期的环境运行
{
        static unsigned char count=0;

虽然问题解决,但还有几个问题不明白,请指点一下:
1)为什么ADC结果换算后就判定不准确;
2)按键检测条件内的sign是如何工作的。

改进后的代码
ADC改为中断模式
keyscan()放在定时器内10ms运行
ADC_init()放定时器内10ms运行

按键问题解决;

以前是:key_vol = (ADC_RES*256+ADC_RESL)*(5.05)/1024 //key_vol换算成电压后,放大100倍再判定值。

现在是:key_vol = (ADC_RES*256+ADC_RESL); //直接判定key_vol

void KeyScan()//按键扫描函数放在约10ms周期的环境运行
{
        static unsigned char count=0;
        static bit sign=0;
        temp_keyvol = key_vol;//10位ADC,最大值1023
        if(key_vol<1000)//有键按下
        {
                if(++count>3 && !sign) //丢掉前3次检测,第4次检测值基本稳定
                {
                        sign=1;
                        if(temp_keyvol<=50)key_value=1;//set键按下
                        else if(temp_keyvol>450 && temp_keyvol<=550)key_value=2; //K1+键按下
                        else if(temp_keyvol>650 && temp_keyvol<=700)key_value=3; //K2-键按下
                        else if(temp_keyvol>750 && temp_keyvol<=800)key_value=4; //Reset键按下
                }
        }
        else //key_vol>=1015表示松手或没有键按下
        {
                count=0;
                sign=0;
        }
}
回复

使用道具 举报

17#
ID:213173 发表于 2023-11-26 14:16 | 只看该作者
samxon 发表于 2023-11-26 09:27
虽然问题解决,但还有几个问题不明白,请指点一下:
1)为什么ADC结果换算后就判定不准确;
2)按键检 ...

1)为什么ADC结果换算后就判定不准确;
你不是做电压表,转换结果不需要换算成电压值。如果VCC不十分精准或有波动再加上运算后舍弃小数误差就更大。因为转换结果是比值,直接使用转换结果不会因VCC变化而变化。
2)按键检测条件内的sign是如何工作的。
sign初始为0,当检测到有键按下,count开始计数,但计数到4,满足判断条件,执行花括号内代码,sign=1;。当再次运行到 if(++count>3 && !sign) ,判断条件已经不成立。也就不会重复响应。必须等按键松手才能恢复初始状态。sign在这里起到自锁作用。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表