找回密码
 立即注册

QQ登录

只需一步,快速开始

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

终于用自己的想法摆脱MCU按键计时总使用while死循环了(非阻塞?),61节日快乐

  [复制链接]
跳转到指定楼层
楼主
ID:728915 发表于 2021-6-1 19:51 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
//*************************首先感叹一下C语言的灵活性。
/************************************以下基于STM8单片机**************************/

/****为解决按键时采用死循环,使CPU进入假死机状态,大大浪费单片机资源的情况*******/

/***以下文字为个人理解,大佬建议跳过,当然,也欢迎各位指出偏驳之处***************/

/**单片机在一个时间点只能进行一个动作,并不能做到真正意义上的同时多任务动作,只能在结束一个动作后进行下一个动作,
由于每一个动作所需要耗费的时间特别短,所以在一定情况下可以看作多任务进程。
以下利用中断计时同理,在每一次计时的动作耗费时间特别短,做完这个动作CPU就可以进行其它动作了,相当于摆脱了死循环,
不会让CPU在那无意义地卡住,陷入要等待其它打破标志的死循环**********************/

/***粗略非阻塞(不知道这算不算非阻塞)延时/计时思路:定时器进行规定时间的中断进入,每进一次中断,(符合条件下)次数+1,以此作为粗略的基础时间(1ms/5ms/10ms/100ms……)*****************************/

/*********************以下为代码内容***************************************/

#include <stm8s.h>
#include <stm8s_gpio.h>
#include <stm8s_tim4.h>
#include <stm8s_clk.h>


//按键K1-K2定义,只能读取是否为“0”(按下)/不为“0”(松开),不能读取是否为“1”或者其它的
#define K1 (GPIO_ReadInputData(GPIOC)&GPIO_PIN_1)   
#define K2 (GPIO_ReadInputData(GPIOC)&GPIO_PIN_2)   

//L1-L2灯亮/灭定义(继电器K1-K2工作/不工作定义)
#define ON  1
#define OFF 0
#define L1(ON_OFF)  if(ON_OFF==ON)GPIO_WriteHigh(GPIOB, GPIO_PIN_3);\
                      else GPIO_WriteLow(GPIOB, GPIO_PIN_3)
#define L2(ON_OFF)  if(ON_OFF==ON)GPIO_WriteHigh(GPIOB, GPIO_PIN_2);\
                      else GPIO_WriteLow(GPIOB, GPIO_PIN_2)

//引脚初始化
void GPIO_Config()
  {

    //继电器三极管控制引脚 PB2-3 初始化,高电平导通
   GPIO_Init(GPIOB, GPIO_PIN_2|GPIO_PIN_3, GPIO_MODE_OUT_PP_LOW_FAST);

   //按键初始化:无中断无浮点上拉输入
   GPIO_Init(GPIOC, GPIO_PIN_1|GPIO_PIN_2, GPIO_MODE_IN_PU_NO_IT);

  }


//初始化定时器TIM4   
void Init_Timer4(void)
{
    TIM4_DeInit();  
    TIM4_TimeBaseInit(TIM4_PRESCALER_64, 0xFA);    /*初始化时基单元。128分频 ,x=16M/128 ,                                                                                                                          自动重载寄存器值为0xfa=16*15+10=250,
                                                                                 中断溢出=x/0xfa 进中断一次2ms。
                                                                                 64分频下0xFA进中断一次1ms ;                                                                                                                                                 128分频下0x19进中断一次0.2ms***/
    TIM4_ClearFlag(TIM4_FLAG_UPDATE);   
    TIM4_ITConfig(TIM4_IT_UPDATE, ENABLE);    //使能TIM4更新中断
    TIM4_Cmd(ENABLE);     //启动定时器
}


//定义全局变量:TIM4每1ms进中断一次 的各按键计次时间sk,各按键对应上一次的计次时间skt
//因为是全局变量,可以在其它地方调用数值后手动进行清零/赋值操作
u16 sk1=0;
u16 sk1t=0;
u16 sk2=0;
u16 sk2t=0;

