一、功能需求
通過檢測按鍵,實現線程的動態創建和刪除。
二、實現原理
- 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教程六----定時器中斷