eric2013 发表于 2019-3-23 02:00:20

V7新版的按键FIFO驱动扩展和移植更简单,组合键也更好用

具体代码和移植方法等下周教程发布后,直接看教程即可。
static/image/hrline/4.gif

改成下面的方式,扩展更加方便:
#define HARD_KEY_NUM            8                                       /* 实体按键个数 */
#define KEY_COUNT                  (HARD_KEY_NUM + 2)      /* 8个独立建 + 2个组合按键 */

/* 使能GPIO时钟 */
#define ALL_KEY_GPIO_CLK_ENABLE() {      \
                __HAL_RCC_GPIOB_CLK_ENABLE();      \
                __HAL_RCC_GPIOC_CLK_ENABLE();      \
                __HAL_RCC_GPIOG_CLK_ENABLE();      \
                __HAL_RCC_GPIOH_CLK_ENABLE();      \
                __HAL_RCC_GPIOI_CLK_ENABLE();      \
      };

/* 依次定义GPIO */
typedef struct
{
      GPIO_TypeDef* gpio;
      uint16_t pin;
      uint8_t ActiveLevel;      /* 激活电平 */
}X_GPIO_T;

/* GPIO和PIN定义 */
static const X_GPIO_T s_gpio_list = {
      {GPIOI, GPIO_PIN_8, 0},         /* K1 */
      {GPIOC, GPIO_PIN_13, 0},      /* K2 */
      {GPIOH, GPIO_PIN_4, 0},          /* K3 */
      {GPIOG, GPIO_PIN_2, 0},         /* JOY_U */      
      {GPIOB, GPIO_PIN_0, 0},         /* JOY_D */
      {GPIOG, GPIO_PIN_3, 0},         /* JOY_L */      
      {GPIOG, GPIO_PIN_7, 0},          /* JOY_R */      
      {GPIOI, GPIO_PIN_11, 0},      /* JOY_OK */
};      

/* 定义一个宏函数简化后续代码
      判断GPIO引脚是否有效按下
*/
static KEY_T s_tBtn = {0};
static KEY_FIFO_T s_tKey;                /* 按键FIFO变量,结构体 */

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

#define KEY_PIN_ACTIVE(id)      

/*
*********************************************************************************************************
*      函 数 名: KeyPinActive
*      功能说明: 判断按键是否按下
*      形    参: 无
*      返 回 值: 返回值1 表示按下(导通),0表示未按下(释放)
*********************************************************************************************************
*/
static uint8_t KeyPinActive(uint8_t _id)
{
      uint8_t level;
      
      if ((s_gpio_list.gpio->IDR & s_gpio_list.pin) == 0)
      {
                level = 0;
      }
      else
      {
                level = 1;
      }

      if (level == s_gpio_list.ActiveLevel)
      {
                return 1;
      }
      else
      {
                return 0;
      }
}

/*
*********************************************************************************************************
*      函 数 名: IsKeyDownFunc
*      功能说明: 判断按键是否按下。单键和组合键区分。单键事件不允许有其他键按下。
*      形    参: 无
*      返 回 值: 返回值1 表示按下(导通),0表示未按下(释放)
*********************************************************************************************************
*/
static uint8_t IsKeyDownFunc(uint8_t _id)
{
      /* 实体单键 */
      if (_id < HARD_KEY_NUM)
      {
                uint8_t i;
                uint8_t count = 0;
                uint8_t save = 255;
               
                /* 判断有几个键按下 */
                for (i = 0; i < HARD_KEY_NUM; i++)
                {
                        if (KeyPinActive(i))
                        {
                              count++;
                              save = i;
                        }
                }
               
                if (count == 1 && save == _id)
                {
                        return 1;      /* 只有1个键按下时才有效 */
                }               

                return 0;
      }
      
      /* 组合键 K1K2 */
      if (_id == HARD_KEY_NUM + 0)
      {
                if (KeyPinActive(KID_K1) && KeyPinActive(KID_K2))
                {
                        return 1;
                }
                else
                {
                        return 0;
                }
      }

      /* 组合键 K2K3 */
      if (_id == HARD_KEY_NUM + 1)
      {
                if (KeyPinActive(KID_K2) && KeyPinActive(KID_K3))
                {
                        return 1;
                }
                else
                {
                        return 0;
                }
      }

      return 0;
}

多次组合键不会出错了:

