zhudean11 发表于 2023-12-28 16:52:02

社区闲人 发表于 2023-12-28 16:00
【分享】新型的按键扫描程序 + 累计循环次数。不占用定时器
https://www.stcaimcu.com/forum.php?mod=vie ...

收到,感谢分享{:4_250:}

ulvtkb126 发表于 2024-1-17 11:19:12

用定时器来延时和用 软件延时那个资源用的少些

Snapdragon 发表于 2024-1-21 15:17:01

不用定时器的话可以做成非阻塞形式的,在while(1)里面循环检测,比如加个状态机什么的。

QW123 发表于 2024-1-31 16:33:29

鸿哥的帖子告诉我,放到定时器里面去,1ms中断一次,判断20次,消抖

jwg 发表于 2024-1-31 16:59:40

QW123 发表于 2024-1-31 16:33
鸿哥的帖子告诉我,放到定时器里面去,1ms中断一次,判断20次,消抖

{:4_250:}

21cnsound 发表于 2024-1-31 19:53:32

QW123 发表于 2024-1-31 16:33
鸿哥的帖子告诉我,放到定时器里面去,1ms中断一次,判断20次,消抖

有必要在1ms这么短的中断中判断按键码?会不会时间片太短了?我觉得10-20ms应该比较合理吧,纯技术探讨

hhh402 发表于 2024-2-1 22:46:56

按键扫描最好方式就是放在10ms计时器中断中,因为检测间隔大于按键抖动时间,不需要做消抖检测,而且这种方式不会和其他程序起冲突。其他放在主循环中执行的方式不靠谱,只要再加上其他程序按键扫描时间间隔就会受影响,难道单片机只检查按键别的什么都不做吗?用计时器中断保证了按键检测时间间隔恒定,学会使用定时器中断是复杂变成的基础。

DebugLab 发表于 2024-2-2 00:15:47

本帖最后由 DebugLab 于 2024-2-2 00:26 编辑

