STM32学习第四天:复杂按键学习

学习前先介绍一下FIFO机制,FIFO机制类似于一个队伍,在队伍里排队,有abc三个人,a先排,后排b,再排c,出来的时候是a先出来,c后出来。即先入先出!在复杂按键的读写里应用的就是这个操作,排队的队尾位置是写的位置,队伍排队的终点就是读的位置。写入多个按键值,会在终点读出来。了解完这个机制后就可以学习复杂按键了。复杂按键相对来说还是比较困难的。

首先是在key.h里写一下关于FIFO机制的结构体方便在.c里应用,

/* 按键FIFO用到变量 */
#define KEY_FIFO_SIZE    10
typedef struct
{
    uint8_t Buf[KEY_FIFO_SIZE];        /* 键值缓冲区 */
    uint8_t Read;                    /* 缓冲区读指针1 */
    uint8_t Write;                    /* 缓冲区写指针 */
    uint8_t Read2;                    /* 缓冲区读指针2 */
}KEY_FIFO_T;

经过刚刚的介绍,这个读写指针想必也很好理解了,然后就是和上篇一样,给这四个按键进行一下宏定义

#define RCC_ALL_KEY          ( WKUP_GPIO_CLK | KEY0_GPIO_CLK | KEY1_GPIO_CLK | KEY2_GPIO_CLK )

//按键-宏定义模板--WKUP--PA0
#define WKUP_GPIO_PIN        GPIO_Pin_0                //WKUP引脚号
#define WKUP_PIN_ID            0                        //WKUP引脚序号
#define WKUP_GPIO_PORT        GPIOA                    //WKUP端口号
#define WKUP_GPIO_CLK        RCC_APB2Periph_GPIOA    //WKUP时钟
//#define WKUP_FUN_OUT        PAout                    //WKUP输出端口配置函数
#define WKUP_FUN_IN            PAin                    //WKUP输入端口配置函数
#define WKUP_GPIO_MODE        GPIO_Mode_IPD            //WKUP端口输入模式--输入下拉
#define WKUP_ACTIVE_LEVEL    1                        //WKUP按键有效电平为高电平

//按键-宏定义模板--KEY0--PE4
#define KEY0_GPIO_PIN        GPIO_Pin_4                //KEY0引脚号
#define KEY0_PIN_ID            4                        //KEY0引脚序号
#define KEY0_GPIO_PORT        GPIOE                    //KEY0端口号
#define KEY0_GPIO_CLK        RCC_APB2Periph_GPIOE    //KEY0时钟
//#define KEY0_FUN_OUT        PEout                    //KEY0输出端口配置函数
#define KEY0_FUN_IN            PEin                    //KEY0输入端口配置函数
#define KEY0_GPIO_MODE        GPIO_Mode_IPU            //KEY0端口输入模式--输入上拉
#define KEY0_ACTIVE_LEVEL    0                        //KEY0按键有效电平为低电平

//按键-宏定义模板--KEY1--PE3
#define KEY1_GPIO_PIN        GPIO_Pin_3                //KEY1引脚号
#define KEY1_PIN_ID            3                        //KEY1引脚序号
#define KEY1_GPIO_PORT        GPIOE                    //KEY1端口号
#define KEY1_GPIO_CLK        RCC_APB2Periph_GPIOE    //KEY1时钟
//#define KEY1_FUN_OUT        PEout                    //KEY1输出端口配置函数
#define KEY1_FUN_IN            PEin                    //KEY1输入端口配置函数
#define KEY1_GPIO_MODE        GPIO_Mode_IPU            //KEY1端口输入模式--输入上拉
#define KEY1_ACTIVE_LEVEL    0                        //KEY1按键有效电平为低电平
    