//TIM4中断服务函数:按键按下开始计时,按键松开停止计时;
//上一次按键按下持续时间数值一直保持,直到下一次按键有效按下后松开(去抖动)。
//各按键独立计时,互不影响
INTERRUPT_HANDLER(TIM4_UPD_OVF_IRQHandler, 23)
{

   //上一次K1按键按下时长:sk1t
    if(K1==0)   //按键按下
     {
       sk1++;   //TIM4每1ms进中断一次,此时sk1+1
       if(sk1==59999)    //防止达到计数上限,约59秒
       {
         sk1=0;
       }
     }  
    if(K1!=0)   //按键松开
     {
       if(sk1>=30)    //防抖动,也可以避免时间一直刷新
       {
         sk1t=sk1;    //因为是全局变量,可以在调用数值结束后手动进行清零操作
       }
       sk1=0;     
     }

   //上一次K2按键按下时长:sk2t   
    if(K2==0)   //按键按下
     {
       sk2++;   //TIM4每1ms进中断一次,此时sk2+1
       if(sk2==59999)    //防止达到计数上限,约59秒
       {
         sk2=0;
       }
     }
    if(K2!=0)   //按键松开
     {
       if(sk2>=30)   //防抖动,也可以避免时间一直刷新
       {
         sk2t=sk2;      
       }
       sk2=0;     
     }

  TIM4_ClearITPendingBit(TIM4_IT_UPDATE);   //清除标志位
}


//控制LED亮灭:上一次按键达到一定时间后亮,否则灭。各按键独立工作,互不影响
void LED12(void)
{

// 按键K1控制灯L1
   if(sk1t>=500)    //按键K1持续按下约500ms
   {
     L1(ON);
   }
   else
   {
     L1(OFF);
   }

// 按键K2控制灯L2
   if(sk2t>=5000)    //按键K1持续按下约5000ms
   {
     L2(ON);
   }
   else
   {
     L2(OFF);
   }
}


//主函数
void main(void)
{
  /*下列语句除 while(1){}; 外,其余语句都只执行一次,TIM4的定时器配置是只在函数Init_Timer4()里设置一次*/

  CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1);    //系统时钟初始化为内部时钟16M
  Init_Timer4();     //初始化TIM4定时器配置
  GPIO_Config();    //初始化IO口
  enableInterrupts();    //使能中断  
  while(1)
  {
   LED12();
  };   
}


//解决报错
#ifdef USE_FULL_ASSERT
void assert_failed(u8* file, u32 line)
{
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

  /* Infinite loop */
  while (1)
  {
  }
}
#endif


/***用我理解的方式,有些地方写得不是很好,水平比较低,所以在网上看大佬写的总是感觉云里雾里,复制下来编译一哈发现总是会少些东西,
然后又不知道怎么补,木得办法咯,只能自己慢慢想,啊哈哈。最后祝各位61节日快乐,谁还不是个幼儿园没毕业/刚毕业的孩子呢!***/




评分

参与人数 1黑币 +100 收起 理由
admin + 100 共享资料的黑币奖励!

查看全部评分

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

使用道具 举报

沙发
ID:101106 发表于 2021-6-4 17:35 | 只看该作者
用中断方式处理按键 长按、双击等特殊操作比较难识别...........
回复

使用道具 举报

板凳
ID:728915 发表于 2021-6-5 19:21 | 只看该作者
killalljp 发表于 2021-6-4 17:35
用中断方式处理按键 长按、双击等特殊操作比较难识别...........

嗯,那就需要我多转转脑子了,主要是中断这种思想,比较费脑子,啊哈哈
回复

使用道具 举报

地板
ID:256945 发表于 2021-6-11 17:30 | 只看该作者
killalljp 发表于 2021-6-4 17:35
用中断方式处理按键 长按、双击等特殊操作比较难识别...........

要精准延时,然后自己定义一个延时时间,在延时时间内再次点击则判断为双击,超过则为两次单击
回复

使用道具 举报

5#
ID:116773 发表于 2021-6-12 07:39 | 只看该作者
killalljp 发表于 2021-6-4 17:35
用中断方式处理按键 长按、双击等特殊操作比较难识别...........

按键按下时让定时器/计数器开始计数,按键松开停止计数,这样就可以知道按键按下时间的长短了。
回复

使用道具 举报

