一、功能需求
通过检测按键,实现线程的动态创建和删除。
二、实现原理
- 1、检测两个按键,按键1按下,动态创建线程,按键2按下,删除线程。
其中涉及到按键的消抖。 - 2、按键的信息传入消息队列,创建线程a来读取消息队列的信息,然后在其线程入口函数读取消息队列的数据,根据这些数据来判断创建还是删除队列。
三、代码实现
**注:**利用定时器来进行按键消抖的方法来自何志杰老师的视频课程,下面使用何老师的按键消抖部分代码。
- 1、使用STM32CubeMx配置Tim2为1ms中断。
- 2、按键消抖的实现
.h文件:
/****************************************Copyright (c)**************************************************
** 福建师范大学物理与能源学院
**------------------------------------------------------------------------------------------------------
** 文件: Button.h
** 版本: v1.0
**------------------------------------------------------------------------------------------------------
** 描述:
** 按键检测文件
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/
#ifndef __BUTTON__H__
#define __BUTTON__H__
#define BUTTON_FILTER_TIME 10 //消抖时间,单位为ms
/*******************************************************************************************************
** 函数: Button_t,初始化按键变量
********************************************************************************************************/
typedef struct
{
unsigned char (*IsKeyDownFunc)(void); // 函数指针,指向判断按键是否按下的函数
unsigned char wFilterCount; // 滤波器计数器
unsigned char wFilterTime; // 滤波时间(最大255,表示255ms)
unsigned short wLongCount; // 长按计数器
unsigned short wLongTime; // 长按键时间, 0表示不检测长按
unsigned char byState; // 按键当前状态(按下还是弹起)
unsigned char byKeyCodeUp; // 按键弹起的键值代码, 0表示不检测按键弹起
unsigned char byKeyCodeDown; // 按键按下的键值代码, 0表示不检测按键按下
}Button_t;
/*******************************************************************************************************
** 函数: Button_t,按键枚举变量
********************************************************************************************************/
typedef enum
{
KEY_NONE = 0, // 0 表示无按键事件
KEY1_DOWN, // KEY1键按下
KEY1_UP, // KEY1键弹起
KEY2_DOWN, // KEY2键按下
KEY2_UP, // KEY2键弹起
}Key_em;
/*******************************************************************************************************
** 函数: ButtonProj,按键处理函数
**------------------------------------------------------------------------------------------------------
** 参数: void
** 返回: void
********************************************************************************************************/
void ButtonProj(void);
#endif
//-----------------------------------------------------------------------------------------------
// 按键事件队列
static rt_mq_t button_mq = RT_NULL;//按键事件队列定义
//-----------------------------------------------------------------------------------------------
// 按键定义
static Button_t s_tBtnKey1;
static Button_t s_tBtnKey2;
/****************************************************************************************/
//用户添加自定义接口变量
/****************************************************************************************/
//-------------------------------------------------------------------------------
// Key1 按键引脚
#define GPIO_Pin_Key1 GPIO_PIN_4
#define GPIO_Mode_Key1 GPIO_Mode_IPU
#define GPIO_Key1 GPIOE
#define Key1In HAL_GPIO_ReadPin(GPIO_Key1, GPIO_Pin_Key1)
//-------------------------------------------------------------------------------
// Key2 按键引脚
#define GPIO_Pin_Key2 GPIO_PIN_3
#define GPIO_Mode_Key2 GPIO_Mode_IPU
#define GPIO_Key2 GPIOE
#define Key2In HAL_GPIO_ReadPin(GPIO_Key2 , GPIO_Pin_Key2)
//-----------------------------------------------------------------------------------------------
// Key按键按下时的电平,=0,按下时为低电平;=1,按下时为高电平
#define KeyPressedLevel 0
//-----------------------------------------------------------------------------------------------
// 获取按键按下函数
static unsigned char IsKey1Down(void) {if (Key1In != KeyPressedLevel) return 0; return 1;}
static unsigned char IsKey2Down(void) {if (Key2In != KeyPressedLevel) return 0; return 1;}
/*******************************************************************************************************
** 函数: ButtonVarInit,初始化按键变量
**------------------------------------------------------------------------------------------------------
** 参数: void
** 返回: void
********************************************************************************************************/
static void ButtonVarInit(void)
{
s_tBtnKey1.IsKeyDownFunc = IsKey1Down; // 检测按键按下函数
s_tBtnKey1.wFilterCount=0; // 滤波器计数器
s_tBtnKey1.wFilterTime = BUTTON_FILTER_TIME; // 滤波时间
s_tBtnKey1.wLongCount = 0; // 长按计数器
s_tBtnKey1.byState=0; // 按键当前状态(按下还是弹起)
s_tBtnKey1.byKeyCodeUp=KEY1_UP; // 按键弹起的键值代码, 0表示不检测按键弹起
s_tBtnKey1.byKeyCodeDown=KEY1_DOWN; // 按键按下的键值代码, 0表示不检测按键按下
s_tBtnKey2.IsKeyDownFunc=IsKey2Down; // 检测按键按下函数
s_tBtnKey2.wFilterCount=0; // 滤波器计数器
s_tBtnKey2.wFilterTime =BUTTON_FILTER_TIME; // 滤波时间
s_tBtnKey2.wLongCount =0; // 长按计数器
s_tBtnKey2.byState=0; // 按键当前状态(按下还是弹起)
s_tBtnKey2.byKeyCodeUp = KEY2_UP; // 按键弹起的键值代码, 0表示不检测按键弹起
s_tBtnKey2.byKeyCodeDown = KEY2_DOWN; // 按键按下的键值代码, 0表示不检测按键按下
}
/*******************************************************************************************************
** 函数: ButtonDetect,Button按键检测函数
**------------------------------------------------------------------------------------------------------
** 参数: ptBtn 按键结构体指针
** 返回: void
********************************************************************************************************/
static void ButtonDetect(Button_t *ptBtn)
{
if(ptBtn->IsKeyDownFunc && ptBtn->IsKeyDownFunc()) // 检测按键函数是否存在,按键是否按下
{
if(ptBtn->wFilterCount < ptBtn->wFilterTime) // 滤波检测,滤波计数器到达滤波时间
{
ptBtn->wFilterCount++; // 计数器加一
return; // 退出检测函数
}
if(ptBtn->byState == 0) // 检测是否是按键按下
{
ptBtn->byState = 1;
rt_mq_send(button_mq, // 写入(发送)队列的ID(句柄)
&(ptBtn->byKeyCodeDown), // 写入(发送)的数据所对应地址
sizeof(ptBtn->byKeyCodeDown) // 数据的长度
);
return;
}
}
else
{
if(ptBtn->wFilterCount) // 滤波检测,滤波计数器是否为0
{
ptBtn->wFilterCount--; // 计数器减一
return; // 退出检测函数
}
if(ptBtn->byState == 1) // 检测是否是按键弹起
{
ptBtn->byState = 0;
}
}
}
/*******************************************************************************************************
** 函数: ButtonProj,按键处理(扫描)函数
**------------------------------------------------------------------------------------------------------
** 参数: void
** 返回: void
********************************************************************************************************/
void ButtonProj(void)
{
//该函数在定时器中断回调函数中调用,定时中断周期为1ms
ButtonDetect(&s_tBtnKey1); // 检测 Key1 键
ButtonDetect(&s_tBtnKey2); // 检测 Key2 键
}
- 3、然后在定时器1ms中断中的回调函数调用按键检测
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim == (&htim2))
{
ButtonProj(); // 也就是每1ms检测一次
}
}
- 4、创建一个线程,对另一个线程进行创建和删除
// 要被创建和删除的线程,若创建成功,会每隔1s打印“Task is running”
void tid2_thread_entry(void *parameter)//用户消息处理入口函数
{
while(1)
{
rt_thread_mdelay(1000);
rt_kprintf("Task is running\n");
}
}
/*******************************************************************************************************
** 函数: button_thread_entry,获取按键事件并进行处理
**------------------------------------------------------------------------------------------------------
** 参数: void
** 返回: void
********************************************************************************************************/
void button_thread_entry(void *parameter)//用户消息处理入口函数
{
rt_err_t uwRet = RT_EOK;
uint8_t r_queue; //用于接收msg_mq消息队列信息
rt_thread_t tid2 = RT_NULL;
/*使能定时器2中断*/
HAL_TIM_Base_Start_IT(&htim2);
ButtonVarInit(); // 初始化按键结构体的参数
button_mq = rt_mq_create("button_mq", //消息队列名字
1, //消息的最大长度, bytes
256, //消息队列的最大容量(个数)
RT_IPC_FLAG_FIFO //队列模式 FIFO
);
if(button_mq != RT_NULL)
rt_kprintf("button_mq create success\n\n");
while(1)
{ //获取队列信息
uwRet = rt_mq_recv(button_mq,
&r_queue,
sizeof(r_queue),
RT_WAITING_FOREVER
);
if(RT_EOK == uwRet)
{
switch(r_queue)//根据接收到的消息内容分别进行处理
{
case KEY1_DOWN:
if(tid2 != RT_NULL) // 检查是否已被创建,若已被创建,不再创建
break;
/* 创建线程*/
tid2 = rt_thread_create("tid2", // 线程名字
tid2_thread_entry, // 线程入口函数
RT_NULL, // 线程入口参数
256, // 堆栈大小,
5, // 线程优先级
5); // 时间片长度
/* 如果获得线程控制块,启动这个线程 */
if (tid2 != RT_NULL)
rt_thread_startup(tid2);
rt_kprintf("Task created\n");
break;
case KEY2_DOWN:
if(tid2 != RT_NULL)
{
if(RT_EOK == rt_thread_delete(tid2))
{
rt_kprintf("Task stop\n");
tid2 = RT_NULL;
}
}
break;
default:
break;
}
}
else
{
rt_kprintf("数据接收错误,错误代码:0x%lx\n\n",uwRet);
}
}
}
// 按键处理线程初始化
int button_process_init(void)
{
rt_thread_t tid;
tid = rt_thread_create("button_process",
button_thread_entry, RT_NULL,
BUTTON_THREAD_STACK_SIZE, BUTTON_THREAD_PRIORITY, 10
);
if (tid != NULL)
rt_thread_startup(tid);
return 0;
}
INIT_APP_EXPORT(button_process_init);
实验效果如下,实现了使用按键动态创建和删除线程。
四、参考资料
- 《何志杰 – RT-thread入门课程》 – 10.RT-Thread Nano-通用定时器(按键消抖)-消息队列
- 【STM32】HAL库 STM32CubeMX教程六----定时器中断