//按键-宏定义模板--KEY2--PE2
#define KEY2_GPIO_PIN        GPIO_Pin_2                //KEY2引脚号
#define KEY2_PIN_ID            2                        //KEY2引脚序号
#define KEY2_GPIO_PORT        GPIOE                    //KEY2端口号
#define KEY2_GPIO_CLK        RCC_APB2Periph_GPIOE    //KEY2时钟
//#define KEY2_FUN_OUT        PEout                    //KEY2输出端口配置函数
#define KEY2_FUN_IN            PEin                    //KEY2输入端口配置函数
#define KEY2_GPIO_MODE        GPIO_Mode_IPU            //KEY2端口输入模式--输入上拉
#define KEY2_ACTIVE_LEVEL    0                        //KEY2按键有效电平为低电平

//IO操作函数
#define WKUP            WKUP_FUN_IN( WKUP_PIN_ID )  //WKUP
#define KEY0            KEY0_FUN_IN( KEY0_PIN_ID )  //KEY0
#define KEY1            KEY1_FUN_IN( KEY1_PIN_ID )  //KEY1
#define KEY2            KEY2_FUN_IN( KEY2_PIN_ID )  //KEY2

本次复杂按键包括了两个按键一起按下的情况,所以我们引入了键码值,写了六个键码值,并附有相关注释

#define KEY_COUNT    6                           /* 按键个数, 4个独立建 + 2个组合键 */

/* 根据应用程序的功能重命名按键宏 */
#define WKUP_DOWN        KEY_1_DOWN
#define WKUP_UP            KEY_1_UP
#define WKUP_LONG        KEY_1_LONG

#define KEY0_DOWN        KEY_2_DOWN
#define KEY0_UP            KEY_2_UP
#define KEY0_LONG        KEY_2_LONG

#define KEY1_DOWN        KEY_3_DOWN
#define KEY1_UP            KEY_3_UP
#define KEY1_LONG        KEY_3_LONG

#define KEY2_DOWN        KEY_4_DOWN
#define KEY2_UP            KEY_4_UP
#define KEY2_LONG        KEY_4_LONG

#define SYS_DOWN_WKUP_KEY0    KEY_5_DOWN        /* WKUP KEY0 组合键 */
#define SYS_UP_WKUP_KEY0    KEY_5_UP
#define SYS_LONG_WKUP_KEY0    KEY_5_LONG

#define SYS_DOWN_KEY0_KEY1    KEY_6_DOWN        /* KEY0 KEY1 组合键 */
#define SYS_UP_KEY0_KEY1    KEY_6_UP
#define SYS_LONG_KEY0_KEY1    KEY_6_LONG

/* 按键ID, 主要用于bsp_KeyState()函数的入口参数 */
typedef enum
{
    KID_K1 = 0,
    KID_K2,
    KID_K3,
    KID_K4,
}KEY_ID_E;

/*
    按键滤波时间50ms, 单位10ms。
    只有连续检测到50ms状态不变才认为有效,包括弹起和按下两种事件
    即使按键电路不做硬件滤波,该滤波机制也可以保证可靠地检测到按键事件
*/
#define KEY_FILTER_TIME   5
#define KEY_LONG_TIME     100            /* 单位10ms, 持续1秒,认为长按事件 */

/*
    每个按键对应1个全局的结构体变量。
*/
typedef struct
{
    /* 下面是一个函数指针,指向判断按键手否按下的函数 */
    uint8_t (*IsKeyDownFunc)(void); /* 按键按下的判断函数,1表示按下 */

    uint8_t  Count;            /* 滤波器计数器 */
    uint16_t LongCount;        /* 长按计数器 */
    uint16_t LongTime;        /* 按键按下持续时间, 0表示不检测长按 */
    uint8_t  State;            /* 按键当前状态(按下还是弹起) */
    uint8_t  RepeatSpeed;    /* 连续按键周期 */
    uint8_t  RepeatCount;    /* 连续按键计数器 */
}KEY_T;

