|
本帖最后由 passteen 于 2018-11-5 22:40 编辑
rt-thread是个非常好的国产操作系统,个人认为有linux的影子,在嵌入式操作系统中具备先天优势。
这个程序就是在rt-thread操作系统下验证的,但是这个程序不局限于他。
一般按键检测用于单击应用,对于双击,连发等功能其实是比较头疼的,这时候大多使用状态机处理了,
这里贴出一个变种程序,非常简单。事实上都可以不叫状态机了。
贴代码:
/********************************************************************************************************/
/* 文 件 名: app_button.c (变种按键状态机程序) */
/* 功 能: 实现按键的长按,单击,双击,n连发功能(最多255)。 */
/* 注意事项: 该代码基于STM-MCU,10ms调用一次。如时间更短则精度更好,但是需要修改判定参数。 */
/* 禁 忌: 无。 */
/* 环 境: 基于安富莱V6及rt-thread操作系统。 */
/********************************************************************************************************/
typedef struct
{
uint8_t (*butt_chk_func)(void); //读键值函数
uint8_t trig; //按键按下触发,不释放永远为0,释放也为0(上升沿检测)
uint8_t oldv; //按键值的拷贝
uint8_t pfilt; //按下触发滤波
uint8_t click; //trig发出的次数,实际就是按下的次数,每10ms检测一次
uint16_t prcnt; //按下时计数
uint16_t popcn; //按键释放时计数
}BUTTON_REG;
BUTTON_REG button_key[10]; //区别密钥变量,不至于混淆
/*
*********************************************************************************************************
* 函 数 名: butt_chkx
* 功能说明: 判断按键是否按下
* 形 参: 无
* 返 回 值: 返回值1 表示按下,0表示未按下
*********************************************************************************************************
*/
/* 安富莱 STM32-V5 开发板 */
static uint8_t butt_chk1(void) {if ((GPIO_PORT_K1->IDR & GPIO_PIN_K1) == 0) return 1;else return 0;}
static uint8_t butt_chk2(void) {if ((GPIO_PORT_K2->IDR & GPIO_PIN_K2) == 0) return 1;else return 0;}
static uint8_t butt_chk3(void) {if ((GPIO_PORT_K3->IDR & GPIO_PIN_K3) == 0) return 1;else return 0;}
static uint8_t butt_chk4(void) {if ((GPIO_PORT_K4->IDR & GPIO_PIN_K4) == 0) return 1;else return 0;}
static uint8_t butt_chk5(void) {if ((GPIO_PORT_K5->IDR & GPIO_PIN_K5) == 0) return 1;else return 0;}
static uint8_t butt_chk6(void) {if ((GPIO_PORT_K6->IDR & GPIO_PIN_K6) == 0) return 1;else return 0;}
static uint8_t butt_chk7(void) {if ((GPIO_PORT_K7->IDR & GPIO_PIN_K7) == 0) return 1;else return 0;}
static uint8_t butt_chk8(void) {if ((GPIO_PORT_K8->IDR & GPIO_PIN_K8) == 0) return 1;else return 0;}
static uint8_t butt_chk9(void) {if (butt_chk1() && butt_chk2()) return 1;else return 0;}
static uint8_t butt_chk10(void) {if (butt_chk1() && butt_chk2()) return 1;else return 0;}
/*
*********************************************************************************************************
* 函 数 名: button_gpio_init
* 功能说明: 配置按键对应的GPIO
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static void button_gpio_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* 第1步:打开GPIO时钟 */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOG_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOI_CLK_ENABLE();
/* 第2步:配置所有的按键GPIO为浮动输入模式(实际上CPU复位后就是输入状态) */
GPIO_InitStructure.Mode = GPIO_MODE_INPUT; /* 设为输入口 */
GPIO_InitStructure.Pull = GPIO_NOPULL; /* 无需上下拉电阻 */
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW; /* IO口最大速度 */
GPIO_InitStructure.Pin = GPIO_PIN_K1;
HAL_GPIO_Init(GPIO_PORT_K1, &GPIO_InitStructure);
GPIO_InitStructure.Pin = GPIO_PIN_K2;
HAL_GPIO_Init(GPIO_PORT_K2, &GPIO_InitStructure);
GPIO_InitStructure.Pin = GPIO_PIN_K3;
HAL_GPIO_Init(GPIO_PORT_K3, &GPIO_InitStructure);
GPIO_InitStructure.Pin = GPIO_PIN_K4;
HAL_GPIO_Init(GPIO_PORT_K4, &GPIO_InitStructure);
GPIO_InitStructure.Pin = GPIO_PIN_K5;
HAL_GPIO_Init(GPIO_PORT_K5, &GPIO_InitStructure);
GPIO_InitStructure.Pin = GPIO_PIN_K6;
HAL_GPIO_Init(GPIO_PORT_K6, &GPIO_InitStructure);
GPIO_InitStructure.Pin = GPIO_PIN_K7;
HAL_GPIO_Init(GPIO_PORT_K7, &GPIO_InitStructure);
GPIO_InitStructure.Pin = GPIO_PIN_K8;
HAL_GPIO_Init(GPIO_PORT_K8, &GPIO_InitStructure);
}
/*
*********************************************************************************************************
* 函 数 名: button_reg_clear
* 功能说明: 将按键结构体变量(寄存器组)清零
* 参 数: 按键编号,用于扫描按键。
* 返 回 值: 无
*********************************************************************************************************
*/
static void button_reg_clear(uint8_t id)
{
button_key[id].trig = 0;
button_key[id].oldv = 0;
button_key[id].pfilt = 0;
button_key[id].click = 0;
button_key[id].prcnt = 0;
button_key[id].popcn = 0;
}
/*
*********************************************************************************************************
* 函 数 名: button_init
* 功能说明: 配置按键对应的GPIO
* 形 参: 无
* 返 回 值: 无
* 注 :rt-thread导入函数需要int类型,因此.... 别忘了加return
*********************************************************************************************************
*/
static int button_init(void)
{
uint8_t i;
button_gpio_init();
for(i=0;i<10;i++)
{
button_reg_clear(i);
}
button_key[0].butt_chk_func = butt_chk1;
button_key[1].butt_chk_func = butt_chk2;
button_key[2].butt_chk_func = butt_chk3;
button_key[3].butt_chk_func = butt_chk4;
button_key[4].butt_chk_func = butt_chk5;
button_key[5].butt_chk_func = butt_chk6;
button_key[6].butt_chk_func = butt_chk7;
button_key[7].butt_chk_func = butt_chk8;
button_key[8].butt_chk_func = butt_chk9;
button_key[9].butt_chk_func = butt_chk10;
return 0;
}
INIT_BOARD_EXPORT(button_init);
/*
*********************************************************************************************************
* 函 数 名: button_detect
* 功能说明: 检测一个按键。非阻塞状态,必须被周期性的调用。
* 参 数: 按键编号,用于扫描按键。
* 返 回 值: 无
*********************************************************************************************************
*/
void button_detect(uint8_t id)
{
uint8_t key_val;
key_val = button_key[id].butt_chk_func();
button_key[id].trig = key_val&(key_val ^ button_key[id].oldv); //核心算法,万能网有介绍
button_key[id].oldv = key_val; //不得与上一语句顺序颠倒
if(button_key[id].trig)
{ button_key[id].popcn=0; //有按下则弹出计数器清零
button_key[id].click += button_key[id].trig; //按下次数,有效滤波
}
button_key[id].prcnt += button_key[id].oldv; //如果按下记录有效,按下次数累加
if(button_key[id].oldv==0 && button_key[id].click)
button_key[id].popcn++; //按键已释放,此时如果按下次数不为0则释放次数累加
}
/*
*********************************************************************************************************
* 函 数 名: button_entry
* 功能说明: rt-thread进程入口函数,判断按键结果。
* 参 数: rt-thread内核的要求,具体暂时不明。
* 返 回 值: 无
*********************************************************************************************************
*/
static void button_entry(void* parameter)
{
uint8_t i;
while(1)
{
for(i=0;i<10;i++)
{
if(button_key.click) //如果有按键按下
{
if(button_key.popcn>20) //如果释放时间(计数)超过20次(200ms),注意该值不要太小,否则会有误触发,这与按键动作的速度相关
{
if(button_key.prcnt<100)
rt_kprintf("clicks: %d \n",button_key.click);
button_reg_clear(i)
}
else if(button_key.click==1 && button_key.prcnt>99) //首先要判断长按
{
rt_kprintf("press long \n");
}
}
}
rt_thread_delay(10);
}
}
/*
*********************************************************************************************************
* 函 数 名: button_scan
* 功能说明: rt-thread进程创建,导入APP应用,不需要加入main函数,有利于功能模块独立
* 参 数: 无
* 返 回 值: rt-thread要求有返回值
*********************************************************************************************************
*/
static int button_scan(void)
{
rt_thread_t button_tid=rt_thread_create("button",button_entry,
RT_NULL, 256,10,10);
if(button_tid != RT_NULL)
rt_thread_startup(button_tid);
return 0;
}
INIT_APP_EXPORT(button_scan);
|
|