不使用中断!不使用定时器!不使用延时函数!
怎么消抖?用执行其他程序的时间消抖!
延时函数?坚决不用!!!
放到主循环里一直循环就行,注意一下主循环的频率10~1000Hz都行,中断函数进去必须马上出来,不能进去100ms以上或者死在里面,如果有以固定频率运行的定时器中断,放在里面也行,这个按键扫描程序几乎不耗时。
基本结构:
void Key_Scan(void)
{
      static bit Key_Flag;
      if(KEY==0)
      {
                if(Key_Flag==1)
                {
                        Key_Flag=0;
                        //在这里加按下要执行的
                }
      }
      else
      {
                if(Key_Flag==0)
                {
                        Key_Flag=1;
                        //在这里加松开要执行的
矩阵键盘因为要切换IO状态读取第二个坐标,需要延时函数消抖,单击双击长按才需要定时器。

矩阵键盘扫描单击双击长按:
#define Init_Up 25 //默认释放时间
#define Init_Down 25 //默认按下时间
#define Init_Long 100 //默认长按时间
#define Init_Disable 1 //默认死区时间
unsigned char Key_Decode(unsigned char temp)
{
      unsigned char key;
      switch(temp)
      {
                case 0x01:key=0;break;
                case 0x02:key=1;break;
                case 0x04:key=2;break;
                case 0x08:key=3;break;
                case 0x10:key=4;break;
                case 0x20:key=5;break;
                case 0x40:key=6;break;
                case 0x80:key=7;break;
                default:key=0xff;
      }
      return key;
}

void Key_Scan(void)
{
      unsigned char temp_x,temp_y;
      X_BUS=0x00;
      Y_BUS=0xff;
      Delay_x10us(100);
      temp_y=Y_BUS;
      if(temp_y!=0xff)
      {
                temp_y=Y_BUS;
                X_BUS=0xff;
                Y_BUS=temp_y;
                Delay_x10us(100);
                temp_x=X_BUS;
                temp_x=Key_Decode(~temp_x);
                temp_y=Key_Decode(~temp_y);
                if(temp_x<8&&temp_y<8)
                {
                        Key=temp_x*8+temp_y;
                        Key_Flag=1;
                }
                else
                {
                        Key_Flag=0;
                        Key=0xff;
                }
      }
      else
      {
                Key_Flag=0;
                Key=0xff;
      }
}

void Clear_Time(bit flag)
{
      TL1=0x00;
      TH1=0x70;
      TR1=flag;
}

void main(void)
{
      Init();      //初始化设置定时器1为不自动重载模式,以20ms为单位,定20ms
                Temp=IapReadByte(Sector0);
      if(Temp>=5&&Temp<=50)
                Time_Up=Temp;
      else
      {
                IapEraseSector(Sector0);
                IapProgramByte(Sector0,Init_Up);
                Time_Up=Init_Up;
      }
      Temp=IapReadByte(Sector1);
      if(Temp>=5&&Temp<=50)
                Time_Down=Temp;
      else
      {
                IapEraseSector(Sector1);
                IapProgramByte(Sector1,Init_Down);
                Time_Down=Init_Down;
      }
      Temp=IapReadByte(Sector2);
      if(Temp>=50&&Temp<=250)
                Time_Long=Temp;
      else
      {
                IapEraseSector(Sector2);
                IapProgramByte(Sector2,Init_Long);
                Time_Long=Init_Long;
      }
      Temp=IapReadByte(Sector3);
      if(Temp>=1&&Temp<=10)
                Time_Disable=Temp;
      else
      {
                IapEraseSector(Sector3);
                IapProgramByte(Sector3,Init_Disable);
                Time_Disable=Init_Disable;
      }
      Key_Temp=255;
      while(1)
      {
                Key_Scan();
                if(Key_State!=Key_Flag)      //按键状态改变
                {
                        Key_State=Key_Flag;
                        Clear_Time(1);
                        if(Key_State)      //按下瞬间
                        {
                              Count_Down=0;
                              Key_Single=1;
                              Key_Long=1;
                              if(Key_Temp==Key)
                                        Key_Double=1;      //和上次一样
                              else      //和上次不一样
                              {
                                        Key_Double=0;
                                        Key_Temp=Key;
                              }
                        }
                        else      //松开瞬间
                        {
                              Count_UP=0;
                              Key_Long=0;
                              if(Key_Single&&Count_Down>Time_Disable&&Count_Down<Time_Down);
                              else
                              {
                                        Clear_Time(0);
                                        Key_Single=0;
                                        Key_Double=0;
                                        Key_Long=0;
                                        Key_Temp=0xff;
                              }
                        }
                }
                if(Key_Flag)      //按下
                {
                        if(Count_UP>=Time_Long&&Key_Long)      //长按
                        {
                              Send_Key(Key_Temp+128);
                              Clear_Time(0);
                              Key_Single=0;
                              Key_Double=0;
                              Key_Long=0;
                              Key_Temp=0xff;
                        }
                }
                else      //松开
                {
                        if(Count_UP>=Time_Up&&Key_Single)      //单击
                        {
                              Send_Key(Key_Temp);
                              Clear_Time(0);
                              Key_Single=0;
                              Key_Double=0;
                              Key_Long=0;
                              Key_Temp=0xff;
                        }
                        if(Count_UP>=Time_Disable&&Count_UP<=Time_Up&&Count_Down>=Time_Disable&&Count_Down<=Time_Down&&Key_Double&&Key_Single)      //双击
                        {
                              Send_Key(Key_Temp+64);
                              Clear_Time(0);
                              Key_Single=0;
                              Key_Double=0;
                              Key_Long=0;
                              Key_Temp=0xff;
                        }
                }
      }
}

void Timer1_Isr(void) interrupt 3
{
      Count_UP++;
      Count_Down++;
      if(Count_UP>250||Count_Down>250)
      {
                Count_UP=0;
                Count_Down=0;
      }
}

QW123 发表于 2024-2-21 16:44:43

21cnsound 发表于 2024-1-31 19:53
有必要在1ms这么短的中断中判断按键码?会不会时间片太短了?我觉得10-20ms应该比较合理吧,纯技术探讨 ...

1ms 是基础值,意思就是: 定时器1ms 中断一次,然后根据每项任务的需求,累加中断次数,达到一个定时器中断,完成不同的任务要求。比方: 按键是累加10次判断是否按下, LED循环使用累加20次去翻转,移动之类的。感觉这个用法在要求不高的条件下,还挺省定时器

zhange 发表于 2024-6-5 09:26:50

myliuyu 发表于 2023-12-28 11:15
amobbs里有几个关于按键的帖子挺不错的可以去参考下

{:5_278:} 这论坛还要钱????
页: 1 [2] 3
查看完整版本: 按键扫描优化方法