/*
    定义键值代码, 必须按如下次序定时每个键的按下、弹起和长按事件

    推荐使用enum, 不用#define,原因:
    (1) 便于新增键值,方便调整顺序,使代码看起来舒服点
    (2) 编译器可帮我们避免键值重复。
*/
typedef enum
{
    KEY_NONE = 0,            /* 0 表示按键事件 */

    KEY_1_DOWN,                /* 1键按下 */
    KEY_1_UP,                /* 1键弹起 */
    KEY_1_LONG,                /* 1键长按 */

    KEY_2_DOWN,                /* 2键按下 */
    KEY_2_UP,                /* 2键弹起 */
    KEY_2_LONG,                /* 2键长按 */

    KEY_3_DOWN,                /* 3键按下 */
    KEY_3_UP,                /* 3键弹起 */
    KEY_3_LONG,                /* 3键长按 */

    KEY_4_DOWN,                /* 4键按下 */
    KEY_4_UP,                /* 4键弹起 */
    KEY_4_LONG,                /* 4键长按 */

    /* 组合键 */
    KEY_5_DOWN,                /* 5键按下 */
    KEY_5_UP,                /* 5键弹起 */
    KEY_5_LONG,                /* 5键长按 */

    KEY_6_DOWN,                /* 6键按下 */
    KEY_6_UP,                /* 6键弹起 */
    KEY_6_LONG,                /* 6键长按 */
}KEY_ENUM;

                然后是key.c里的函数

#include "bsp.h"

/*
    该程序适用于WSNEP_V01开发板

    如果用于其它硬件,请修改GPIO定义和 IsKeyDown1 - IsKeyDown6 函数 和 static void bsp_InitKeyHard(void)函数

    如果用户的按键个数小于4个,你可以将多余的按键全部定义为和第1个按键一样,并不影响程序功能
    #define KEY_COUNT    6      这个在 bsp_key.h 文件中有定义
*/

static KEY_T s_tBtn[KEY_COUNT];
static KEY_FIFO_T s_tKey;        /* 按键FIFO变量,结构体 */

static void bsp_InitKeyVar(void);
static void bsp_InitKeyHard(void);
static void bsp_DetectKey(uint8_t i);

/*
*********************************************************************************************************
*    函 数 名: IsKeyDownX
*    功能说明: 判断按键是否按下
*    形    参: 无
*    返 回 值: 返回值1 表示按下,0表示未按下
*********************************************************************************************************
*/
/* 为了区分6个事件: WKUP单独按下, KEY0单独按下,KEY1单独按下,KEY2单独按下, WKUP和KEY0同时按下 ,KEY0和KYE1同时按下*/
static uint8_t IsKeyDown1(void)
{
    if( (WKUP == WKUP_ACTIVE_LEVEL) &&( KEY0 != KEY0_ACTIVE_LEVEL) &&( KEY1 != KEY1_ACTIVE_LEVEL) &&( KEY2 != KEY2_ACTIVE_LEVEL)
        )
    {
        return 1;
    }else
    {
        return 0;
    }
}
static uint8_t IsKeyDown2(void)
{
    if((WKUP != WKUP_ACTIVE_LEVEL) &&( KEY0 == KEY0_ACTIVE_LEVEL) &&( KEY1 != KEY1_ACTIVE_LEVEL) &&( KEY2 != KEY2_ACTIVE_LEVEL)
     )
    {
        return 1;
    }else
    {
        return 0;
    }
}
static uint8_t IsKeyDown3(void)
{
    if((WKUP != WKUP_ACTIVE_LEVEL) &&( KEY0 != KEY0_ACTIVE_LEVEL) &&( KEY1 == KEY1_ACTIVE_LEVEL) &&( KEY2 != KEY2_ACTIVE_LEVEL)
     )
    {
        return 1;
    }else
    {
        return 0;
    }
}
static uint8_t IsKeyDown4(void)
{
    if((WKUP != WKUP_ACTIVE_LEVEL) &&( KEY0 != KEY0_ACTIVE_LEVEL) &&( KEY1 != KEY1_ACTIVE_LEVEL) &&( KEY2 == KEY2_ACTIVE_LEVEL)
     )
    {
        return 1;
    }else
    {
        return 0;
    }
}
static uint8_t IsKeyDown5(void)    /* WKUP KEY0组合键 */
{
    if((WKUP == WKUP_ACTIVE_LEVEL) &&( KEY0 == KEY0_ACTIVE_LEVEL) &&( KEY1 != KEY1_ACTIVE_LEVEL) &&( KEY2 != KEY2_ACTIVE_LEVEL)
     )
    {
        return 1;
    }else
    {
        return 0;
    }
}
static uint8_t IsKeyDown6(void)    /* KEY0 KEY1组合键 */
{
    if((WKUP != WKUP_ACTIVE_LEVEL) &&( KEY0 == KEY0_ACTIVE_LEVEL) &&( KEY1 == KEY1_ACTIVE_LEVEL) &&( KEY2 != KEY2_ACTIVE_LEVEL)
     )
    {
        return 1;
    }else
    {
        return 0;
    }
}

