FreeRTOSFreeRTOS学习笔记(11)— FreeRTOS的线程管理定时器管理(CMSIS_API)

Posted 果果小师弟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FreeRTOSFreeRTOS学习笔记(11)— FreeRTOS的线程管理定时器管理(CMSIS_API)相关的知识,希望对你有一定的参考价值。

一、线程管理

osThreadCreate

函数原型

osThreadId osThreadCreate (const osThreadDef_t *thread_def, void *argument)

功能:使用 osThreadDef 宏所定义的结构体变量来创建一个线程。创建好线程后,然后进入 READY 状态,等待任务管理来调度运行。

参数

  • 参数 1:指定osThreadDef所定义结构体变量的指针,通过该指针即可访问结构体变量,然后使用里面的信息来创建线程。由于结构体变量名字的前面有一个os_thread_def_前缀,所以需要使用 osThread宏来添加前缀,如果指定的名字为task1的话,第一个参数应该写为osThread(task1),进行宏替换后的最终效果为&os_thread_def_task1。实际上我们完全可以将第一个参数直接写为&os_thread_def_task1,不过使用osThread宏显然会更方便一些。

  • 参数 2:传递给线程函数的参数。线程函数的参数 argument 的值就来自于这里,如果没有什么参数要传递的,就设置为NULL。

返回值:

  • 函数调用成功就返回唯一标识线程的线程 ID(句柄),如果失败就返回 NULL。

之前我们通过cubemx创建了2个认任务,删掉FreeRTOS.c中创建线程的代码,然后我们自己来添加创建线程的代码。

案例 1:只创建一个线程实例

#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"
#include "SEGGER_RTT.h"

osThreadId myTask01Handle;//任务一句柄ID

void StartTask01(void const * argument);//任务一函数声明


void MX_FREERTOS_Init(void) 
{
  osThreadDef(myTask01, StartTask01, osPriorityNormal, 0, 128);
  myTask01Handle = osThreadCreate(osThread(myTask01), NULL);
}

void StartTask01(void const * argument)
{
    for(;;)
    {
			SEGGER_RTT_printf(0, "StartTask01 is running\\r\\n");
			osDelay(200);
    }
}

案例 2:创建多个线程实例

比如使用同一个线程的结构体变量来创建两个线程,我们这里可以将创建代码放到StartTask01线程函数中,该线程启动起来后会创建两个线程。在前面就讲过,一次创建多个线程实例,其实就是将一个线程函数被注册为多个线程。

#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"
#include "SEGGER_RTT.h"

osThreadId myTask01Handle;//任务一句柄ID
osThreadId myTask02Handle;//任务一句柄ID
osThreadId myTask03Handle;//任务一句柄ID

void StartTask01(void const * argument);//任务一函数声明
void StartTask02(void const * argument);//任务二函数声明

void MX_FREERTOS_Init(void) 
{
	osThreadDef(myTask01, StartTask01, osPriorityNormal, 0, 128);
	myTask01Handle = osThreadCreate(osThread(myTask01), NULL);
}

void StartTask01(void const * argument)
{
	/* 一次性创建多个线程实例 */
	osThreadDef(myTask02, StartTask02, osPriorityNormal, 2, 128);
	myTask02Handle = osThreadCreate(osThread(myTask02),  (void *)2);
	myTask03Handle = osThreadCreate(osThread(myTask02),  (void *)2);
	for(;;)
	{
		SEGGER_RTT_printf(0, "StartTask01 is running\\r\\n");
		osDelay(200); //200ms
	}
}

/* 第 2 个线程和第 3 个线程运行的是相同的线程函数 */
void StartTask02(void const * argument)
{
	uint8_t value = (int)argument;
	for(;;)
	{
		SEGGER_RTT_printf(0, "value = %d\\r\\n", value);
		osDelay(200);
	}
}

osThreadGetId(获取线程ID句柄)

函数原型:osThreadId osThreadGetId (void)
功能:获取线程 ID句柄。

//参数:无
//返回值:调用成功就返回线程 ID句柄,失败就返回 NULL
//案例:让每个线程获取自己的ID句柄并打印,由于ID句柄的类型为指针类型,因此应该使用`%p`来打印。
osThreadId osThreadGetId (void)
{
#if ( ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) || ( configUSE_MUTEXES == 1 ) )
  return xTaskGetCurrentTaskHandle();
#else
	return NULL;
#endif
}

如何得到线程自己的ID句柄?

  • 方法 1:创建线程时将ID句柄保存在了全局变量中,可以从全局变量中获取到。
  • 方法 2:线程可以通过调用osThreadGetId函数来获取自己的线程 ID句柄。