6#
ID:298123 发表于 2021-6-16 13:40 | 只看该作者
void task_KeyScan (void *params)
{
    static uint8_t s_KeyState = KEY_STATE_INIT;
    static uint16_t s_KeyTimeCount = 0;
    static uint16_t s_LastKey = KEY_VALUE_NULL;
    static uint16_t KeyNew = KEY_VALUE_NULL, KeyOld = KEY_VALUE_NULL;
    static uint8_t KeyReport = 0;
    static uint16_t key_continue = 0;
       
          do{
   
                KeyNew = myKeyScan();

    switch(s_KeyState)
    {
        case KEY_STATE_INIT:
                {
                    if(KEY_VALUE_NULL != (KeyNew))
                    {
                                                                                          s_KeyTimeCount = 0;
                                                                              key_continue = 0;
                                                                                          KeyReport = 0;
                        s_KeyState = KEY_STATE_WOBBLE;
                        KeyOld =         KeyNew;                                                                                       
                    }
                                                                                                   
                }
        break ;

        case KEY_STATE_WOBBLE:
                {
                                                                          if(KeyNew == KeyOld) {
                                                                                                if(++s_KeyTimeCount > 1) {
                                                                                                          s_KeyTimeCount = 0;
                            s_KeyState = KEY_STATE_PRESS;
                                                                                                }
                                                                                }
                    else         {
                                                                                    s_KeyState = KEY_STATE_INIT;
                                                                                }                                                                       
                }
        break ;

        case KEY_STATE_PRESS:
                {
                    if(KeyOld == KeyNew)
                    {                                                                                  
                        s_LastKey = KeyNew;
                                                                  s_LastKey &= ~KEY_FLAG_TRUE;
                                                                                          s_LastKey |= KEY_DOWN;
                        KeyReport = 1;
                        s_KeyState = KEY_STATE_LONG;
                    }
                    else
                    {
                        s_KeyState = KEY_STATE_INIT;
                    }
                }
        break ;

        case KEY_STATE_LONG :
                {
                    if( KeyOld == KeyNew )
                    {
                        if(++s_KeyTimeCount > KEY_LONG_PERIOD)
                        {   key_continue = s_KeyTimeCount;
                            s_KeyTimeCount = 0;
                                                                                                          s_LastKey &= ~KEY_FLAG_TRUE;
                            s_LastKey |= KEY_LONG;
                            KeyReport = 1;
                            s_KeyState = KEY_STATE_CONTINUE;
                        }
                    }
                    else
                    {
                        s_KeyState = KEY_STATE_RELEASE;
                    }
                }
        break ;

        case KEY_STATE_CONTINUE :
                {
                    if(KeyOld == KeyNew)
                    {
                        if(++s_KeyTimeCount > KEY_CONTINUE_PERIOD)
                        {
                                                                                                          key_continue = s_KeyTimeCount;
                            s_KeyTimeCount = 0 ;
                                                                                                          s_LastKey &= ~KEY_FLAG_TRUE;
                            s_LastKey |= KEY_CONTINUE;
                                                                                                          KeyReport = 1;                                                                                                                                
                        }
                    }
                    else
                    {
                        s_KeyState = KEY_STATE_RELEASE;
                    }
                }
        break ;

        case KEY_STATE_RELEASE :
                {
                                                                          s_LastKey &= ~KEY_UP;
                                                                          //s_LastKey &= ~KEY_FLAG_TRUE;
                    s_LastKey |= KEY_UP;
                                                                          KeyReport = 1;
                    s_KeyState = KEY_STATE_INIT;
                }
        break ;

        default :  s_KeyState = KEY_STATE_INIT;
                                        break ;
    }
                if(KeyReport) {
                            //OSMboxPost(Key_Box, (void *)s_LastKey);
          KeyReport = 0;
          Key_SetValue(s_LastKey, key_continue);
                }
               
          vTaskDelay(4);         /* 40 MS  */
               
        }while(1);
               
}
回复

使用道具 举报

7#
ID:298123 发表于 2021-6-16 13:41 | 只看该作者
建议网上找找这个状态机方式的按键程序,可以解决各种复杂功能。 用定时器40m扫描一次
回复

使用道具 举报

8#
ID:519089 发表于 2021-7-14 20:16 | 只看该作者
状态机就解决了,,,
回复

使用道具 举报

9#
ID:142059 发表于 2021-7-18 12:07 | 只看该作者
回复

使用道具 举报

10#
ID:954192 发表于 2021-7-18 16:06 | 只看该作者
学习了,谢谢
回复

使用道具 举报

11#
ID:728915 发表于 2021-7-19 18:55 | 只看该作者
Angle145 发表于 2021-7-18 12:07
可以参考我写的http://www.51hei.com/bbs/dpj-210244-1.html

谢谢!
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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