/*
*********************************************************************************************************
*    函 数 名: bsp_InitKey
*    功能说明: 初始化按键. 该函数被 bsp_Init() 调用。
*    形    参:  无
*    返 回 值: 无
*********************************************************************************************************
*/
void bsp_InitKey(void)
{
    bsp_InitKeyVar();        /* 初始化按键变量 */
    bsp_InitKeyHard();        /* 初始化按键硬件 */
}

/*
*********************************************************************************************************
*    函 数 名: bsp_PutKey
*    功能说明: 将1个键值压入按键FIFO缓冲区。可用于模拟一个按键。
*    形    参:  _KeyCode : 按键代码
*    返 回 值: 无
*********************************************************************************************************
*/
void bsp_PutKey(uint8_t _KeyCode)
{
    s_tKey.Buf[s_tKey.Write] = _KeyCode;

    if (++s_tKey.Write  >= KEY_FIFO_SIZE)
    {
        s_tKey.Write = 0;
    }
}

/*
*********************************************************************************************************
*    函 数 名: bsp_GetKey
*    功能说明: 从按键FIFO缓冲区读取一个键值。
*    形    参:  无
*    返 回 值: 按键代码
*********************************************************************************************************
*/
uint8_t bsp_GetKey(void)
{
    uint8_t ret;

    if (s_tKey.Read == s_tKey.Write)
    {
        return KEY_NONE;
    }
    else
    {
        ret = s_tKey.Buf[s_tKey.Read];

        if (++s_tKey.Read >= KEY_FIFO_SIZE)
        {
            s_tKey.Read = 0;
        }
        return ret;
    }
}

/*
*********************************************************************************************************
*    函 数 名: bsp_GetKey2
*    功能说明: 从按键FIFO缓冲区读取一个键值。独立的读指针。
*    形    参:  无
*    返 回 值: 按键代码
*********************************************************************************************************
*/
uint8_t bsp_GetKey2(void)
{
    uint8_t ret;

    if (s_tKey.Read2 == s_tKey.Write)
    {
        return KEY_NONE;
    }
    else
    {
        ret = s_tKey.Buf[s_tKey.Read2];

        if (++s_tKey.Read2 >= KEY_FIFO_SIZE)
        {
            s_tKey.Read2 = 0;
        }
        return ret;
    }
}

/*
*********************************************************************************************************
*    函 数 名: bsp_GetKeyState
*    功能说明: 读取按键的状态
*    形    参:  _ucKeyID : 按键ID,从0开始
*    返 回 值: 1 表示按下, 0 表示未按下
*********************************************************************************************************
*/
uint8_t bsp_GetKeyState(KEY_ID_E _ucKeyID)
{
    return s_tBtn[_ucKeyID].State;
}

/*
*********************************************************************************************************
*    函 数 名: bsp_SetKeyParam
*    功能说明: 设置按键参数
*    形    参:_ucKeyID : 按键ID,从0开始
*            _LongTime : 长按事件时间
*             _RepeatSpeed : 连发速度
*    返 回 值: 无
*********************************************************************************************************
*/
void bsp_SetKeyParam(uint8_t _ucKeyID, uint16_t _LongTime, uint8_t  _RepeatSpeed)
{
    s_tBtn[_ucKeyID].LongTime = _LongTime;            /* 长按时间 0 表示不检测长按键事件 */
    s_tBtn[_ucKeyID].RepeatSpeed = _RepeatSpeed;            /* 按键连发的速度,0表示不支持连发 */
    s_tBtn[_ucKeyID].RepeatCount = 0;                        /* 连发计数器 */
}


