FreeRTOS笔记(二)—静态任务
文章目录
一、任务定义
在多任务系统中,我们根据功能的不同,把整个系统分割 成一个个独立的且无法返回的函数,这个函数我们称为任务。
二、任务创建
2.1 定义任务栈
写一个 RTOS,对于全局变量与局部变量这些种种环境参数,我们必须弄清楚他们是如何存储的。
在裸机系统 中,他们统统放在一个叫栈的地方,栈是单片机 RAM 里面一段连续的内存空间,栈的大小一般在启动文件或者链接脚本里面指定,最后由C库函数_main进行初始化。
在多任务系统中,每个任务都是独立的,互不干扰的,所以要为每个任务都分配独立的栈空间,这个栈空间通常是一个预先定义好的全局数组,也可以是动态分配的一段内存空间,但它们都存在于RAM中。
多任务系统中,有多少个任务就需要定义多少个任务栈。
示例 1 定义任务栈
/*
#define portSTACK_TYPE uint32_t
typedef portSTACK_TYPE StackType_t;
*/
#define TASK1_STACK_SIZE 128
StackType_t Task1Stack[TASK1_STACK_SIZE];
#define TASK2_STACK_SIZE 128
StackType_t Task2Stack[TASK2_STACK_SIZE];
2.2 定义任务函数
任务是一个独立的函数,函数主体无限循环且不能返回。
示例 2 任务函数定义
/* 软件延时 */
void delay (uint32_t count)
{
for(; count!=0; count--);
}
/* 任务1 */
void Task1_Entry( void *p_arg )
{
for( ;; )
{
flag1 = 1;
delay( 100 );
flag1 = 0;
delay( 100 );
}
}
/* 任务2 */
void Task2_Entry( void *p_arg )
{
for( ;; )
{
flag2 = 1;
delay( 100 );
flag2 = 0;
delay( 100 );
}
}
2.3 定义任务控制块
-
在裸机系统中,程序的主体是 CPU 按照顺序执行的。而在多任务系统中,任务的执行是由系统调度的。
-
系统为了顺利的调度任务,为每个任务都额外定义了一个任务控制块, 这个任务控制块就相当于任务的身份证,里面存有任务的所有信息,比如任务的栈指针, 任务名称,任务的形参等。
-
有了这个任务控制块之后,以后系统对任务的全部操作都可以通过这个任务控制块来实现。
示例 3 任务控制块数据类型声明
typedef struct tskTaskControlBlock
{
volatile StackType_t *pxTopOfStack; /* 栈顶 */
ListItem_t xStateListItem; /* 任务节点 一个内置在 TCB 控制块中的链表节点,通过 这个节点,可以将任务控制块挂接到各种链表中。这个节点就类似晾衣架的钩子,TCB 就是衣服。*/
StackType_t *pxStack; /* 任务栈起始地址 */
char pcTaskName[ configMAX_TASK_NAME_LEN ]; /* 任务名称,字符串形式 */
} tskTCB;
typedef tskTCB TCB_t;
2.4 实现任务创建函数
示例 4 任务创建函数xTaskCreateStatic()函数
/*
//任务句柄void指针
typedef void * TaskHandle_t;
//TaskFunction_t类型的pxTaskCode为函数指针,指向任务函数的入口。任务永远不会返回(位于死循环内)。
typedef void (*TaskFunction_t)( void * );//参数为void指针类型并返回void类型。
//
#define portSTACK_TYPE uint32_t
typedef portSTACK_TYPE StackType_t;
*/
TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode, /* 任务入口 */
const char * const pcName, /* 任务名称,字符串形式 ,字符串的最大长度(包括字符串结束字符)由宏configMAX_TASK_NAME_LEN指定,该宏位于FreeRTOSConfig.h文件中。*/
const uint32_t ulStackDepth, /* 任务栈大小,单位为字,不是字节数,在16位宽度的堆栈下,usStackDepth定义为100,则实际使用200字节堆栈存储空间。1字=2字节=16位 */
void * const pvParameters, /* 任务形参,当任务创建时,作为一个参数传递给任务。*/
StackType_t * const puxStackBuffer, /* 任务栈起始地址 */
TCB_t * const pxTaskBuffer ) /* 任务控制块指针 */
{
TCB_t *pxNewTCB;
TaskHandle_t xReturn;//定义一个任务句柄 xReturn,任务句柄用于指向任务的 TCB。
/*任务控制块指针与任务栈起始地址均非空时,形参赋值*/
if( ( pxTaskBuffer != NULL ) && ( puxStackBuffer != NULL ) )
{
pxNewTCB = ( TCB_t * ) pxTaskBuffer;
pxNewTCB->pxStack = ( StackType_t * ) puxStackBuffer;
/* 创建新的任务 */
prvInitialiseNewTask( pxTaskCode, /* 任务入口 */
pcName, /* 任务名称,字符串形式 */
ulStackDepth, /* 任务栈大小,单位为字 */
pvParameters, /* 任务形参 */
&xReturn, /* 任务句柄 */
pxNewTCB); /* 任务栈起始地址 */
}
else
{
xReturn = NULL;
}
/* 返回任务句柄,如果任务创建成功,此时xReturn应该指向任务控制块 */
return xReturn;
}
示例 5 创建新任务prvInitialiseNewTask()函数
/*
typedef unsigned long UBaseType_t;
*/
static void prvInitialiseNewTask( TaskFunction_t pxTaskCode, /* 任务入口 */
const char * const pcName, /* 任务名称,字符串形式 */
const uint32_t ulStackDepth, /* 任务栈大小,单位为字 */
void * const pvParameters, /* 任务形参 */
TaskHandle_t * const pxCreatedTask, /* 任务句柄 */
TCB_t *pxNewTCB ) /* 任务控制块指针 */
{
StackType_t *pxTopOfStack;
UBaseType_t x;
/* 获取栈顶地址 */
pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
/* 向下做8字节对齐 ,在 Cortex-M3(Cortex-M4 或 Cortex-M7)内核的单片机中,因为总线宽度是 32 位的,通常只要栈保持 4 字节对齐就行, 可这样为啥要 8字节?难道有哪些操作是 64位的?确实有,那就是浮点运算,所以要8字节对齐(但是目前都还没有涉及到浮点运算,只是为了后续兼容浮点运行的考虑)。如果栈顶指针是8字节对齐的,在进行向下8字节对齐的时候,指针不会移动,如果不是8字节对齐的,在做向下8字节对齐的时候,就会空出几个字节,不会使用,比如当 pxTopOfStack是 33,明显不能整除 8,进行向下 8字节对齐就是 32,那么就会空出一个字
节不使用。*/
pxTopOfStack = ( StackType_t * )

本文详细介绍了在FreeRTOS中如何创建静态任务,包括任务定义、任务栈、任务函数、任务控制块的创建,以及就绪列表的初始化和调度器的实现。通过示例代码展示了任务创建和调度过程,解释了任务切换的关键步骤。
最低0.47元/天 解锁文章
&spm=1001.2101.3001.5002&articleId=107597362&d=1&t=3&u=539220a007aa4d85b333b6260b258f45)
400

被折叠的 条评论
为什么被折叠?



