找回密码
 立即注册

QQ登录

只需一步,快速开始

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

STM32状态机思想实现单击、双击、长按 源程序

  [复制链接]
跳转到指定楼层
楼主
ID:763998 发表于 2020-5-29 09:35 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
/*直接复制以下代码,实现,自己在独立研究吸收*/


#include "stm32f10x.h"
#include "bitband_cm3.h"
#include "systick.h"
#define N_key    0             //无键
#define S_key    1             //单键
#define D_key    2             //双键
#define L_key    3             //长键
#define KEY_AN (GPIOA->IDR & 1<<0)
#define BEEF PCout(3)

/**********************************************************************
*函数名:delay_us
*功  能:延迟1us
*参  数:us最大2^24/9=1864135us
*返  回:无
*备  注:无
**********************************************************************/
void delay_us(u16 us)
{
        SysTick->LOAD = us * 9;        //装载计数值
        SysTick->VAL = 0;                //清空当前值
        SysTick->CTRL |= 1;        //使能计数器
        while(!(SysTick->CTRL & (1 << 16)));//等待计数结束
        SysTick->CTRL &=~ 1;//关闭计数
}

//LED初始化
void LED_Init(void)
{
#if 0
        RCC->APB2ENR |= 3<<3;//开启PB/PC口时钟
        GPIOB->CRL &=~(0XF<<4*1);//清PB1
        GPIOB->CRL |=(0X3<<4*1);//通用输出 50M
        GPIOC->CRL &=~(0XF<<4*5);//清PC5
        GPIOC->CRL |=(0X3<<4*5);//通用输出 50M
        GPIOB->ODR |=(1<<1);//默认给高电平,关灯,
        GPIOC->ODR |=(5<<1);
//        GPIOB->ODR &=~(1<<1);//点灯
//        GPIOC->ODR &=~(5<<1);
        #else  
        GPIO_InitTypeDef GPIO_InitStruct;
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOB,ENABLE);//开启PB/PC口时钟.
        GPIO_InitStruct.GPIO_Pin=GPIO_Pin_1;
        GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;//通用推挽
        GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;//50M
        GPIO_Init(GPIOB, &GPIO_InitStruct);//PB1
        GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;
        GPIO_Init(GPIOC, &GPIO_InitStruct);//PC5
        GPIO_SetBits(GPIOB, GPIO_Pin_1);//默认给高电平,关灯,不能少了这步,因为输出数据寄存器默认值给低电平
        GPIO_SetBits(GPIOC, GPIO_Pin_5);
//        GPIO_ResetBits(GPIOB, GPIO_Pin_1);//开灯
//        GPIO_ResetBits(GPIOC, GPIO_Pin_5);

//        PBout(1)=0;
//        PCout(5)=0;
        #endif
}
//按键初始化
void KEY_Iint(void)
{
        RCC->APB2ENR |= 1<<2;//开启PA口时钟
}
//蜂鸣器初始化
void BEEF_Iint(void)
{
        RCC->APB2ENR |= 1<<4;//开启PC口时钟
        GPIOC->CRL &=~(0XF<<4*3);//清PC3
        GPIOC->CRL |=(0X3<<4*3);//通用输出 50M
}


/*
        驱动层
        1.完成按键的消抖,松手检测
        2.把过程细分为一个个状态
        3.实现长按与单击功能

        按键初始态
        按键确认态
        按键计时态
        等待按键释放态
*/
unsigned char key_driver()
{
    static u8 key_state = 0, key_time = 0;
    u8 key_return = N_key;

    switch (key_state)
    {
      case 0:                                      // 按键初始态
        if (!KEY_AN) key_state = 1;         // 键被按下,状态转换到按键消抖和确认状态
        break;

      case 1:                                                              // 按键消抖与确认态
        if (!KEY_AN)
        {
             key_time = 0;                    
             key_state = 2;                                                           // 按键仍然处于按下,消抖完成,状态转换到按下键时间的计时状态,但返回的还是无键事件
        }
        else
             key_state = 0;                                                           // 按键已抬起,转换到按键初始态。此处完成和实现软件消抖,其实按键的按下和释放都在此消抖的。
        break;

      case 2:                                                                                                                         // 按下键时间的计时状态
        if(KEY_AN)
        {
             key_return = S_key;                        // 此时按键释放,说明是产生一次短操作,回送S_key
             key_state = 0;                                                           // 转换到按键初始态
        }
        else if (++key_time >= 100)                     // 继续按下,计时加10ms(10ms为本函数循环执行间隔)
        {
             key_return = L_key;                        // 按下时间>1000ms,此按键为长按操作,返回长键事件
             key_state = 3;                                                           // 转换到等待按键释放状态
        }
        break;

      case 3:                                                                          // 等待按键释放状态,此状态只返回无按键事件
        if (KEY_AN) key_state = 0;                             // 按键已释放,转换到按键初始态
        break;
    }
    return key_return;
}

