FreeRTOS任务创建

什么是任务

在 FreeRTOS 中,任务就是一个函数,原型如下:

void ATaskFunction( void *pvParameters );

 这个函数不能返回
 同一个函数,可以用来创建多个任务;换句话说,多个任务可以运行同一
个函数
 函数内部,尽量使用局部变量:
 每个任务都有自己的栈
 每个任务运行这个函数时
 任务 A 的局部变量放在任务 A 的栈里、任务 B 的局部变量放在任务 B
的栈里
 不同任务的局部变量,有自己的副本
 函数使用全局变量、静态变量的话
 只有一个副本:多个任务使用的是同一个副本
 要防止冲突

任务栈

在一个裸机系统中, 如果有全局变量, 有子函数调用, 有中断发生。 那么系统在运行的时候, 全局变量放在哪里, 子函数调用时, 局部变量放在哪里, 中断发生时, 函数返回地址放哪里。 如果只是单纯的裸机编程, 它们放哪里我们不用管, 但是如果要写一个 RTOS, 这些种种环境参数, 我们必须弄清楚他们是如何存储的。 在裸机系统中, 他们统统放在一个叫栈的地方, 栈是单
片机 RAM 里面一段连续的内存空间, 栈的大小一般在启动文件或者链接脚本里面指定, 最后由 C 库函数 main 进行初始化。
但是, 在多任务系统中, 每个任务都是独立的, 互不干扰的, 所以要为每个任务都分配独立的栈空间, 这个栈空间通常是一个预先定义好的全局数组, 也可以是动态分配的一段内存空间, 但它们都存在于 RAM 中。

在多任务系统中, 有多少个任务就需要定义多少个任务栈

#define TASK1_STACK_SIZE 128 

StackType_t Task1Stack[TASK1_STACK_SIZE]; 

#define TASK2_STACK_SIZE 128

StackType_t Task2Stack[TASK2_STACK_SIZE];

任务栈其实就是一个预先定义好的全局数组, 数据类型为StackType_t, 大小由 TASK1_STACK_SIZE 这个宏来定义, 默认为 128, 单位为字, 即 512 字节, 这也是 FreeRTOS 推荐的最小的任务栈。

任务控制块

在多任务系统中,
任务的执行是由系统调度的。 系统为了顺利的调度任务, 为每个任务都额外定义了一个任务控制块, 这个任务控制块就相当于任务的身份证, 里面存有任务的所有信息, 比如任务的栈指针, 任务名称, 任务的形参等。 有了这个任务控制块之后, 以后系统对任务的全部操作都可以通过这个任务控制块来实现。

typedef struct tskTaskControlBlock
{
volatile StackType_t *pxTopOfStack; /* 栈顶 */ (1)

ListItem_t xStateListItem; /* 任务节点 */ (2)

StackType_t *pxStack; /* 任务栈起始地址 */ (3)

/* 任务名称, 字符串形式 */(4)
char pcTaskName[ configMAX_TASK_NAME_LEN ];

} tskTCB;

typedef tskTCB TCB_t; (5)

代码(1): 栈顶指针, 作为 TCB 的第一个成员。
代码(2): 任务节点, 这是一个内置在 TCB 控制块中的链表节点, 通过这个节点, 可以将任务控制块挂接到各种链表中。 这个节点就类似晾衣架的钩子, TCB就是衣服。
代码(3): 任务栈起始地址。
代码(4): 任务名称, 字符串形式, 长度由宏 configMAX_TASK_NAME_LEN 来控制, 该宏在 FreeRTOSConfig.h 中定义, 默认为 16。
代码(5): 数据类型重定义。 

调度器

调度器的启动由 vTaskStartScheduler()函数来完成
调度器是操作系统的核心, 其主要功能就是实现任务的切换, 即从就绪列表里面找到优先级最高的任务, 然后去执行该任务。

创建任务

动态方法

BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, // 函数指针, 任务函数

const char * const pcName, // 任务的名字

const configSTACK_DEPTH_TYPE usStackDepth, // 栈大小,单位为 word,10 表示
40 字节

void * const pvParameters, // 调用任务函数时传入的参数

UBaseType_t uxPriority, // 优先级

TaskHandle_t * const pxCreatedTask ); // 任务句柄, 以后使用它来操作这个任
务