/*
*********************************************************************************************************
*    函 数 名: bsp_ClearKey
*    功能说明: 清空按键FIFO缓冲区
*    形    参:无
*    返 回 值: 按键代码
*********************************************************************************************************
*/
void bsp_ClearKey(void)
{
    s_tKey.Read = s_tKey.Write;
}

/*
*********************************************************************************************************
*    函 数 名: bsp_InitKeyHard
*    功能说明: 配置按键对应的GPIO
*    形    参:  无
*    返 回 值: 无
*********************************************************************************************************
*/
static void bsp_InitKeyHard(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    /* 第1步:打开GPIO时钟 */
    RCC_APB2PeriphClockCmd(RCC_ALL_KEY, ENABLE);
    
    GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);    /* 关闭jtag,使能SWD,可以用SWD模式调试 */

    /* 第2步:配置所有的按键GPIO */

    GPIO_InitStructure.GPIO_Pin  = WKUP_GPIO_PIN;    /* WKUP端口 */
    GPIO_InitStructure.GPIO_Mode = WKUP_GPIO_MODE;     /* WKUP端口模式 */      
    GPIO_Init(WKUP_GPIO_PORT, &GPIO_InitStructure);    /* 初始化WKUP */
    
    GPIO_InitStructure.GPIO_Pin  = KEY0_GPIO_PIN;    /* KEY0端口 */
    GPIO_InitStructure.GPIO_Mode = KEY0_GPIO_MODE;     /* KEY0端口模式 */
     GPIO_Init(KEY0_GPIO_PORT, &GPIO_InitStructure);    /* 初始化KEY0 */
    
    GPIO_InitStructure.GPIO_Pin  = KEY1_GPIO_PIN;    /* KEY1端口 */
    GPIO_InitStructure.GPIO_Mode = KEY1_GPIO_MODE;     /* KEY1端口模式 */
     GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStructure);    /* 初始化KEY1 */
    
    GPIO_InitStructure.GPIO_Pin  = KEY2_GPIO_PIN;    /* KEY2端口 */
    GPIO_InitStructure.GPIO_Mode = KEY2_GPIO_MODE;     /* KEY1端口模式 */
     GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStructure);    /* 初始化KEY2 */


}

/*
*********************************************************************************************************
*    函 数 名: bsp_InitKeyVar
*    功能说明: 初始化按键变量
*    形    参:  无
*    返 回 值: 无
*********************************************************************************************************
*/
static void bsp_InitKeyVar(void)
{
    uint8_t i;

    /* 对按键FIFO读写指针清零 */
    s_tKey.Read = 0;
    s_tKey.Write = 0;
    s_tKey.Read2 = 0;

    /* 给每个按键结构体成员变量赋一组缺省值 */
    for (i = 0; i < KEY_COUNT; i++)
    {
        s_tBtn[i].LongTime = KEY_LONG_TIME;            /* 长按时间 0 表示不检测长按键事件 */
        s_tBtn[i].Count = KEY_FILTER_TIME / 2;        /* 计数器设置为滤波时间的一半 */
        s_tBtn[i].State = 0;                            /* 按键缺省状态,0为未按下 */
        //s_tBtn[i].KeyCodeDown = 3 * i + 1;                /* 按键按下的键值代码 */
        //s_tBtn[i].KeyCodeUp   = 3 * i + 2;                /* 按键弹起的键值代码 */
        //s_tBtn[i].KeyCodeLong = 3 * i + 3;                /* 按键被持续按下的键值代码 */
        s_tBtn[i].RepeatSpeed = 0;                        /* 按键连发的速度,0表示不支持连发 */
        s_tBtn[i].RepeatCount = 0;                        /* 连发计数器 */
    }

    /* 如果需要单独更改某个按键的参数,可以在此单独重新赋值 */
    /* 比如,我们希望按键1按下超过1秒后,自动重发相同键值 */
//    s_tBtn[KID_K1].LongTime = 100;
//    s_tBtn[KID_K1].RepeatSpeed = 5;    /* 每隔50ms自动发送键值 */


    /* 判断按键按下的函数 */
    s_tBtn[0].IsKeyDownFunc = IsKeyDown1;
    s_tBtn[1].IsKeyDownFunc = IsKeyDown2;
    s_tBtn[2].IsKeyDownFunc = IsKeyDown3;
    s_tBtn[3].IsKeyDownFunc = IsKeyDown4;

    /* 组合键 */
    s_tBtn[4].IsKeyDownFunc = IsKeyDown5;
    s_tBtn[5].IsKeyDownFunc = IsKeyDown6;
}

