1.之前做按键,在中断判断并进入回调函数,但是经常会导致其他任务来不及处理,或者是按键触发了但没有执行回调,即用户操作时感觉按键失灵。
2.这里更新了一下代码,思路是这样的:中断进入按键扫描,有消抖,不阻塞,如果按键事件触发时即入列,然后操作系统每隔10ms进行一次轮询,若队列不为空,则出列并执行按键回调。
有纰漏请指出,转载请说明。
学习交流请发邮件 1280253714@qq.com
-
- #include "includes.h"
-
- KEY_S key1;
- KEY_S key2;
-
-
- //按键平时为高电平,按下为低电平
- static void Key_GPIO_Config(void)
- {
- GPIO_InitTypeDef GPIO_InitStruct;
-
- RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK, ENABLE);
- RCC_APB2PeriphClockCmd(KEY2_GPIO_CLK, ENABLE);
-
- GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
-
- GPIO_InitStruct.GPIO_Pin = KEY1_GPIO_PIN;
- GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStruct);
-
- GPIO_InitStruct.GPIO_Pin = KEY2_GPIO_PIN;
- GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStruct);
- }
-
- void KeyOsInit(void)
- {
- Key_GPIO_Config();
-
- key1.keyNum = keyNum1;
- key1.state = keyNone;
- key1.pfnKeyCallBack = key1TaskProc;
-
- key2.keyNum = keyNum2;
- key2.state = keyNone;
- key2.pfnKeyCallBack = key2TaskProc;
- }
-
-
- static bool readKeyGpioLevel(u8 keyNum)
- {
- bool keyRes = KEY_OFF;
- switch ( keyNum ){
- case keyNum1:
- keyRes = GPIO_ReadInputDataBit(KEY1_GPIO_PORT, KEY1_GPIO_PIN);
- break;
- case keyNum2:
- keyRes = GPIO_ReadInputDataBit(KEY2_GPIO_PORT, KEY2_GPIO_PIN);
- break;
- default:
- break;
- }
- return keyRes;
- }
-
- void keyScanLoop(KEY_S *keyx)
- {
- if ( keyx->state == keyLong ){
- if (KEY_ON == readKeyGpioLevel(keyx->keyNum)){
- stKeyData.keyNum = keyx->keyNum;
- stKeyData.keyState = keyLong;
- KeyEnQueue(&stKeyQueue,&stKeyData);
- // keyx->pfnKeyCallBack(); //如果是执行这里的话,那么就是在中断进入回调了
- return;
- } else {
- keyx->preState = keyx->state;
- keyx->state = keyNone;
- }
- }
-
- // 不在检测状态时按键按下
- if ( 0 == keyx->bIsChecking ){
- if (KEY_ON == readKeyGpioLevel(keyx->keyNum)){
- keyx->readKeyLevel = KEY_ON;
- keyx->bIsChecking = 1;
- keyx->keyDownTimes = 0;
- keyx->preState = keyx->state;
- keyx->state = keyNone;
- keyx->timedReadRes = 0;
- keyx->checkInterval = checkIntervalMs;
- keyx->shakeInterval = shakeIntervalMs;
- }
- } else {
- // 按键周期倒计时
- if ( keyx->checkInterval ){
- keyx->checkInterval--;
-
- // 定时读取
- if ( keyx->timedReadInterval ){
- keyx->timedReadInterval--;
- } else {
- keyx->timedReadInterval = timedReadIntervalMs;
- if( KEY_ON == readKeyGpioLevel(keyx->keyNum) ){
- keyx->timedReadRes++;
- }
- }
-
- // 检测状态时按键按下
- if ( KEY_ON == keyx->readKeyLevel ) {
- // 按键软件消抖
- if ( keyx->shakeInterval ){
- keyx->shakeInterval--;
- } else {
- keyx->shakeInterval = shakeIntervalMs;
- if (KEY_ON == readKeyGpioLevel(keyx->keyNum)){
- keyx->keyDownTimes++;
- // 读取的电平反转
- keyx->readKeyLevel = KEY_OFF;
- }
- }
- } else {
- if (KEY_OFF == readKeyGpioLevel(keyx->keyNum)){
- keyx->readKeyLevel = KEY_ON;
- }
- }
- }
- // 按键倒计时结束,通过按下次数和定时读取次数判断按键键值
- else {
- keyx->bIsChecking = 0;
- switch (keyx->keyDownTimes){
- case keyNone:
- keyx->state = keyNone;
- return;
- case keyShort:
- keyx->state = keyShort;
- break;
- case keyDouble:
- case keyTriple: //按下三次也算双击
- keyx->state = keyDouble;
- break;
- default :
- keyx->state = keyLong;
- break;
- }
- if ( keyx->timedReadRes > keyLongTimes ){ //可自定义读取次数
- keyx->state = keyLong;
- }
- stKeyData.keyNum = keyx->keyNum;
- stKeyData.keyState = keyx->state;
- KeyEnQueue(&stKeyQueue,&stKeyData);
- // keyx->pfnKeyCallBack(); //如果是执行这里的话,那么就是在中断进入回调了
- }
- }
- }
-
- void KeyTaskProc(void)
- {
- if(stKeyQueue.state != KeyQueueEmpty)
- {
- if(KeyDeQueue(&stKeyQueue) != KeyDataEmpty)
- {
- stKeyData = stKeyQueue.dataDeQueue;
- switch(stKeyData.keyNum)
- {
- case keyNum1:
- key1TaskProc();
- break;
- case keyNum2:
- key2TaskProc();
- break;
- }
- }
- }
- }
-
- //定时器设置为1ms进一次中断
- void TIM3_IRQHandler()
- {
- if ( TIM_GetITStatus( TIM3, TIM_IT_Update) != RESET )
- {
- keyScanLoop(&key1);
- keyScanLoop(&key2);
- TIM_ClearITPendingBit(TIM3 , TIM_FLAG_Update);
- }
- }
-
- u32 key1Cnt=0;
- u32 key2Cnt=0;
-
- void key1TaskProc(void)
- {
- if(key1.state == keyShort){
- key1Cnt++;
-
- }
- if(key1.state == keyLong){
- if( key1.keyLongExeInterval ){
- key1.keyLongExeInterval--;
- } else {
- key1.keyLongExeInterval = keyLongExeIntervalMs;
- key1Cnt++;
-
- }
- }
- }
-
- void key2TaskProc(void)
- {
- if(key2.state == keyShort){
- key2Cnt++;
- }
- if(key2.state == keyLong){
- if( key2.keyLongExeInterval ){
- key2.keyLongExeInterval--;
- } else {
- key2.keyLongExeInterval = keyLongExeIntervalMs;
- key2Cnt++;
- }
- }
- }
-
- /**************************************** 按键队列处理 ****************************************/
-
- KeyQueue_S stKeyQueue;
- KeyQueueData_S stKeyData;
- /********************************************
- * @函数名 QueueInit
- * @描述 队列初始化
- * @参数 需要初始化的队列
- * @返回值 无
- * @注意 无
- ********************************************/
- void KeyQueueInit(KeyQueue_S *queue)
- {
- memset(&queue, 0, sizeof(queue));
- }
-
- /********************************************
- * @函数名 QueueStateDetermine
- * @描述 检查队列当前状态
- * @参数 需要检查的队列
- * @返回值 队列当前状态
- * @注意 无
- ********************************************/
- KeyQueueState KeyQueueStateDetermine(KeyQueue_S *queue)
- {
- if(queue->size == 0)
- {
- return KeyQueueEmpty;
- }
- else if(queue->size == KeyQueueMaxSize)
- {
- return KeyQueueFull;
- }
- else
- {
- return KeyQueueNormal;
- }
- }
-
- /********************************************
- * @函数名 EnQueue
- * @描述 入列
- * @参数
- queue 有入列需要的队列
- data 需要入列的数据
- * @返回值 无
- * @注意 当队列满时无法入列
- ********************************************/
- void KeyEnQueue(KeyQueue_S *queue, KeyQueueData_S *data)
- {
- if(queue->size == KeyQueueMaxSize)
- {
- return;
- }
- queue->dataEnQueue = *data;
- queue->data[queue->rear] = queue->dataEnQueue;
- queue->size++;
-
- queue->rear++;
- if(queue->rear == KeyQueueMaxSize)
- {
- queue->rear = 0;
- }
- queue->state = KeyQueueStateDetermine(queue);
- }
-
- /********************************************
- * @函数名 DeQueue
- * @描述 出列
- * @参数 queue 有出列需要的队列
- * @返回值 返回的数据是否为空
- * @注意 实际出列的数据放在 queue->dataDeQueue
- ********************************************/
- KeyDeQueueState KeyDeQueue(KeyQueue_S *queue)
- {
- if(queue->size == 0)
- {
- return KeyDataEmpty;
- }
- queue->dataDeQueue = queue->data[queue->front];
- memset(&queue->data[queue->front], 0, sizeof(queue->data[queue->front]));
- queue->size--;
-
- queue->front++;
- if(queue->front == KeyQueueMaxSize)
- {
- queue->front = 0;
- }
- queue->state = KeyQueueStateDetermine(queue);
- return KeyDataNormal;
- }
-
- #ifndef __KEY_H
- #define __KEY_H
-
- #include "includes.h"
-
- typedef enum {
- keyNum1,
- keyNum2,
- keySum,
- } keyOrder;
-
- typedef enum {
- keyNone = 0,
- keyShort,
- keyDouble,
- keyTriple,
- keyLong
- } keyState;
-
- typedef struct key_s {
- keyOrder keyNum;
- bool bIsChecking; //正在检测
- bool bTaskProcessing;
- bool readKeyLevel;
- keyState preState; //上一个按键状态
- keyState state;
- u8 timedReadInterval; //定时读取的间隔
- u8 keyDownTimes; //一个检测周期按下的次数
- u16 timedReadRes; //定时读取,如果在一个检测周期按下的次数为1或2,而每隔n ms读取的低电平次数大于某个值,可认为是长按
- u16 shakeInterval; //抖动多少时间后进行检测
- u16 checkInterval; //整个检测的持续时间
- u16 keyLongInterval; //长按后隔一段时间再检测,避免检测到短按
- u16 keyLongExeInterval;
- void (*pfnKeyCallBack)(void);
- } KEY_S;
-
- extern KEY_S key1;
- extern KEY_S key2;
-
- #define keyLongTimes 40
- #define timedReadIntervalMs 10
- #define shakeIntervalMs 10
- #define checkIntervalMs 500
- #define keyLongExeIntervalMs 10 //OS 10ms进一次,10次触发一次长按,故为100ms
-
- #define KEY_ON 1 //按键平时为低电平,按下为高电平
- #define KEY_OFF 0
-
- #define KEY1_GPIO_PIN GPIO_Pin_0
- #define KEY1_GPIO_PORT GPIOA
- #define KEY1_GPIO_CLK RCC_APB2Periph_GPIOA
-
- #define KEY2_GPIO_PIN GPIO_Pin_13
- #define KEY2_GPIO_PORT GPIOC
- #define KEY2_GPIO_CLK RCC_APB2Periph_GPIOC
-
-
- void KeyOsInit(void);
- void keyScanLoop(KEY_S *keyx);
- void KeyTaskProc(void);
- void key1TaskProc(void);
- void key2TaskProc(void);
-
- /**************************************** 按键队列处理 ****************************************/
-
- #define KeyQueueMaxSize 5 //队列最大存放的数据个数
-
- typedef enum {
- KeyQueueEmpty, //队列为空
- KeyQueueNormal, //队列不为空
- KeyQueueFull, //队列已满
- } KeyQueueState; //队列当前状态
-
- typedef enum {
- KeyDataEmpty, //数据为空
- KeyDataNormal, //数据不为空
- } KeyDeQueueState; //出列的数据情况
-
- typedef struct {
- keyOrder keyNum;
- keyState keyState;
- } KeyQueueData_S; //队列存放的数据类型
-
- // 定义队列结构
- typedef struct {
- KeyQueueData_S data[KeyQueueMaxSize]; //队列缓存
- KeyQueueData_S dataEnQueue; //入列数据
- KeyQueueData_S dataDeQueue; //出列数据
- KeyQueueState state; //队列当前状态
- u8 front; //队头
- u8 rear; //队尾
- u8 size; //队列大小
- } KeyQueue_S;
-
- void KeyQueueInit(KeyQueue_S *queue);
- KeyQueueState KeyQueueStateDetermine(KeyQueue_S *queue);
- void KeyEnQueue(KeyQueue_S *queue, KeyQueueData_S *data);
- KeyDeQueueState KeyDeQueue(KeyQueue_S *queue);
-
- extern KeyQueue_S stKeyQueue;
- extern KeyQueueData_S stKeyData;
-
- #endif
- #include "includes.h"
-
-
- int main(void)
- {
- TimerOsInit();
- LED_Init();
- UartInit();
- AdcOsInit();
- OS_TaskInit();
- SysTickInit();
- KeyOsInit();
-
- OS_TaskCreat(Task1, UaetSendHeartBeat, 100);
- OS_TaskCreat(Task2, KeyTaskProc, 10);
-
- while (1){
- AdcTask();
- UartLoopTask();
- testPro();
- ledLoopTask();
- OS_TaskRun();
- }
- }
STM32单片机操作系统、按键与FIFO_哔哩哔哩_bilibili
STM32单片机操作系统、按键与FIFO