静态方法 

//创建开始任务
StartTask_Handler=xTaskCreateStatic((TaskFunction_t )start_task, //任务函数 (1)

(const char* )"start_task",//任务名称 (2)

(uint32_t )START_STK_SIZE,//任务堆栈大小(3)

(void* )NULL,//传递给任务函数的参数 (4)

(UBaseType_t )START_TASK_PRIO, //任务优先级 (5)

(StackType_t* )StartTaskStack,//任务堆栈 (6)

(StaticTask_t* )&StartTaskTCB);//任务控制块 (7)

代码(1): 任务入口函数, 即任务函数的名称, 需要我们自己定义并且实现。

代码(2): 任务名字, 字符串形式, 最大长度由 FreeRTOSConfig.h 中定义的configMAX_TASK_NAME_LEN 宏指定, 多余部分会被自动截掉, 这里任务名字最好要与任务函数入口名字一致, 方便进行调试。

代码(3): 任务堆栈大小, 单位为字, 在 32 位的处理器下(STM32) , 一个字等于 4 个字节, 那么任务大小就为 128 * 4 字节。

代码(4): 任务入口函数形参, 不用的时候配置为 0 或者 NULL 即可。

代码(5): 任务的优先级。 优先级范围根据 FreeRTOSConfig.h 中的宏configMAX_PRIORITIES 决定, 如果使能configUSE_PORT_OPTIMISED_TASK_SELECTION, 这个宏定义, 则最多支持 32 个优先级; 如果不用特殊方法查找下一个运行的任务, 那么则不强制要求限制最大可用优先级数目。 在 FreeRTOS 中, 数值越大优先级越高, 0 代表最低优先级。

代码(6): 任务栈起始地址, 只有在使用静态内存的时候才需要提供, 在使用动态内存的时候会根据提供的任务栈大小自动创建。

代码(7): 任务控制块指针, 在使用静态内存的时候, 需要给任务初始化函数 xTaskCreateStatic()传递预先定义好的任务控制块的指针。 在使用动态内存的时候, 任务创建函数 xTaskCreate()会返回一个指针指向任务控制块, 该任务控制块是 xTaskCreate()函数里面动态分配的一块内存。

编程(以动态方法创建) 

创建两个LED灯,分别控制其闪烁

//任务优先级
#define START_TASK_PRIO		1
//任务堆栈大小	
#define START_STK_SIZE 		128  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);

//任务优先级
#define LED1_TASK_PRIO		2
//任务堆栈大小	
#define LED1_STK_SIZE 		50  
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);

//任务优先级
#define LED2_TASK_PRIO		3
//任务堆栈大小	
#define LED2_STK_SIZE 		50  
//任务句柄
TaskHandle_t LED2Task_Handler;
//任务函数
void led2_task(void *pvParameters);


/*******************************************************************************
* 函 数 名         : main
* 函数功能		   : 主函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
int main()
{
	SysTick_Init(72);
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
	LED_Init();
	USART1_Init(115200);
	
	//创建开始任务
    xTaskCreate((TaskFunction_t )start_task,            //任务函数
                (const char*    )"start_task",          //任务名称
                (uint16_t       )START_STK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )START_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
    vTaskStartScheduler();          //开启任务调度
}

//开始任务任务函数
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //进入临界区
      
    //创建LED1任务
    xTaskCreate((TaskFunction_t )led1_task,     
                (const char*    )"led1_task",   
                (uint16_t       )LED1_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )LED1_TASK_PRIO,
                (TaskHandle_t*  )&LED1Task_Handler); 
				
	//创建LED2任务
    xTaskCreate((TaskFunction_t )led2_task,     
                (const char*    )"led2_task",   
                (uint16_t       )LED2_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )LED2_TASK_PRIO,
                (TaskHandle_t*  )&LED2Task_Handler); 
				
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
} 

//LED1任务函数
void led1_task(void *pvParameters)
{
    while(1)
    {
        LED1=0;
        vTaskDelay(200);
        LED1=1;
        vTaskDelay(800);
    }
}

//LED2任务函数
void led2_task(void *pvParameters)
{
    while(1)
    {
        LED2=0;
        vTaskDelay(800);
        LED2=1;
        vTaskDelay(200);
    }
}



 




 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值