单片机长短按简单实现
目录
- 单片机长短按简单实现
- 1 原理
- 2 示例代码
- 2.1 按键实现
- 3 测试log
- 4 其他实现方式
1 原理
按键检测和处理的步骤如下:
1:定时扫描按键(使用定时器定时扫描,也可以用软件延时或者系统心跳之类的方式,总之能保证每次扫描间隔时间固定并且在一个较小的范围即可)。
2:扫描到有按键按下(通常是检测GPIO的电平状态来判断按键是否按下,具体情况需要结合实际硬件电路来看)。
3:开始计时,记录按键持续按下的时间。
4:若按下的时间达到了短按的时间(具体多长的时间为短按由自己定义),选择触发按键处理(按下即触发),或者先记录状态,等按键释放时再触发按键处理(弹起时触发)。
5:按键时间超过短按时间,继续计时。
6:按键时间达到长按时间(具体多长的时间为长按由自己定义),选择触发按键处理(按下即触发),或者先记录状态,等按键释放时再触发按键处理(弹起时触发)。
2 示例代码
该示例使用的GD32,共配置了4个按键,特点如下:
1:按键按下时电平为0,释放时为1。
2:短按时间为30ms。
3:长按时间为1s。
4:短按释放时触发按键处理。
5:长按按下时即触发按键处理。
6:按键扫描和按键处理均放在定时器中断服务函数,若按键处理的时间较长,建议分开操作(按键扫描还是放在中断,按键处理放在其他地方,以免长时间占用中断时间)。
7:按键处理我这里都是留空的,只用串口打印了一句话,表明已经触发了按键处理,具体处理什么东西看实际需求。
注:示例代码仅供参考,还需要按具体需求修改。
2.1 按键实现
key.c:
#include "key.h"
#include "main.h"
key_t key1;
key_t key2;
key_t key3;
key_t key4;
void timer_user_init(void);
// 按键初始化
void key_user_init(void)
{
/* enable the key clock */
rcu_periph_clock_enable(KEY1_CLOCK);
rcu_periph_clock_enable(KEY2_CLOCK);
rcu_periph_clock_enable(KEY3_CLOCK);
rcu_periph_clock_enable(KEY4_CLOCK);
/* configure key gpio port */
gpio_init(KEY1_PORT, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, KEY1_PIN);
gpio_init(KEY2_PORT, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, KEY2_PIN);
gpio_init(KEY3_PORT, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, KEY3_PIN);
gpio_init(KEY4_PORT, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, KEY4_PIN);
timer_user_init(); // 启动定时器,定时扫描按键
}
// 按键扫描
int key_scan(void)
{
if(READ_KEY1_STATE == KEY_PRESSED && (key1.status == 0))
{
if(++key1.debounce > KEY_SHORT_PRESSED)
{// 短按
key1.status = 1;
// LOG("key1 short pressed.\n");
}
}
else if(READ_KEY1_STATE == KEY_PRESSED && (key1.status == 1))
{
if(++key1.debounce > KEY_LONG_PRESSED)
{// 长按
key1.status = 2;
key1.debounce = 0;
// LOG("key1 long pressed.\n");
return KEY1_LONG_PRESSED;
}
}
else if((READ_KEY1_STATE == KEY_RELEASED) && (key1.status > 0))
{
if(key1.status == 1)
{// 短按释放
key1.status = 0;
key1.debounce = 0;
return KEY1_SHORT_PRESSED;
}
if(key1.status == 2)
{// 长按释放
key1.status = 0;
key1.debounce = 0;
}
}
if(READ_KEY2_STATE == KEY_PRESSED && (key2.status == 0))
{
if(++key2.debounce > KEY_SHORT_PRESSED)
{// 短按
key2.status = 1;
}
}
else if(READ_KEY2_STATE == KEY_PRESSED && (key2.status == 1))
{
if(++key2.debounce > KEY_LONG_PRESSED)
{// 长按
key2.status = 2;
key2.debounce = 0;
return KEY2_LONG_PRESSED;
}
}
else if((READ_KEY2_STATE == KEY_RELEASED) && (key2.status > 0))
{
if(key2.status == 1)
{// 短按释放
key2.status = 0;
key2.debounce = 0;
return KEY2_SHORT_PRESSED;
}
if(key2.status == 2)
{// 长按释放
key2.status = 0;
key2.debounce = 0;
}
}
if(READ_KEY3_STATE == KEY_PRESSED && (key3.status == 0))
{
if(++key3.debounce > KEY_SHORT_PRESSED)
{// 短按
key3.status = 1;
}
}
else if(READ_KEY3_STATE == KEY_PRESSED && (key3.status == 1))
{
if(++key3.debounce > KEY_LONG_PRESSED)
{// 长按
key3.status = 2;
key3.debounce = 0;
return KEY3_LONG_PRESSED;
}
}
else if((READ_KEY3_STATE == KEY_RELEASED) && (key3.status > 0))
{
if(key3.status == 1)
{// 短按释放
key3.status = 0;
key3.debounce = 0;
return KEY3_SHORT_PRESSED;
}
if(key3.status == 2)
{// 长按释放
key3.status = 0;
key3.debounce = 0;
}
}
if(READ_KEY4_STATE == KEY_PRESSED && (key4.status == 0))
{
if(++key4.debounce > KEY_SHORT_PRESSED)
{// 短按
key4.status = 1;
}
}
else if(READ_KEY4_STATE == KEY_PRESSED && (key4.status == 1))
{
if(++key4.debounce > KEY_LONG_PRESSED)
{// 长按
key4.status = 2;
key4.debounce = 0;
return KEY4_LONG_PRESSED;
}
}
else if((READ_KEY4_STATE == KEY_RELEASED) && (key4.status > 0))
{
if(key4.status == 1)
{// 短按释放
key4.status = 0;
key4.debounce = 0;
return KEY4_SHORT_PRESSED;
}
if(key4.status == 2)
{// 长按释放
key4.status = 0;
key4.debounce = 0;
}
}
return -1;
}
// 按键处理
void key_handle(void)
{
static uint8_t key_state;
uint8_t i;
static uint8_t step = 0;
key_state = key_scan(); // 按键扫描
if(key_state == KEY1_SHORT_PRESSED)
{// 按键1短按
LOG("key1 short pressed.\n");
}
else if(key_state == KEY1_LONG_PRESSED)
{// 按键1长按
LOG("key1 long pressed.\n");
}
else if(key_state == KEY2_SHORT_PRESSED)
{// 按键2短按
LOG("key2 short pressed.\n");
}
else if(key_state == KEY2_LONG_PRESSED)
{// 按键2长按
LOG("key2 long pressed.\n");
}
else if(key_state == KEY3_SHORT_PRESSED)
{// 按键3短按
LOG("key3 short pressed.\n");
}
else if(key_state == KEY3_LONG_PRESSED)
{// 按键3长按
LOG("key3 long pressed.\n");
}
else if(key_state == KEY4_SHORT_PRESSED)
{// 按键4短按
LOG("key4 short pressed.\n");
}
else if(key_state == KEY4_LONG_PRESSED)
{// 按键4长按
LOG("key4 long pressed.\n");
}
}
/********************** 定时器配置,用于定时扫描按键 *************************/
void TIMER2_IRQHandler(void)
{
if(SET == timer_interrupt_flag_get(TIMER2, TIMER_INT_UP))
{
/* clear channel 0 interrupt bit */
timer_interrupt_flag_clear(TIMER2, TIMER_INT_UP);
key_handle(); // 按键扫描并处理
}
}
void nvic_config(void)
{
nvic_irq_enable(TIMER2_IRQn, 0, 0);
}
void timer_config(void)
{
/* ----------------------------------------------------------------------------
TIMER2 Configuration:
TIMER2CLK = SystemCoreClock/18000 = 10KHz, the period is 1s(10/10000 = 1s).
---------------------------------------------------------------------------- */
timer_parameter_struct timer_initpara;
/* enable the peripherals clock */
rcu_periph_clock_enable(RCU_TIMER2);
/* deinit a TIMER */
timer_deinit(TIMER2);
/* initialize TIMER init parameter struct */
timer_struct_para_init(&timer_initpara);
/* TIMER2 configuration */
timer_initpara.prescaler = 18000 - 1; // 180MHz / 18000 = 10kHz
timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
timer_initpara.counterdirection = TIMER_COUNTER_UP;
timer_initpara.period = 10 - 1; // 10 * 0.01ms = 1ms
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
timer_init(TIMER2, &timer_initpara);
/* enable the TIMER interrupt */
timer_interrupt_enable(TIMER2, TIMER_INT_UP);
/* enable a TIMER */
timer_enable(TIMER2);
}
void timer_user_init(void)
{
/* configure the TIMER peripheral */
timer_config();
/* configure the TIMER2 interrupt */
nvic_config();
}
key.h:
#ifndef __KEY_H
#define __KEY_H
#include "gd32e50x.h"
#include "gd32e50x_gpio.h"
#define KEY1_SHORT_PRESSED 0
#define KEY2_SHORT_PRESSED 1
#define KEY3_SHORT_PRESSED 2
#define KEY4_SHORT_PRESSED 3
#define KEY1_LONG_PRESSED 4
#define KEY2_LONG_PRESSED 5
#define KEY3_LONG_PRESSED 6
#define KEY4_LONG_PRESSED 7
#define KEY_PRESSED 0 // 按下时电平为0
#define KEY_RELEASED 1 // 按下时电平为1
#define KEY_SHORT_PRESSED 30 // 1ms x 30 = 30ms
#define KEY_LONG_PRESSED 1000 // 1ms x 1000 = 1s
#define KEY1_CLOCK RCU_GPIOB
#define KEY1_PORT GPIOB
#define KEY1_PIN GPIO_PIN_12
#define KEY2_CLOCK RCU_GPIOB
#define KEY2_PORT GPIOB
#define KEY2_PIN GPIO_PIN_13
#define KEY3_CLOCK RCU_GPIOB
#define KEY3_PORT GPIOB
#define KEY3_PIN GPIO_PIN_14
#define KEY4_CLOCK RCU_GPIOB
#define KEY4_PORT GPIOB
#define KEY4_PIN GPIO_PIN_15
#define READ_KEY1_STATE gpio_input_bit_get(KEY1_PORT, KEY1_PIN)
#define READ_KEY2_STATE gpio_input_bit_get(KEY2_PORT, KEY2_PIN)
#define READ_KEY3_STATE gpio_input_bit_get(KEY3_PORT, KEY3_PIN)
#define READ_KEY4_STATE gpio_input_bit_get(KEY4_PORT, KEY4_PIN)
typedef struct
{
uint16_t debounce;
uint8_t status;
} key_t;
void key_user_init(void);
int key_scan(void);
#endif
main.c:
#include "main.h"
#include "uart.h"
#include "key.h"
int main(void)
{
// systick_config();
uart_user_init();
key_user_init();
printf("app init success.\n");
while(1)
{
}
}
mian.h:
#ifndef MAIN_H
#define MAIN_H
#include "gd32e50x.h"
#include "gd32e50x_rcu.h"
#include "gd32e50x_gpio.h"
#include "systick.h"
#include "uart.h"
#define UART_DEBUG
#ifdef UART_DEBUG
#define DEBUG(format, ...) printf(format, ##__VA_ARGS__)
#define LOG printf
#else
#define DEBUG(format, ...)
#define LOG(format, ...)
#endif
#endif /* MAIN_H */
3 测试log
4 其他实现方式
详见我之前的博客:以STM32为例,实现按键的短按和长按