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)