HAL STM32+EC11编码器实现增减调节及单击、双击、长按功能
- 📺实现效果演示:
📘内容提要
📝本文主要实现,通过STM32 HAL库开发,实现的EC11编码器功能,按键结合状态机思想实现的拓展单击、双击、长按的综合功能。单片机硬件上使用了2个外部中断引脚实现。
- 🗝该功能可以很方便实现移植,例如使用在OLED屏幕显示菜单上。
- 🔖验证对象:STM32F401
- 🍁EC11编码器部分的原理图:
🛠stm32cubemx配置
- 🔧将EC11中键引脚配置为输入模式、开启内部上拉模式,其余2个引脚配置为外部中断引脚(一个配置为下降沿中断,另外一个配置为上、下降沿中断,这一点很关键!)。
✅EC11编码器增减功能,通过外部中断实现
- 🌿外部中断回调处理函数:
c
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(GPIO_Pin);
static uint8_t count = 0;
static uint8_t b_flag;
GPIO_PinState a_value = HAL_GPIO_ReadPin(EC11_A_GPIO_Port, EC11_A_Pin);
GPIO_PinState b_value = HAL_GPIO_ReadPin(EC11_B_GPIO_Port, EC11_B_Pin);
if(GPIO_Pin == EC11_A_Pin) {
if(a_value == RESET && count == 0) {
b_flag = 0;
if(b_value) b_flag = 1;
count = 1;
}
if(a_value == SET && count == 1) {
if(b_value == RESET && b_flag == 1) { //开始逆时针转动
test_num--;
dir_flag = 1;
}
if(b_value && b_flag == 0) { //开始顺时针转动
test_num++;
dir_flag = 0;
}
count = 0;
}
}
/* EC11中键,按键中断 */
// if(GPIO_Pin == EC11_KEY_Pin)
// {
// key_click_flag = 1;
// }
}
📓单击、双击、长按功能实现驱动代码
- 📝KEY.C
c
#include "key.h"
//代码来源网络 侵权联系删除
/*
--------------------------------------------------------------------
| short click |
| ______ ________ |
| | \ / | |
| | \_______________/ | |
| | | | | |
| |shake| < long |shake| |
| |
-------------------------------------------------------------------
| double click |
| ______ _____________ ____ |
| | \ / | | \ / | |
| | \_______________/ | | \_______________/ | |
| | | | | < max | | | | |
| |shake| < long |shake|dclick |shake| any time |shake| |
| |
--------------------------------------------------------------------
| long click |
| ______ ________ |
| | \ / | |
| | \_______________________________________/ | |
| | | | | |
| |shake| > long click |shake| |
| |
--------------------------------------------------------------------
*/
#define KEY_STATUS_DOWN_CHECK 0x00
#define KEY_STATUS_DOWN_SHAKE 0x01
#define KEY_STATUS_DOWN_HANDLE 0x02
#define KEY_STATUS_LONG_CHECK 0x03
#define KEY_STATUS_SHORT_UP_SHAKE 0x04
#define KEY_STATUS_DOUBLE_CHECK 0x05
#define KEY_STATUS_SHORT_UP_HANDLE 0x06
#define KEY_STATUS_DOUBLE_DOWN_SHAKE 0x07
#define KEY_STATUS_DOUBLE_UP_CHECK 0x08
#define KEY_STATUS_DOUBLE_UP_SHAKE 0x09
#define KEY_STATUS_DOUBLE_UP_HANDLE 0x0a
#define KEY_STATUS_LONG_HANDLE 0x0b
#define KEY_STATUS_CONTINUE_CHECK 0x0c
#define KEY_STATUS_LONG_UP_SHAKE 0x0d
#define KEY_STATUS_LONG_UP_HANDLE 0x0e
#define KEY_READ_DOWN 0x00 /* key is pressed */
#define KEY_READ_UP 0x01 /* Key isn't pressed */
#define KEY_BUF_SIZE 0x10 /* key value buffer size */
struct
{
unsigned short value[KEY_BUF_SIZE];
unsigned char rd;
unsigned char wr;
}key_buf;
struct key_dev
{
unsigned char status; /* state machine status */
unsigned char num; /* number */
unsigned short count_ms; /* ms counter */
unsigned short long_click_ms; /* long click check min time */
unsigned short shake_filter_ms; /* shake filter time */
unsigned short max_dclick_ms; /* double click max interval time */
unsigned short continue_send_ms; /* after long, continue send interval */
unsigned char (*read_key)(void); /* key read pin status function pointer*/
};
/******************************************************************************
User Interface [START]
*******************************************************************************/
unsigned char key0_read(void)
{
if (HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_RESET)
{
return KEY_READ_DOWN;
}
else
{
return KEY_READ_UP;
}
}
struct key_dev key_dev[] = {
{
KEY_STATUS_DOWN_CHECK,
KEY0_NUM,
0,
1500,
20,
300,
1000,
(unsigned char (*)(void))key0_read,
},
/*
.
.
user add other key
.
.
*/
};
/******************************************************************************
User Interface [END]
*******************************************************************************/
static void key_write_value(unsigned short key_val);
static void key_status_down_check(struct key_dev *key_dev);
static void key_status_down_shake(struct key_dev *key_dev);
static void key_status_down_handle(struct key_dev *key_dev);
static void key_status_long_check(struct key_dev *key_dev);
static void key_status_short_up_shake(struct key_dev *key_dev);
static void key_status_double_check(struct key_dev *key_dev);
static void key_status_short_up_handle(struct key_dev *key_dev);
static void key_status_double_down_shake(struct key_dev *key_dev);
static void key_status_double_up_check(struct key_dev *key_dev);
static void key_status_double_up_shake(struct key_dev *key_dev);
static void key_status_double_up_handle(struct key_dev *key_dev);
static void key_status_long_hanle(struct key_dev *key_dev);
static void key_status_continue_check(struct key_dev *key_dev);
static void key_status_long_up_shake(struct key_dev *key_dev);
static void key_status_long_up_handle(struct key_dev *key_dev);
/**
description : write key vaule to buffer
param : key_val - key value , (KEY_EVENT | KEY_NUMBER<<8)
retval : None
author : huohongpeng
data : 2017-03-02
*/
static void key_write_value(unsigned short key_val)
{
key_buf.value[key_buf.wr++] = key_val;
key_buf.wr %= KEY_BUF_SIZE;
/*
overflow handle
*/
if(key_buf.wr == key_buf.rd)
{
key_buf.rd++;
key_buf.rd %= KEY_BUF_SIZE;
}
}
/**
description : read key vaule from buffer
param : None
retval : key_val - key value , (KEY_EVENT | KEY_NUMBER<<8)
author : huohongpeng
data : 2017-03-02
*/
unsigned short key_read_value(void)
{
unsigned short key_val;
if(key_buf.wr == key_buf.rd)
{
key_val = KEY_EVENT_NULL;
}
else
{
key_val = key_buf.value[key_buf.rd++];
key_buf.rd %= KEY_BUF_SIZE;
}
return key_val;
}
/**
description : check key whether press down
param : key_dev - key device pointer
retval : None
author : huohongpeng
data : 2017-03-02
*/
static void key_status_down_check(struct key_dev *key_dev)
{
unsigned char key_read;
key_read = key_dev->read_key();
if(key_read == KEY_READ_DOWN)
{
key_dev->status = KEY_STATUS_DOWN_SHAKE;
key_dev->count_ms = 0;
}
}
/**
description : filter shake after key pressed down
param : key_dev - key device pointer
retval : None
author : huohongpeng
data : 2017-03-02
*/
static void key_status_down_shake(struct key_dev *key_dev)
{
unsigned char key_read;
key_dev->count_ms++;
if(key_dev->count_ms < key_dev->shake_filter_ms)
{
return;
}
key_read = key_dev->read_key();
if(key_read == KEY_READ_DOWN)
{
key_dev->status = KEY_STATUS_DOWN_HANDLE;
}
else
{
key_dev->status = KEY_STATUS_DOWN_CHECK;
}
}
/**
description : key press down handle after pressed down filter shake
param : key_dev - key device pointer
retval : None
author : huohongpeng
data : 2017-03-02
*/
static void key_status_down_handle(struct key_dev *key_dev)
{
unsigned short key_val = key_dev->num<<8 | KEY_EVENT_DOWN;
key_write_value(key_val);
key_dev->status = KEY_STATUS_LONG_CHECK;
key_dev->count_ms = 0;
}
/**
description : check key whether long click
param : key_dev - key device pointer
retval : None
author : huohongpeng
data : 2017-03-02
*/
static void key_status_long_check(struct key_dev *key_dev)
{
unsigned char key_read;
key_dev->count_ms++;
key_read = key_dev->read_key();
if(key_dev->count_ms < key_dev->long_click_ms)
{
if(key_read == KEY_READ_UP)
{
key_dev->status = KEY_STATUS_SHORT_UP_SHAKE;
}
return;
}
key_dev->status = KEY_STATUS_LONG_HANDLE;
}
/**
description : short cilck key up filter shake
param : key_dev - key device pointer
retval : None
author : huohongpeng
data : 2017-03-02
*/
static void key_status_short_up_shake(struct key_dev *key_dev)
{
unsigned char key_read;
static unsigned short old = 0xffff;
if(old == 0xffff)
{
old = key_dev->count_ms;
key_dev->count_ms = 0;
}
key_dev->count_ms++;
if(key_dev->count_ms < key_dev->shake_filter_ms)
{
return;
}
key_read = key_dev->read_key();
if(key_read == KEY_READ_UP)
{
key_dev->status = KEY_STATUS_DOUBLE_CHECK;
key_dev->count_ms = 0;
}
else
{
key_dev->status = KEY_STATUS_LONG_CHECK;
key_dev->count_ms += old;
}
old = 0xffff;
}
/**
description : double cilck check. we consider double click event if key pressed
down when after short click up and within max double click interval
param : key_dev - key device pointer
retval : None
author : huohongpeng
data : 2017-03-02
*/
static void key_status_double_check(struct key_dev *key_dev)
{
unsigned char key_read;
key_dev->count_ms++;
key_read = key_dev->read_key();
if(key_dev->count_ms < key_dev->max_dclick_ms)
{
if(key_read == KEY_READ_DOWN)
{
key_dev->status = KEY_STATUS_DOUBLE_DOWN_SHAKE;
key_dev->count_ms = 0;
}
}
else
{
key_dev->status = KEY_STATUS_SHORT_UP_HANDLE;
}
}
/**
description : short click key up handle
param : key_dev - key device pointer
retval : None
author : huohongpeng
data : 2017-03-02
*/
static void key_status_short_up_handle(struct key_dev *key_dev)
{
unsigned short key_val;
key_val= key_dev->num<<8 | KEY_EVENT_SHORT;
key_write_value(key_val);
key_val= key_dev->num<<8 | KEY_EVENT_UP_SHORT;
key_write_value(key_val);
key_dev->status = KEY_STATUS_DOWN_CHECK;
}
/**
description : double click key down filter shake
param : key_dev - key device pointer
retval : None
author : huohongpeng
data : 2017-03-02
*/
static void key_status_double_down_shake(struct key_dev *key_dev)
{
unsigned char key_read;
static unsigned short old = 0xffff;
if(old == 0xffff)
{
old = key_dev->count_ms;
key_dev->count_ms = 0;
}
key_dev->count_ms++;
if(key_dev->count_ms < key_dev->shake_filter_ms)
{
return;
}
key_read = key_dev->read_key();
if(key_read == KEY_READ_DOWN)
{
unsigned short key_val;
key_val= key_dev->num<<8 | KEY_EVENT_DOUBLE;
key_write_value(key_val);
key_dev->status = KEY_STATUS_DOUBLE_UP_CHECK;
key_dev->count_ms = 0;
}
else
{
key_dev->status = KEY_STATUS_DOUBLE_CHECK;
key_dev->count_ms += old;
}
old = 0xffff;
}
/**
description : double click key up check
param : key_dev - key device pointer
retval : None
author : huohongpeng
data : 2017-03-02
*/
static void key_status_double_up_check(struct key_dev *key_dev)
{
unsigned char key_read;
key_read = key_dev->read_key();
if(key_read == KEY_READ_UP)
{
key_dev->status = KEY_STATUS_DOUBLE_UP_SHAKE;
key_dev->count_ms = 0;
}
}
/**
description : double click key up filter shake
param : key_dev - key device pointer
retval : None
author : huohongpeng
data : 2017-03-02
*/
static void key_status_double_up_shake(struct key_dev *key_dev)
{
unsigned char key_read;
key_dev->count_ms++;
if(key_dev->count_ms < key_dev->shake_filter_ms)
{
return;
}
key_read = key_dev->read_key();
if(key_read == KEY_READ_UP)
{
key_dev->status = KEY_STATUS_DOUBLE_UP_HANDLE;
}
else
{
key_dev->status = KEY_STATUS_DOUBLE_UP_CHECK;
}
}
/**
description : double click key up handle
param : key_dev - key device pointer
retval : None
author : huohongpeng
data : 2017-03-02
*/
static void key_status_double_up_handle(struct key_dev *key_dev)
{
unsigned short key_val;
key_val= key_dev->num<<8 | KEY_EVENT_UP_DOUBLE;
key_write_value(key_val);
key_dev->status = KEY_STATUS_DOWN_CHECK;
}
/**
description : long click handle after long click check
param : key_dev - key device pointer
retval : None
author : huohongpeng
data : 2017-03-02
*/
static void key_status_long_hanle(struct key_dev *key_dev)
{
unsigned short key_val;
key_val= key_dev->num<<8 | KEY_EVENT_LONG;
key_write_value(key_val);
key_dev->status = KEY_STATUS_CONTINUE_CHECK;
key_dev->count_ms = 0;
}
/**
description : continue send short click if long click time overflow
param : key_dev - key device pointer
retval : None
author : huohongpeng
data : 2017-03-02
*/
static void key_status_continue_check(struct key_dev *key_dev)
{
unsigned char key_read;
unsigned short key_val;
key_dev->count_ms++;
key_read = key_dev->read_key();
if(key_read == KEY_READ_UP)
{
key_dev->status = KEY_STATUS_LONG_UP_SHAKE;
}
if(key_dev->count_ms < key_dev->continue_send_ms)
{
return;
}
if(key_dev->continue_send_ms == 0)
{
return;
}
key_val= key_dev->num<<8 | KEY_EVENT_SHORT;
key_write_value(key_val);
key_dev->count_ms = 0;
}
/**
description : long click key up filter shake
param : key_dev - key device pointer
retval : None
author : huohongpeng
data : 2017-03-02
*/
static void key_status_long_up_shake(struct key_dev *key_dev)
{
unsigned char key_read;
static unsigned short old = 0xffff;
if(old == 0xffff)
{
old = key_dev->count_ms;
key_dev->count_ms = 0;
}
key_dev->count_ms++;
if(key_dev->count_ms < key_dev->shake_filter_ms)
{
return;
}
key_read = key_dev->read_key();
if(key_read == KEY_READ_UP)
{
key_dev->status = KEY_STATUS_LONG_UP_HANDLE;
}
else
{
key_dev->status = KEY_STATUS_CONTINUE_CHECK;
key_dev->count_ms += old;
}
old = 0xffff;
}
/**
description : long click key up filter handle
param : key_dev - key device pointer
retval : None
author : huohongpeng
data : 2017-03-02
*/
static void key_status_long_up_handle(struct key_dev *key_dev)
{
unsigned short key_val;
key_val= key_dev->num<<8 | KEY_EVENT_UP_LONG;
key_write_value(key_val);
key_dev->status = KEY_STATUS_DOWN_CHECK;
}
/**
description : run key state machine once every 1ms
param : key_dev - key device pointer
retval : None
author : huohongpeng
data : 2017-03-02
*/
static void key_check_1ms(struct key_dev *key_dev)
{
switch(key_dev->status)
{
case KEY_STATUS_DOWN_CHECK :
key_status_down_check(key_dev);
break;
case KEY_STATUS_DOWN_SHAKE :
key_status_down_shake(key_dev);
break;
case KEY_STATUS_DOWN_HANDLE :
key_status_down_handle(key_dev);
break;
case KEY_STATUS_LONG_CHECK :
key_status_long_check(key_dev);
break;
case KEY_STATUS_SHORT_UP_SHAKE :
key_status_short_up_shake(key_dev);
break;
case KEY_STATUS_DOUBLE_CHECK :
key_status_double_check(key_dev);
break;
case KEY_STATUS_SHORT_UP_HANDLE :
key_status_short_up_handle(key_dev);
break;
case KEY_STATUS_DOUBLE_DOWN_SHAKE :
key_status_double_down_shake(key_dev);
break;
case KEY_STATUS_DOUBLE_UP_CHECK :
key_status_double_up_check(key_dev);
break;
case KEY_STATUS_DOUBLE_UP_SHAKE :
key_status_double_up_shake(key_dev);
break;
case KEY_STATUS_DOUBLE_UP_HANDLE :
key_status_double_up_handle(key_dev);
break;
case KEY_STATUS_LONG_HANDLE :
key_status_long_hanle(key_dev);
break;
case KEY_STATUS_CONTINUE_CHECK :
key_status_continue_check(key_dev);
break;
case KEY_STATUS_LONG_UP_SHAKE :
key_status_long_up_shake(key_dev);
break;
case KEY_STATUS_LONG_UP_HANDLE :
key_status_long_up_handle(key_dev);
break;
default:
key_dev->status = key_dev->status;
}
}
/**
description : run all key state machine once every 1ms
param : key_dev - key device pointer
retval : None
author : huohongpeng
data : 2017-03-02
call :
timer(1ms) interrupt handle
*/
void key_check_all_loop_1ms(void)
{
unsigned char key_num, i;
key_num = sizeof(key_dev)/sizeof(struct key_dev);
for(i = 0; i < key_num; i++)
{
key_check_1ms(&key_dev[i]);
}
}
- 📄KEY.h
c
#ifndef __key_H
#define __key_H
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
#define shortmaxclickms (300)
#define shortminclickms (10)
#define longclickms (800)
#define KEY_EVENT_NULL 0x0000
#define KEY_EVENT_DOWN 0x0001
#define KEY_EVENT_UP_SHORT 0x0002 // 短按后松开事件
#define KEY_EVENT_UP_LONG 0x0003 // 长按后松开事件
#define KEY_EVENT_UP_DOUBLE 0x0004 // 双击后松开事件
#define KEY_EVENT_SHORT 0x0005
#define KEY_EVENT_LONG 0x0006
#define KEY_EVENT_DOUBLE 0x0007
#define KEY_READ_DOWN 0x00 /* key is pressed */
#define KEY_READ_UP 0x01 /* Key isn't pressed */
/******************************************************************************
User Interface [START]
*******************************************************************************/
#define KEY0_NUM 0x0001
#define KEY0_DOWN (KEY_EVENT_DOWN | KEY0_NUM<<8)
#define KEY0_UP_SHORT (KEY_EVENT_UP_SHORT | KEY0_NUM<<8)
#define KEY0_UP_LONG (KEY_EVENT_UP_LONG | KEY0_NUM<<8)
#define KEY0_UP_DOUBLE (KEY_EVENT_UP_DOUBLE | KEY0_NUM<<8)
#define KEY0_SHORT (KEY_EVENT_SHORT | KEY0_NUM<<8)
#define KEY0_LONG (KEY_EVENT_LONG | KEY0_NUM<<8)
#define KEY0_DOUBLE (KEY_EVENT_DOUBLE | KEY0_NUM<<8)
/******************************************************************************
User Interface [END]
*******************************************************************************/
void key_check_all_loop_1ms(void);
unsigned short key_read_value(void);
#ifdef __cplusplus
}
#endif
#endif
🔱main函数中按键处理内容
c
#include "stdio.h"
#include "key.h"
int32_t test_num = 0;//支持正负数值增减
uint8_t dir_flag = 2; /* 方向标志 0: 顺时 1: 逆时 2: 未动*/
uint8_t key_click_flag = 0;//EC11 中键
unsigned short key_value;
int main(void)
{
/* USER CODE BEGIN 1 */
uint32_t tick2, tick3;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
uint32_t Main_Fosc = HAL_RCC_GetSysClockFreq();
printf("Main_Fosc:%dHz \r\n", Main_Fosc);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while(1) {
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(dir_flag != 2) {
switch(dir_flag) {
case 0:
printf("顺时针转动\r\n");
break;
case 1:
printf("逆时针转动\r\n");
break;
}
dir_flag = 2;
printf("num: %d\n", test_num);
}
if(HAL_GetTick() - tick2 >= 1) {
tick2 = HAL_GetTick();
key_check_all_loop_1ms();
}
/* Key按键按下查询 */
if(HAL_GetTick() - tick3 >= 10) {
tick3 = HAL_GetTick();
key_value = key_read_value();
if(key_value == KEY0_UP_SHORT) { //单击事件
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
// 实现单击KEY
test_num++;
printf("Press 单击,num:%d\r\n", test_num);
} else if(key_value == KEY0_UP_DOUBLE) { //双击事件
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
test_num += 2;
printf("\r\nDouble Press 双击,num:%d\r\n", test_num);
} else if(key_value == KEY0_LONG) { //长按事件
printf("\r\nLong Press 长按\r\n");
// 实现长按KEY
test_num = 0;//清除编码器计数值
printf("按键计数清零,num:%d\r\n", test_num);
}
}
}
/* USER CODE END 3 */
}
📚工程源码
c
链接:https://pan.baidu.com/s/1zTMkY8iEGSCu_gSavR_I3A?pwd=4m2q
提取码:4m2q