osThreadGetPriority(获取线程的优先级)

  • 函数原型:osPriority osThreadGetPriority (osThreadId thread_id)
//功能:获取指定 ID 的那个线程的优先级
//参数:线程 ID
//返回值:优先级
osPriority osThreadGetPriority (osThreadId thread_id)
{
#if (INCLUDE_uxTaskPriorityGet == 1)
  if (inHandlerMode())
  {
    return makeCmsisPriority(uxTaskPriorityGetFromISR(thread_id));  
  }
  else
  {  
    return makeCmsisPriority(uxTaskPriorityGet(thread_id));
  }
#else
  return osPriorityError;
#endif
}

案例

StartTask01函数中获取并打印所有线程的优先级。

#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"
#include "SEGGER_RTT.h"

osThreadId myTask01Handle;//任务一句柄ID
osThreadId myTask02Handle;//任务一句柄ID
osThreadId myTask03Handle;//任务一句柄ID

void StartTask01(void const * argument);//任务一函数声明
void StartTask02(void const * argument);//任务二函数声明


void MX_FREERTOS_Init(void) 
{
	osThreadDef(myTask01, StartTask01, osPriorityNormal, 0, 128);
	myTask01Handle = osThreadCreate(osThread(myTask01), NULL);
}

void showPriority(uint8_t no, osPriority pri)
{
	if(osPriorityIdle == pri) SEGGER_RTT_printf(0,"%d: %s\\r\\n", no,"osPriorityIdle");		
	else if(osPriorityLow == pri) SEGGER_RTT_printf(0,"%d: %s\\r\\n", no,"osPriorityLow");		
	else if(osPriorityBelowNormal == pri)SEGGER_RTT_printf(0,"%d: %s\\r\\n",no,"osPriorityBelowNormal");		
	else if(osPriorityNormal == pri)SEGGER_RTT_printf(0,"%d: %s\\r\\n", no,"osPriorityNormal");		
	else if(osPriorityAboveNormal == pri)SEGGER_RTT_printf(0,"%d: %s\\r\\n",no,"osPriorityAboveNormal");		
	else if(osPriorityHigh == pri)SEGGER_RTT_printf(0,"%d: %s\\r\\n", no,"osPriorityHigh");		
	else if(osPriorityRealtime == pri)SEGGER_RTT_printf(0,"%d: %s\\r\\n",no,"osPriorityRealtime");
}

void StartTask01(void const * argument)
{
	/* 一次性创建多个线程实例 */
	osThreadDef(myTask02, StartTask02, osPriorityNormal, 2, 128);
	myTask02Handle = osThreadCreate(osThread(myTask02),  (void *)2);
	myTask03Handle = osThreadCreate(osThread(myTask02),  (void *)2);
	
	osPriority thd01Pri; //即 Task01 线程
	osPriority thd02Pri;
	osPriority thd03Pri;
	
	for(;;)
		{
			/* 获取当前线程的优先级 */
			thd01Pri= osThreadGetPriority(osThreadGetId());
			//或者 osThreadGetPriority(myTask01Handle);
			showPriority(1, thd01Pri); //1:代表 StartTask01,currThdPri:优先级
			
			/* 获取其它两个线程的优先级 */
			thd02Pri = osThreadGetPriority(myTask02Handle);
			showPriority(2, thd02Pri); //2:StartTask02
			
			thd03Pri = osThreadGetPriority(myTask03Handle);
			showPriority(3, thd03Pri); //3:StartTask03
			
			SEGGER_RTT_printf(0, "StartTask01 is running\\r\\n");
			osDelay(200); //200ms
		}
}

/* 第 2 个线程和第 3 个线程运行的是相同的线程函数 */
void StartTask02(void const * argument)
{
	uint8_t value = (int)argument;
	for(;;)
	{
		//SEGGER_RTT_printf(0, "value = %d\\r\\n", value);
		osDelay(200);
	}
}


比如开发了一款产品时,如果用户需要查看某些线程的优先级的话,那么此时就需要使用这个函数来获取线程的优先级。

osThreadSetPriority

函数原型:osStatus osThreadSetPriority (osThreadId thread_id, osPriority priority);

功能:设置线程优先级

  • 创建线程我们需要指定优先级,在运行的过程中如果需要修改优先级的话,此时我们就需要调用此函数来修改。

参数

  • thread_id:线程ID句柄,通ID句柄找到线程,然后修改线程的优先级
  • priority:要设置的优先级

返回值:

  • 返回值类型 osStatus。
    如果返回OS_OK就表示函数调用成功,如果返回错误码就表示函数调用失败了。

