江协科技:全按键功能非阻塞式实现内容回顾
首先就是Key_Flag的运用,我们定义它为uint8_t类型,在使用时我们将其转化成二进制,对它二进制数的每一位0-1进行定义,我们图上只使用了7位,第7位作为预留位,这里第7位的控留,留着后面增加新的标志位,若要加入的标志位多于8个,可以改成uint16_t类型,这个都因人而异。将上述的内容用代码实现,编程思路可以采用状态机的思路,定义变量S用于记录当前程序所处的状态,对每一个状态转向下一个状
前日,江协科技发了第二期的编程技巧,我也是近日学习完之后,突发奇想记录一下。
内容主要是在第一期:定时器实现非阻塞式程序,的基础上,实现按键的单击、双击、长按操作。这里来主要讲解一下代码怎么写。
先来看一下我写的代码:
这是main的代码
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "KEY.H"
#include "TIMER.H"
uint16_t Num1;
uint16_t Num2;
int main (void)
{
OLED_Init();
Key_Init();
TIMER_Init();
while(1)
{
//示例1:
// if(Key_Check(KEY_HOLD))
// {
// Num1 = 1;
// }
// else
// {
// Num1 = 0;
// }
//示例2:
// if(Key_Check(KEY_DOWN))
// {
// Num1++;
// }
// if(Key_Check(KEY_UP))
// {
// Num2++;
// }
//示例3:
// if(Key_Check(KEY_SINGLE))
// {
// Num1++;
// }
// if(Key_Check(KEY_DOUBLE))
// {
// Num1+=100;
// }
// if(Key_Check(KEY_LONG))
// {
// Num1 = 0;
// }
//示例4:
// if(Key_Check(KEY_1,KEY_SINGLE)||Key_Check(KEY_1,KEY_REPEAT)) //单击或长按重复,长按快速自增
// {
// Num1++;
// }
// if(Key_Check(KEY_2,KEY_SINGLE)||Key_Check(KEY_2,KEY_REPEAT)) //单击或长按重复,长按快速自减
// {
// Num1--;
// }
// if(Key_Check(KEY_3,KEY_SINGLE)) //单击+1,长按快速自增
// {
// Num1 = 0;
// }
// if(Key_Check(KEY_4,KEY_LONG)) //长按,长按快速自增
// {
// Num1 = 9999;
// }
OLED_ShowNum(1,6,Num1,5);
OLED_ShowNum(2,6,Num2,5);
}
}
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
{
Key_Tick(KEY_COUNT);
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}
这是Key.c的代码:
#include "stm32f10x.h" // Device header
#define KEY_1 0
#define KEY_2 1
#define KEY_3 2
#define KEY_4 3
#define KEY_PRESSED 1
#define KEY_UNPRESSED 0
#define KEY_HOLD 0X01
#define KEY_DOWN 0X02
#define KEY_UP 0X04
#define KEY_SINGLE 0X08
#define KEY_DOUBLE 0X10
#define KEY_LONG 0X20
#define KEY_REPEAT 0X40
#define KEY_TIME_DOUBLE 200 //双击时间阈值:第一次按下松开后,隔多长时间,再次按下算双击
#define KEY_TIME_LONG 2000 //长按时间阈值:按下不放,过多长的时间,算是长按
#define KEY_TIME_REPEAT 100 //重复时间阈值:长按之后,每隔多久REPEAT置1一次
#define KEY_COUNT 4
uint8_t Key_Flag[KEY_COUNT]; //用二进制表示第0位为HOLD,DOWN,UP,SINGLE,DOUBLE,LONG,REPEAT
void Key_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
//下接按键
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//上接按键
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
//获取按键状态
uint8_t Key_GetState(uint8_t n)
{
if(n == KEY_1)
{
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0) //上接按键,按下为低电平
{
return KEY_PRESSED; //表示按键按下
}
}
else if(n == KEY_2)
{
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11)==0)
{
return KEY_PRESSED;
}
}
else if(n == KEY_3)
{
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13)==1) //下接按键,按下为高电平
{
return KEY_PRESSED;
}
}
else if(n == KEY_4)
{
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_15)==1)
{
return KEY_PRESSED;
}
}
return KEY_UNPRESSED; //表示按键未按下
}
uint8_t Key_Check(uint8_t n ,uint8_t Flag)
{
if(Key_Flag[n] & Flag)
{
if(Flag != KEY_HOLD)
{
Key_Flag[n] &=~Flag;
}
return 1;
}
return 0;
}
void Key_Tick(uint8_t n)
{
static uint8_t Count,i;
static uint8_t CurrState[KEY_COUNT]; //本次状态
static uint8_t PrevState[KEY_COUNT]; //上一次状态
static uint8_t S[KEY_COUNT];
static uint16_t Time[KEY_COUNT]; //用于递减计时
for(i=0;i<KEY_COUNT;++i)
{
if(Time[i] >0)
{
Time[i]--;
}
}
Count++;
if(Count>=20)
{
Count = 0;
for(i=0;i<KEY_COUNT;++i)
{
PrevState[i] = CurrState[i];
CurrState[i] = Key_GetState(i);
//低级事件的检测
if(CurrState[i] == KEY_PRESSED)
{
Key_Flag[i] |= KEY_HOLD; //HOLD == 1
}
else
{
Key_Flag[i] &= ~KEY_HOLD; //HOLD == 0
}
if(CurrState[i] == KEY_PRESSED && PrevState[i] == KEY_UNPRESSED)
{
Key_Flag[i] |= KEY_DOWN; //DOWN == 1
}
if(CurrState[i] == KEY_UNPRESSED && PrevState[i] == KEY_PRESSED)
{
Key_Flag[i] |= KEY_UP; //UP == 1
}
//高级事件的检测(状态机)
if(S[i] == 0) //检测按键按下
{
if(CurrState[i] == KEY_PRESSED)
{
Time[i] = KEY_TIME_LONG;
S[i] = 1;
}
}
else if(S[i] == 1) //检测按键松开,等待长按时间
{
if(CurrState[i] == KEY_UNPRESSED)
{
Time[i] = KEY_TIME_DOUBLE; //设定双击时间
S[i] = 2;
}
else if(Time[i] == 0) //长按时间到
{
Time[i] = KEY_TIME_REPEAT; //设定重复时间
Key_Flag[i] |= KEY_LONG; //LONG = 1;
S[i] = 4;
}
}
else if(S[i] == 2) //检测按键按下,等待双击时间
{
if(Time[i] == 0) //双击时间到
{
Key_Flag[i] |= KEY_SINGLE; //SINGLE = 1
S[i] = 0;
}
else if(CurrState[i] == KEY_PRESSED) //按键按下
{
Key_Flag[i] |= KEY_DOUBLE; //DOUBLE = 1
S[i] = 3;
}
}
else if(S[i] == 3) //检测按键松开
{
if(CurrState[i] == KEY_UNPRESSED)
{
S[i] = 0;
}
}
else if(S[i] == 4) //检测按键松开,等待重复时间
{
if(CurrState[i] == KEY_UNPRESSED)
{
S[i]= 0;
}
else if(Time[i]== 0)
{
Time[i] = KEY_TIME_REPEAT;
Key_Flag[i] |= KEY_REPEAT; //REPEAT = 1
S[i] = 4;
}
}
}
}
}
这是Key.h的代码:
#ifndef __KEY_H
#define __KEY_H
#define KEY_1 0
#define KEY_2 1
#define KEY_3 2
#define KEY_4 3
#define KEY_PRESSED 1
#define KEY_UNPRESSED 0
#define KEY_HOLD 0X01
#define KEY_DOWN 0X02
#define KEY_UP 0X04
#define KEY_SINGLE 0X08
#define KEY_DOUBLE 0X10
#define KEY_LONG 0X20
#define KEY_REPEAT 0X40
#define KEY_TIME_DOUBLE 200
#define KEY_TIME_LONG 2000
#define KEY_TIME_REPEAT 100
#define KEY_COUNT 4
void Key_Init(void);
uint8_t Key_GetState(uint8_t n);
void Key_Tick(uint8_t n);
uint8_t Key_Check(uint8_t n ,uint8_t Flag);
#endif
我这里两个文件都写了宏定义,江协的视频是直接在Key.c文件里#include"KEY.H",我不是很喜欢这样写,一般用也是只打开Key.h文件,所以我就都写了一遍,这个无伤大雅,看个人喜好。
下面来看一下代码的核心部分:
首先就是Key_Flag的运用,我们定义它为uint8_t类型,在使用时我们将其转化成二进制,对它二进制数的每一位0-1进行定义,我们图上只使用了7位,第7位作为预留位,这里第7位的控留,留着后面增加新的标志位,若要加入的标志位多于8个,可以改成uint16_t类型,这个都因人而异。

下面是对单击、双击、长按/重复的波形图。依据波形图的样式来写代码对单击、双击、长按/重复进行判断。

将上述的内容用代码实现,编程思路可以采用状态机的思路,定义变量S用于记录当前程序所处的状态,对每一个状态转向下一个状态进行判断,在每一个状态下,执行相应的操作。

最后,要注意的就是上接按键和下接按键了。

更多推荐



所有评论(0)