/*
*********************************************************************************************************
*    函 数 名: bsp_DetectKey
*    功能说明: 检测一个按键。非阻塞状态,必须被周期性的调用。
*    形    参:  按键结构变量指针
*    返 回 值: 无
*********************************************************************************************************
*/
static void bsp_DetectKey(uint8_t i)
{
    KEY_T *pBtn;

    /*
        如果没有初始化按键函数,则报错
        if (s_tBtn[i].IsKeyDownFunc == 0)
        {
            printf("Fault : DetectButton(), s_tBtn[i].IsKeyDownFunc undefine");
        }
    */

    pBtn = &s_tBtn[i];
    if (pBtn->IsKeyDownFunc())
    {
        if (pBtn->Count < KEY_FILTER_TIME)
        {
            pBtn->Count = KEY_FILTER_TIME;
        }
        else if(pBtn->Count < 2 * KEY_FILTER_TIME)
        {
            pBtn->Count++;
        }
        else
        {
            if (pBtn->State == 0)
            {
                pBtn->State = 1;

                /* 发送按钮按下的消息 */
                bsp_PutKey((uint8_t)(3 * i + 1));
            }

            if (pBtn->LongTime > 0)
            {
                if (pBtn->LongCount < pBtn->LongTime)
                {
                    /* 发送按钮持续按下的消息 */
                    if (++pBtn->LongCount == pBtn->LongTime)
                    {
                        /* 键值放入按键FIFO */
                        bsp_PutKey((uint8_t)(3 * i + 3));
                    }
                }
                else
                {
                    if (pBtn->RepeatSpeed > 0)
                    {
                        if (++pBtn->RepeatCount >= pBtn->RepeatSpeed)
                        {
                            pBtn->RepeatCount = 0;
                            /* 常按键后,每隔10ms发送1个按键 */
                            bsp_PutKey((uint8_t)(3 * i + 1));
                        }
                    }
                }
            }
        }
    }
    else
    {
        if(pBtn->Count > KEY_FILTER_TIME)
        {
            pBtn->Count = KEY_FILTER_TIME;
        }
        else if(pBtn->Count != 0)
        {
            pBtn->Count--;
        }
        else
        {
            if (pBtn->State == 1)
            {
                pBtn->State = 0;

                /* 发送按钮弹起的消息 */
                bsp_PutKey((uint8_t)(3 * i + 2));
            }
        }

        pBtn->LongCount = 0;
        pBtn->RepeatCount = 0;
    }
}

/*
*********************************************************************************************************
*    函 数 名: bsp_KeyScan
*    功能说明: 扫描所有按键。非阻塞,被systick中断周期性的调用
*    形    参:  无
*    返 回 值: 无
*********************************************************************************************************
*/
void bsp_KeyScan(void)
{
    uint8_t i;

    for (i = 0; i < KEY_COUNT; i++)
    {
        bsp_DetectKey(i);
    }
}

最后在.h的末尾加入.c里应用的函数的声明,复杂按键就书写完了,就可以通过主函数应用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值