byccc 发表于 2019-3-23 12:01:07

我的板子到了,回头来个V5,V6,V7全家福:lol

diiiiiii 发表于 2019-3-23 14:20:37

扫描按键?为什么不用中断触发呢?

eric2013 发表于 2019-3-23 14:54:26

diiiiiii 发表于 2019-3-23 14:20
扫描按键?为什么不用中断触发呢?
从裸机的角度分析
    中断方式:中断方式可以快速地检测到按键按下的,并执行相应的按键程序,但实际情况是由于按键的机械抖动特性,在程序进入中断后必须进行滤波处理才能判定是有效的按键事件。如果每个按键都是独立的接一个IO引脚,需要我们给每个IO都设置一个中断,程序中过多的中断会影响系统的稳定性,中断方式跨平台移植困难。
    查询方式:查询方式有一个最大的缺点就是需要程序定期的去执行查询,耗费一定的系统资源。实际上耗费不了多大的系统资源,因为这种查询方式也只是查询按键是否按下,按键事件的执行还是在主程序里面实现。

从OS的角度分析
    中断方式:在OS中要尽可能少用中断方式,因为在RTOS中过多的使用中断会影响系统的稳定性和可预见性(抢占式调度的OS基本没有可预见性)。只有比较重要的事件处理需要用中断的方式。
    查询方式:对于用户按键推荐使用这种查询方式来实现,现在的OS基本都带有CPU利用率的功能,这个按键FIFO占用的还是很小的,基本都在%1以下

diiiiiii 发表于 2019-3-23 17:46:15

eric2013 发表于 2019-3-23 14:54
从裸机的角度分析
    中断方式:中断方式可以快速地检测到按键按下的,并执行相应的按键程序,但实际情 ...

谢谢回复。我之前认为在大多数时间里按键都是空闲的,即没有按下。再占用定时器去不断扫描会浪费资源。

hpdell 发表于 2019-3-24 19:55:54

这个按键扫描方法,支持 单个按键 双击,3击 扫描检测不?

如果不支持,该如何修改啊 ??

zl1215 发表于 2019-3-24 21:08:49

工程上基本都是定时扫描,放后台或者某个优先级低的定时里面做就行。

eric2013 发表于 2019-3-25 02:41:46

hpdell 发表于 2019-3-24 19:55
这个按键扫描方法,支持 单个按键 双击,3击 扫描检测不?

如果不支持,该如何修改啊 ??

这种要求的话,程序无需修改,仅需在按键按下后记录当前时刻,下次再按下时求个差值就行。

hpdell 发表于 2019-3-25 09:30:22

eric2013 发表于 2019-3-25 02:41
这种要求的话,程序无需修改,仅需在按键按下后记录当前时刻,下次再按下时求个差值就行。

好的,我试试看

晚起的鸟 发表于 2020-9-25 09:40:55

我把按键个数改成2, GPIO和PIN定义那里改成我的按键,然后打印键值。两个按键键值相同,也不知道咋回事?

eric2013 发表于 2020-9-25 10:45:48

晚起的鸟 发表于 2020-9-25 09:40
我把按键个数改成2, GPIO和PIN定义那里改成我的按键,然后打印键值。两个按键键值相同,也不知道咋回事?

上修改后的代码看看。

gallop020142 发表于 2020-9-28 09:51:41

晚起的鸟 发表于 2020-9-25 09:40
我把按键个数改成2, GPIO和PIN定义那里改成我的按键,然后打印键值。两个按键键值相同,也不知道咋回事?

