利用按键实现某个动态线程创建和删除 ---- 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);

实验效果如下,实现了使用按键动态创建和删除线程。
在这里插入图片描述

四、参考资料

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