举例

thd03Pri = osThreadGetPriority(myTask03Handle);//获取StartTask03的优先级
showPriority(3, thd03Pri); //打印StartTask03的优先级
osThreadSetPriority(myTask03Handle , osPriorityAboveNormal);//设置StartTask03的优先级为osPriorityAboveNormal
showPriority(3, thd03Pri);//打印StartTask03的优先级。打印的结果是:3:osPriorityAboveNormal

示例代码:

#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"
#include "SEGGER_RTT.h"

osThreadId myTask01Handle;//任务一句柄ID
osThreadId myTask02Handle;//任务一句柄ID
osThreadId myTask03Handle;//任务一句柄ID

void StartTask01(void const * argument);//任务一函数声明
void StartTask02(void const * argument);//任务二函数声明


void MX_FREERTOS_Init(void) 
{
	osThreadDef(myTask01, StartTask01, osPriorityNormal, 0, 128);
	myTask01Handle = osThreadCreate(osThread(myTask01), NULL);
}

void showPriority(uint8_t no, osPriority pri)
{
	if(osPriorityIdle == pri) 
		SEGGER_RTT_printf(0,"%d: %s\\r\\n", no,"osPriorityIdle");
		
	else if(osPriorityLow == pri) 
		SEGGER_RTT_printf(0,"%d: %s\\r\\n", no,"osPriorityLow");
		
	else if(osPriorityBelowNormal == pri) 
		SEGGER_RTT_printf(0,"%d: %s\\r\\n",no,"osPriorityBelowNormal");
		
	else if(osPriorityNormal == pri) 
		SEGGER_RTT_printf(0,"%d: %s\\r\\n", no,"osPriorityNormal");
		
	else if(osPriorityAboveNormal == pri) 
		SEGGER_RTT_printf(0,"%d: %s\\r\\n",no,"osPriorityAboveNormal");
		
	else if(osPriorityHigh == pri) 
		SEGGER_RTT_printf(0,"%d: %s\\r\\n", no,"osPriorityHigh");
		
	else if(osPriorityRealtime == pri) 
		SEGGER_RTT_printf(0,"%d: %s\\r\\n",no,"osPriorityRealtime");
}

void StartTask01(void const * argument)
{
	/* 一次性创建多个线程实例 */
	osThreadDef(myTask02, StartTask02, osPriorityNormal, 2, 128);
	myTask02Handle = osThreadCreate(osThread(myTask02),  (void *)2);
	myTask03Handle = osThreadCreate(osThread(myTask02),  (void *)2);
	
	osPriority thd01Pri; //即 Task01 线程
	osPriority thd02Pri;
	osPriority thd03Pri;
	
	for(;;)
	{
		/* 获取任务一线程的优先级 */
		thd01Pri= osThreadGetPriority(osThreadGetId());//或者 osThreadGetPriority(myTask01Handle);			
		showPriority(1, thd01Pri); //1:代表 StartTask01,currThdPri:优先级
		/* 重新设置任务一线程的优先级 */
		osThreadSetPriority(myTask01Handle , osPriorityAboveNormal);//设置StartTask03的优先级为osPriorityAboveNormal
		showPriority(1, thd01Pri);//打印StartTask03的优先级。打印的结果是:3:osPriorityAboveNormal
		
		/*获取任务二线程的优先级 */
		thd02Pri = osThreadGetPriority(myTask02Handle);
		showPriority(2, thd02Pri); //2:StartTask02
		/* 重新设置任务二线程的优先级 */
		osThreadSetPriority(myTask02Handle , osPriorityAboveNormal);//设置StartTask03的优先级为osPriorityAboveNormal
		showPriority(2, thd02Pri);//打印StartTask03的优先级。打印的结果是:3:osPriorityAboveNormal
		
		/*获取任务三线程的优先级 */
		thd03Pri = osThreadGetPriority(myTask03Handle);
		showPriority(3, thd03Pri); //3:StartTask03
		/* 重新设置任务三线程的优先级 */
		osThreadSetPriority(myTask03Handle , osPriorityAboveNormal);//设置StartTask03的优先级为osPriorityAboveNormal
		showPriority(3, thd03Pri);//打印StartTask03的优先级。打印的结果是:3:osPriorityAboveNormal

		osDelay(1000); //1000ms
	}
}

当然开发的产品需要人为修改任务的优先级时,就可以使用这个函数来修改。

osThreadTerminate

函数原型:osStatus osThreadTerminate(osThreadId thread_id)
功能:终止线程
终止线程的方式:

  • 方式 1:线程函数执行完毕后会自然终止
  • 方式 2:调用 osThreadTerminate 函数强行终止