这个问题就是下面这段代码的问题:
除了组合键之外,其他按键同时只能检测一个按键
static uint8_t IsKeyDownFunc(uint8_t _id)
{
      /* 实体单键 */
      if (_id < HARD_KEY_NUM)
      {
                uint8_t i;
                uint8_t count = 0;
                uint8_t save = 255;
               
                /* 判断有几个键按下 */
                for (i = 0; i < HARD_KEY_NUM; i++)
                {
                        if (KeyPinActive(i))
                        {
                              count++;
                              save = i;
                        }
                }
               
                if (count == 1 && save == _id)
                {
                        return 1;      /* 只有1个键按下时才有效 */
                }               

                return 0;
      }

eric2013 发表于 2020-9-29 05:23:57

gallop020142 发表于 2020-9-28 09:51
这个问题就是下面这段代码的问题:
除了组合键之外,其他按键同时只能检测一个按键
static uint8_t IsK ...

他说的是检查到的两个按键的键值相同,跟这个没关系。

核弹 发表于 2022-2-24 09:53:25

有BUG,长按一个按键的时候,去单击另一个按键会识别成长按的按键单击

eric2013 发表于 2022-2-24 10:32:37

核弹 发表于 2022-2-24 09:53
有BUG,长按一个按键的时候,去单击另一个按键会识别成长按的按键单击
自己的程序里面加个简单的变量处理即可,检测到长按后设置变量,其它按键检查到次变量设置读取出来的消息不错处理,
本质原因是这个按键FIFO没有做长按+另一个键的消息处理。

mojinpan 发表于 2022-2-24 19:26:51

本帖最后由 mojinpan 于 2022-2-25 11:20 编辑

我提供一个更精简的按键驱动,支持各种单击,双击,长按,shift按键等多种功能,而且支持到最多64个按键全并发处理
https://github.com/mojinpan/keykey.h
/** ****************************************************************************
* @file         key.h
* @author         mojinpan
* @copyright (c) 2018 - 2020 mojinpan
* @brief         按键驱动
*
* @par 功能说明
* @details
* 1.支持最大64个按键(数量可配置)
* 2.支持按键消抖处理(可滤掉小于2个扫描周期的毛刺)
* 3.支持按键环形缓冲区(长度可配置)
* 4.支持4种不同按键模式:A:短按键 B:shift键 C:长按单发 D:长按连发
* 5.短按键    :短按直接输出键值
* 6.shift键:点击后除了输出一次短按键外,还将后续所有其他按键的键值改为,shift键+按键值(包括长短按键)
* 7.长按连发:长按一定时间后连续输出按键键值
* 8.长按单发:长按一定时间后反码输出键值(仅一次)
* 9.4种不同按键模式可相互叠加使用,所有按键功能都能并发输出
*
* @par 移植说明
* @details
* 1.根据具体情况编写KeyIOread()和KeyInit().
* 2.每20ms~50ms调用KeyScan()扫描按键,调用KeyGetBufLen()判断是否有按键,调用KeyGet()获得按键值.
*******************************************************************************/
#ifndef __KEY__
#define __KEY__

/*******************************************************************************
                              Header Files      头文件
*******************************************************************************/
#include "bsp.h"
#include "stdbool.h"
#include "stdint.h"
/*******************************************************************************
                               Key Scancode      按键扫描码
*******************************************************************************/
///* @brief 按键键值,每个按键占1bit,不得重复
typedef enum
{
    KEY_UP           = 0x0001,
    KEY_DN           = 0x0002,   
    KEY_LT           = 0x0004,
    KEY_RT           = 0x0008,
    KEY_ENT          = 0x0010,
    KEY_ESC          = 0x0020,
    KEY_INC          = 0x0040,
    KEY_DEC          = 0x0080,
    KEY_FUN          = 0x0100
       
    KEY_UP_L        = 0xFFFE,       
    KEY_DN_L        = 0xFFFD,
       
    KEY_UP_SF    = 0x0101,
    KEY_DN_SF    = 0x0102,
    KEY_LT_SF    = 0x0104,
    KEY_RT_SF    = 0x0108,
    KEY_ENT_SF   = 0x0110,
    KEY_ESC_SF   = 0x0120,
    KEY_INC_SF   = 0x0140,
    KEY_DEC_SF   = 0x0180       
} KEY_value;
/*******************************************************************************
                              Drive Config      驱动配置
*******************************************************************************/
#define KEY_MAX_NUM       11                     //定义最大按键数量(1~64)
#define KEY_BUF_SIZE      8                      //定义按键缓冲区大小(2,4,8,16,32,64,128,256)
#define KEY_LONG_EN       1                      //定义是否支持长按键(1 or 0)
#define KEY_SHORT_SHIFT   KEY_FUN          //定义shift键,短按有效(0 or 按键扫描码)

#if KEY_LONG_EN > 0
#define KEY_PRESS_DLY   40                   //定义长按键延时n个扫描周期开始判定有效值(1~256)
#define KEY_PRESS_TMR   5                      //定义长按键n个扫描周期为1次有效扫描值(1~256)
#define KEY_LONG_SHIFT    KEY_UP|KEY_DN//长按键反码输出(仅一次),未定义的长按连续输出(0 or 按键扫描码)
#endif
/*******************************************************************************
                                                                Function declaration 函数声明
*******************************************************************************/
void KeyInit(void);               //按键初始化函数
void KeyScan(void);               //按键扫描函数,20ms~50ms定时调用
uint8_t KeyGetBufLen(void);       //按键判断函数,判断是否有按键按下
uint32_t KeyGet(void);            //按键获得函数,获取具体按键值,无按键时返回0
void KeyFlush(void);                          //清空按键buf
#endif

key.c
/** ****************************************************************************
* @file                 key.c
* @author         mojinpan
* @copyright (c) 2018 - 2020 mojinpan
* @brief                 按键驱动
*
* @version         V0.1
* @date                 2011-3-2
* @details
* 1.支持矩阵键盘和IO按键两种
* 2.支持长按键和短按键两种触发方式。
*
* @version         V0.2
* @date                 2011-3-5
* @details
* 1.最大按键数量16个,可继续扩充至64个.
* 2.支持长按键和短按键并行触发.
* 3.增加短按键部分改为按键释放时返回.
*
* @version         V0.3
* @date                 2013-11-4
* @details
* 1.重写按键获取的逻辑,长按键算法不成熟,暂时去掉
* 2.矩阵按键没用到,暂时去掉
* 3.增加按键消抖算法
*
* @version         V0.4
* @date                 2013-11-6
* @details
* 1.增加环形buf
* 2.重写接口函数,便于在OS环境中调用,当然裸奔也支持.
* c_cpp_properties.json
* @version         V0.5
* @date                 2013-11-6
* @details
* 1.增加长按键的支持
* 2.增加shift键的支持
* 3.增加按键数量的定义,定义后根据按键数量选择合适的数据类型,从而节省ram和rom
* 4.处于兼容性的考虑,将最大按键支持数量由64个缩减至32个
*
* @version         V0.6
* @date                 2013-11-24
* @details
* 1.修正长按键输出后,还输出短按键的bug
* 2.修改的KeyScan()的逻辑,将长按键细分为长按键连续输出和长按键反码输出(仅一次)两种方式
* 3.shift键按下时也提供按键值输出,方便应用程序判断shift键的状态
* 4.由于第3点的修改,短按键和shift键不在冲突,即长按键/短按键/shift键相互独立
* 5.将无按键返回0xff,改成返回0
* 6.增加一个KeyFlush()函数,用于清空buf
*
* @version         V0.7
* @date                 2019-04-26
* @details
* 1.参照liunx上kfifo的代码优化环形buf的算法.
* 2.将KeyHit()函数重命名为KeyBufLen()函数,将返回有无按键改为返回按键缓冲数量.
* 3.KEY_TYPE 改为使用stdint.h中的数据类型定义,并重新启用64个按键的支持
*******************************************************************************/

#include "bsp.h"
#include "key.h"

/*******************************************************************************
                           Macro Definition    宏定义
*******************************************************************************/
#if   KEY_MAX_NUM > 32
#define KEY_TYPE    uint64_t
#elif   KEY_MAX_NUM > 16
#define KEY_TYPE    uint32_t
#elif KEY_MAX_NUM > 8
#define KEY_TYPE    uint16_t
#else
#define KEY_TYPE    uint8_t
#endif
/*******************************************************************************
                            Global Variables    全局变量
*******************************************************************************/
staticKEY_TYPE PreScanKey= 0;               //前次按键扫描值      
staticKEY_TYPE PreReadKey= 0;               //前次按键读取值      
staticKEY_TYPE KeyShift    = 0;               //shift按键记录
staticKEY_TYPE KeyMask   = 0;               //按键掩码
#if LONG_KEY_EN > 0
staticuint8_tKeyPressTmr = 0;                           //长按键判断周期
#endif

static KEY_TYPE KeyBuf;         //环形buf
static uint8_tKeyBufInIdx= 0;               //buf入指针                  
static uint8_tKeyBufOutIdx = 0;               //buf出指针
/** ****************************************************************************
@brief按键扫描函数,需根据实际硬件情况进行移植
@note   所有按键均必须高电平有效
*******************************************************************************/
__weak static KEY_TYPEKeyIOread( void )
{
    KEY_TYPE KeyScanCode=0;
   
        KeyScanCode |= GPIO_IN(K_UP ) ? 0 : KEY_UP ;
        KeyScanCode |= GPIO_IN(K_DN ) ? 0 : KEY_DN ;
        KeyScanCode |= GPIO_IN(K_LT ) ? 0 : KEY_LT ;
        KeyScanCode |= GPIO_IN(K_RT ) ? 0 : KEY_RT ;
        KeyScanCode |= GPIO_IN(K_ENT) ? 0 : KEY_ENT;
        KeyScanCode |= GPIO_IN(K_ESC) ? 0 : KEY_ESC;
        KeyScanCode |= GPIO_IN(K_INC) ? 0 : KEY_INC;
        KeyScanCode |= GPIO_IN(K_DEC) ? 0 : KEY_DEC;
        KeyScanCode |= GPIO_IN(K_FUN) ? 0 : KEY_FUN;
       
return KeyScanCode;
}

/** ****************************************************************************
@brief从buf中获得一个按键值
@return 返回按键值,无按键返回0xFF
*******************************************************************************/
static KEY_TYPE KeyBufOut (void)
{
    if(KeyBufOutIdx == KeyBufInIdx) return 0;            //buf空
    return KeyBuf;   //返回按键值
}
/** ****************************************************************************
@brief将按键值放入buf中
@paramcode: 需放入buf的按键值
*******************************************************************************/
static voidKeyBufIn (KEY_TYPE code)
{                        
    if(KeyBufInIdx - KeyBufOutIdx == KEY_BUF_SIZE)
    {
      KeyBufOutIdx++;//buf满则放弃最早的一个按键值
    }

    KeyBuf = code ;
}

/** ****************************************************************************
@brief按键扫描
@details
1.功能说明
a.按键消抖
b.捕捉长按键和短按键
c.根据设置将按键分成短按键/长按连续建/短按shift键/长按shift键4种方式写入buf
2.算法说明
a.按键获取算法
    1).NowKey & PreKey                              : 电平触发
    2).NowKey ^ PreKey                              : 边缘触发
    3).NowKey & (NowKey ^ PreKey)或(~PreKey) & NowKey : 上升沿触发
    4).PreKey & (NowKey ^ PreKey)或PreKey & (~NowKey) : 下降沿触发
b.滤波算法
    1).PreScanKey & NowScanKey                        : 电平触发
    2).PreReadKey & (PreScanKey ^ NowScanKey)         : 采样保持
    3).NowReadKey = 1) | 2)                           : 带采样保持的电平触发
3.调用说明
a.对下调用的KeyIOread()中,有效按键必须为高电平,且每个bit表示一个按键值
b.应用调用该函数的间隔应该在20ms~50ms,在调用间隔内的毛刺均可滤除。
*******************************************************************************/
void KeyScan(void)
{
KEY_TYPE NowScanKey   = 0;                              //当前按键值扫描值
KEY_TYPE NowReadKey   = 0;                              //当前按键值
//KEY_TYPE KeyPressDown = 0;                              //按键按下                                    
KEY_TYPE KeyRelease   = 0;                              //按键释放                                                         
NowScanKey= KeyIOread();
NowReadKey= (PreScanKey&NowScanKey)|                  //电平触发
               PreReadKey&(PreScanKey^NowScanKey);      //采样保持(即消抖)

//KeyPressDown= NowReadKey & (NowReadKey ^ PreReadKey);   //上升沿触发
KeyRelease    = PreReadKey & (NowReadKey ^ PreReadKey);   //下降沿触发   

#if LONG_KEY_EN > 0                                       
if(NowReadKey == PreReadKey && NowReadKey)                                 //用电平触发做长按键的有效判据
{            
    KeyPressTmr++;
    if(KeyPressTmr >= KEY_PRESS_TMR)                                                //长按判断周期到,保存相应长按键值
        {                     
      if(NowReadKey & ~(KEY_LONG_SHIFT))                                        //长按键模式一
          {                  
      KeyBufIn(NowReadKey | KeyShift);                  //长按键重复输出
      }
      else if(NowReadKey & (KEY_LONG_SHIFT) & ~KeyMask )        //长按键模式二
          {         
      KeyBufIn(~(NowReadKey | KeyShift));               //长按键反码输出作为第二功能键      
      }
      KeyPressTmr = 0;                                                //重置连按周期,准备获取下1个长按键
      KeyMask = NowReadKey;
    }
}
else{
    KeyPressTmr = KEY_PRESS_DLY;                            //按键变化,重置按键判断周期
}
#endif

if(KeyRelease)
{                                                                           //短按键判断
      if(KeyRelease &(~KeyMask)&& !NowReadKey)
          {
      KeyShift ^= (KeyRelease & (KEY_SHORT_SHIFT));       //shift按键码(边缘触发)
      KeyBufIn(KeyRelease | KeyShift | PreReadKey);
      }
          else
          {
      KeyMask = 0;
      }
}

PreScanKey = NowScanKey;
PreReadKey = NowReadKey;
}

/** ****************************************************************************
@brief按键初始化
*******************************************************************************/
__weak void KeyInit( void )
{
        //按键初始化,输入,浮空,低速
        GpioInit2Input(K_UP,GPIO_NOPULL,GPIO_SPEED_FREQ_LOW);       
        GpioInit2Input(K_DN,GPIO_NOPULL,GPIO_SPEED_FREQ_LOW);               
        GpioInit2Input(K_LT,GPIO_NOPULL,GPIO_SPEED_FREQ_LOW);       
        GpioInit2Input(K_RT,GPIO_NOPULL,GPIO_SPEED_FREQ_LOW);       
        GpioInit2Input(K_ENT ,GPIO_NOPULL,GPIO_SPEED_FREQ_LOW);       
        GpioInit2Input(K_ESC ,GPIO_NOPULL,GPIO_SPEED_FREQ_LOW);       
        GpioInit2Input(K_INC ,GPIO_NOPULL,GPIO_SPEED_FREQ_LOW);       
        GpioInit2Input(K_DEC ,GPIO_NOPULL,GPIO_SPEED_FREQ_LOW);       
        GpioInit2Input(K_FUN ,GPIO_NOPULL,GPIO_SPEED_FREQ_LOW);          
}

/** ****************************************************************************
@brief从buf中获得一个按键值
@return 回按键值,无按键返回0xFF
*******************************************************************************/
uint32_tKeyGet(void)
{                        
return (uint32_t)KeyBufOut();
}   

/** ****************************************************************************
@brief判断是否有按键按下
@return buf中按键的数量
*******************************************************************************/
uint8_tKeyGetBufLen (void)
{
   return KeyBufInIdx - KeyBufOutIdx;
}

/** ****************************************************************************
@brief清空buff中所有按键值
*******************************************************************************/
void KeyFlush(void)
{
   KeyBufOutIdx = KeyBufInIdx;
}


wgco98 发表于 2024-2-21 18:59:18

eric2013 发表于 2022-2-24 10:32
自己的程序里面加个简单的变量处理即可,检测到长按后设置变量,其它按键检查到次变量设置读取出来的消息 ...

这个功能是否可以规范到现在的按键处理函数中。因为用的按键都是不能自恢复的,会使用同时几个按键按下的情况处理

eric2013 发表于 2024-2-22 09:01:28

wgco98 发表于 2024-2-21 18:59
这个功能是否可以规范到现在的按键处理函数中。因为用的按键都是不能自恢复的,会使用同时几个按键按下的 ...

好的,如果后面还有机会的话,升级个新版本。

桃子不逃 发表于 2024-3-21 20:58:32

请问如果按键按下时,是低电平,即按键按下时,io口接地,这个程序要怎么改?
/* GPIO和PIN定义 */
static const X_GPIO_T s_gpio_list = {
        {GPIOA, GPIO_PIN_0, 1},                /* K1 */
        {GPIOC, GPIO_PIN_13, 1},        /* K2 */       
        {GPIOG, GPIO_PIN_13, 0},                /* K3 */
        {GPIOG, GPIO_PIN_14, 0},                /* K4 */       
};       

是只需要将这里的激活状态设置为0嘛?

eric2013 发表于 2024-3-22 08:55:03

桃子不逃 发表于 2024-3-21 20:58
请问如果按键按下时,是低电平,即按键按下时,io口接地,这个程序要怎么改?
/* GPIO和PIN定义 */
stati ...

对,就是这个。

hahaha 发表于 2024-4-14 23:20:02

FREETROS系统下的按键FIFO八个串口FIFO例程有吗?
页: [1]
查看完整版本: V7新版的按键FIFO驱动扩展和移植更简单,组合键也更好用