/*
        业务逻辑层
        1.单击、双击、长按的分配
*/
unsigned char key_read()
{
    static u8 key_m = 0, key_time_1 = 0;
    u8 key_return = N_key,key_temp;

    key_temp = key_driver();

    switch(key_m)
    {
        case 0:
            if (key_temp == S_key )
            {
                 key_time_1 = 0;               // 第1次单击,不返回,到下个状态判断后面是否出现双击
                 key_m = 1;
            }
            else
                 key_return = key_temp;        // 对于无键、长键,返回原事件
            break;

        case 1:
            if (key_temp == S_key)             // 又一次单击(间隔肯定<500ms)
            {
                 key_return = D_key;           // 返回双击键事件,回初始状态
                 key_m = 0;
            }
            else                                
            {                                  // 这里500ms内肯定读到的都是无键事件,因为长键>1000ms,在1s前低层返回的都是无键
                 if(++key_time_1 >= 30)
                 {
                      key_return = S_key;      // 500ms内没有再次出现单键事件,返回上一次的单键事件
                      key_m = 0;               // 返回初始状态
                 }
             }
             break;
    }
    return key_return;
}

/*
        单击:300ms~1000ms之间
        双击:300ms内
        长按:超过1s

        单击:控制LED1
        双击:控制LED2
        长按:控制蜂鸣器
*/
int main(void)
{
        LED_Init();
        KEY_Iint();
        BEEF_Iint();
        while(1)
        {
                switch(key_read())
                {
                        case N_key:
                                delay_ms(10);
                        break;
                        case S_key:
                                PBout(1)=!PBout(1);
                        break;
                        case D_key:
                                PCout(5)=!PCout(5);
                                break;
                        case L_key:
                                BEEF = !BEEF;
                                break;
                }
        }
        return 0;
}


评分

参与人数 3黑币 +68 收起 理由
AAA_MCU + 12 很给力!
hzd530 + 6
admin + 50 共享资料的黑币奖励!

查看全部评分

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

使用道具 举报

沙发
ID:85865 发表于 2020-6-28 08:17 | 只看该作者
这个可以有
回复

使用道具 举报

板凳
ID:763998 发表于 2020-6-28 09:22 | 只看该作者

哈哈哈,差点失去发帖的动力,幸好有你
回复

使用道具 举报

地板
ID:790159 发表于 2020-6-29 10:18 | 只看该作者
已学习 ,感谢楼主分享
回复

使用道具 举报

5#
ID:82098 发表于 2021-4-25 20:13 | 只看该作者
四个按键如何处理?敬请指教!
回复

使用道具 举报

6#
ID:519089 发表于 2021-7-14 21:06 | 只看该作者
学习了,,,支持原创
回复

使用道具 举报

7#
ID:584195 发表于 2021-11-15 18:22 | 只看该作者
可以带我写指针函数的写法吗?
回复

使用道具 举报

8#
ID:809754 发表于 2022-8-5 16:23 | 只看该作者
很多按键呢  怎么搞
回复

使用道具 举报

9#
ID:165291 发表于 2022-11-14 20:08 | 只看该作者
多按键有必要这么写么?
回复

使用道具 举报

10#
ID:52721 发表于 2023-1-11 11:10 | 只看该作者
对于单键而言,是个很不错的选择。
回复

使用道具 举报

11#
ID:890367 发表于 2023-1-11 18:24 来自手机 | 只看该作者
多按键呢
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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