参数:要终止线程的线程 ID
返回值:osStatus,不再介绍

案例:

/* StartTask03 线程函数 */
void StartTask01(void const * argument)
{
	/* 创建单个线程实例,2:两个线程实例 */
	...
	...
	while(1)
	{
		/* 获取线程优先级 */
		...
		...
		...
		osDelay(1000);
		osThreadTerminate(myTask02Handle); //强行终止 StartTask02
		osThreadTerminate(myTask03Handle); //强行终止 StartTask03
		break;//退出循环,自然终止当前线程,或者使用osThreadTerminate(osThreadGetId())也可以
	}
}

osThreadYield

  • 函数原型:osStatus osThreadYield(void)
  • 功能:主动让出cpu占用权
  • 参数:无
  • 返回值:osStatus
  • 案例:当前正在运行的线程在时间片还没到之前,可以主动调用这个函数让出 CPU,让 CPU执行其它线程。

那么什么时候需要这样做呢?
答:当线程后续代码没有执行意义时就可以调用这个函数主动让出 CPU。

二、延时函数

1、宏

osFeature_wait

  • 如果为 1:表示 osWait 函数可用。
  • 如果为 0:表示 osWait 函数不可用。
    这个宏默认就是 0,也就说默认情况下osWait函数不可用,就算该宏为 1 也没用,因为osWait 并没有定义。

2、API

osDelay

(a)函数原型:osStatus osDelay (uint32_t millisec)
(b)功能:实现毫秒(ms)级的延时。
(c)参数:延时时间,如果设置为 1000,表示延时 1000ms(1s)
(d)返回值:osStatus
(e)案例:在前面的例子中已经使用过这个函数,因此这里不再举例。
(f)与通过循环实现的延时函数的区别

我们以前的课程在实现延时时,往往通过循环来实现,比如:

int i=0, j=0;
for(i=0; i<2000; i++)
{
	for(j=0; j<2000; j++)
	{
	}
}

循环延时的本质是让 cpu 执行循环指令,说白了就是让 cpu 空转,从而达到延时的效果,但是实际上是在占用 cpu 资源的,真正的延时是休眠,也就是说调用延时函数时进入休眠状态,所谓休眠就是不占用 cpu,cpu 此时既可以去执行其它的指令

osDelay:真正延时函数。当线程调用该函数时,线程会进入休眠状态,线程不再占用 cpu 资源,cpu 转而去执行其它线程的指令。

osWait

前面说过,这个函数名没有是实现,因此这里不做介绍。就算这个函数实现了也没用,因为 osFeature_wait默认为 0,表示osWait函数可用。

三、定时器管理

3.1、宏、枚举

3.1.1、osTimerDef 宏

宏原型

#define osTimerDef(name, function) \\
const osTimerDef_t os_timer_def_##name = { (function) }


通过宏原型可以看出,这个宏与前面介绍的 osThreadDef 是类似的。

作用

使用结构体类型osTimerDef_t定义一个定时器数据结构(结构体变量),后续就使用这个数据结构来创建一个软件定时器,软件定时器底层也是通过Systick(tim1)硬件定时器来实现的,只不过提供了软件层面的管理,因此叫软件定时器。

typedef struct os_timer_def  {
  os_ptimer                 ptimer;///< start address of a timer function
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
  osStaticTimerDef_t        *controlblock;  ///< control block to hold timer's data for static allocation; NULL for dynamic allocation
#endif
} osTimerDef_t;

参数

  • name:指定名字,结构体变量名会在指定名字前加os_timer_def_前缀。如果指定名字为timer1,那么结构体变量的名字就为os_timer_def_timer1
  • function:定时时间到后调用的处理函数。
    定时时间到后,会自动调用这个函数,进行相应的处理,比如定时到后点亮LED,点亮LED 的代码就可以写到这个函数中,如果什么也不想做,那就写一个空函数。

定时器所用函数的格式为固定格式:

void 名字(const void *参数名 )
{
...
...
}

案例

void timFun(const void *argument )
{
FreeRTOSFreeRTOS学习笔记— 手写FreeRTOS双向链表/源码分析

FreeRTOSFreeRTOS学习笔记— 开始创建任务并测试任务代码

FreeRTOSFreeRTOS学习笔记— 学习FreeRTOS的编程风格和本质

FreeRTOSFreeRTOS学习笔记— 任务创建删除挂起和恢复

FreeRTOSFreeRTOS学习笔记— FreeRTOS任务与协程

FreeRTOSFreeRTOS学习笔记(10)— FreeRTOS的osThreadDef创建任务(CMSIS_API)