利用按鍵實現某個動態線程創建和刪除 ---- RT-thread&&STM32

一、功能需求

通過檢測按鍵,實現線程的動態創建和刪除。

二、實現原理

  • 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);

實驗效果如下,實現了使用按鍵動態創建和刪除線程。
在這裏插入圖片描述

四、參考資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章