IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> FreeRTOS(二)创建任务 -> 正文阅读

[嵌入式]FreeRTOS(二)创建任务

任务

一. 任务的基本概念

  1. FreeRTOS的任务可以认为是一系列独立的任务的集合。

  2. 每个任务在自己的环境中运行,并且每个时刻只有一个任务在运行,但从宏观上看,所有的任务都在同时执行;
    不同任务的切换与任务本身无关,这是由调度器来实现的。调度器负责在任务切入、切出时保存上下文环境(寄存器值、栈内容);
    所以每个任务都有自己独立的栈空间,任务切出时,其执行环境会被保存在该任务的栈空间中,当任务再次运行时,就能从栈中恢复上次的运行环境。

  3. 任务越多,需要的栈空间就越大,一个系统能运行多少个任务,取决于系统可用的SRAM。

  4. 任务采用抢占式调度机制,高优先级的任务可打断低优先级任务,低优先级任务必须在高优先级任务阻塞后才能得到调度,
    FreeRTOS也支持时间片轮转调度方式,只不过时间片的调度不允许抢占任务的CPU使用权。

  5. 任务通常运行在一个死循环中,也不会退出,如果不需要某个任务,可以调用删除任务API函数将其删除。

  6. 任务里面的延时函数必须使用 FreeRTOS 里面提供的延时函数,并不能使用我们裸机编程中的那种延时。
    这两种的延时的区别:
    FreeRTOS 里面的延时是阻塞延时,即调用 vTaskDelay()函数的时候,当前任务会被起,调度器会切换到其它就绪的任务,从而实现多任务。
    如果还是使用裸机编程中的那种延时,那么整个任务就成为了一个死循环,如果恰好该任务的优先级是最高的,那么系统永远都是在这个任务中运行,比它优先级更低的任务无法运行,根本无法实现多任务。

二. 任务调度器的基本概念

  1. FreeRTOS中提供的任务调度器是基于优先级的全抢占调度:在系统中除了中断处理函数、调度器上锁部分的代码和禁止中断的代码不可抢占之外,
    系统的其他部分都是可以抢占的。

  2. FreeRTOS内核中采用两种方法寻找最高优先级的任务,一般和特殊。

  3. FreeRTOS内核中允许创建相同优先级的任务。相同优先级的任务采用时间片轮转方式进行调度(分时调度器),
    时间片轮转调度仅在当前系统中无更高优先级就绪任务存在的情况下才有效。

  4. 任务调度的原则:一旦任务状态发生改变,并且当前运行的任务优先级小于优先级队列组中任务最高优先级时,立刻进行任务切换,(除非当前系统正处于中断处理程序中或禁止任务切换的状态)。

三. 任务状态的概念

  1. 就绪(ready)态: 该任务在就绪列表中,就绪的任务已经具备执行能力,只等待调度器进行调度,新创建的任务会被初始化为就绪态。
  2. 运行(running)态: 该状态表明任务正在执行,此时它占用处理器,FreeRTOS调度器选择运行的永远是处于最高优先级的就绪态任务,当任务开始运行的一刻,就变成了运行态。
  3. 阻塞(blocked)态: 如果任务当前正在等待某个时序或外部中断,我们就说这个任务处于阻塞态,该任务不在就绪列表中,包含任务挂起、任务延时、任务正在等待信号量、读写队列或者等待读写事件等。
  4. 挂起(suspended)态: 处于挂起态的任务对调度器而言是不可见的,让一个任务进入挂起态的唯一办法就是调用vTaskSuspend()函数;
    而恢复一个挂起态的任务的唯一途径是调用vTaskResum()或vTaskResumFromISR()。

四. 创建一个LED任务

? 首先一定要有一个空闲任务,空闲任务是 FreeRTOS 系统自己启动的一个任务,优先级最低。当整个系统都没有就绪任务的时候,系统必须保证有一个任务在运行,空闲任务就是为这个设计的。当用户任务延时到期,又会从空闲任务切换回用户任务。如果当系统中始终有一个在运行的任务时,可以不加空闲任务。

? 一个任务的三要素是任务主体函数,任务栈,任务控制块。FreeRTOS 里面有一个叫动态任务创建函数 xTaskCreate(),它将任务主体函数,任务栈(动态的)和任务控制块(动态的)这三者联系在一起,让任务可以随时被系统启动。

创建任务的步骤

  1. 定义任务函数
static void LED_Task(void* parameter)
{
    while(1)
    {
        LED1_ON;
        vTaskDelay(500); 
        printf("LED_Task Running,LED1_ON\r\n");
        LED1_OFF;     
        vTaskDelay(500);	 		
        printf("LED_Task Running,LED1_OFF\r\n");
    }
}
  1. 定义任务的堆栈
static StackType_t LED_Task_Stack[128];
  1. 定义任务控制块,任务控制块就是一个结构体,里面有非常多的成员,这些成员共同描述了任务的全部信息
static StaticTask_t LED_Task_TCB;
  1. 定义硬件初始化函数

stm32中断优先级分组为4,即4位都用来表示抢占优先级,范围为0~15,优先级只需要分组一次即可,以后如果有其他的任务需要用到中断时,都统一用这个优先级分组,不能再分组。

static void BSP_Init(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
    LED_GPIO_Config();
    USART_Config();
}
  1. 利用静态任务创建函数 xTaskCreateStatic()或动态任务创建函数xTaskCreate()来创建用户任务(这里是动态创建一个任务),创建任务一定要在临界区中创建,即不能被中断打断。临界段被打断的两种情况:一个是系统调度,还有一个就是外部中断。系统调度,最终也是产生Pendsv中断,在PendSV handler里面实现任务的切换。因此,FreeRTos对临界段的保护最终还是回到对中断的开和关的控制。
static void AppTaskCreate(void)
{
    BaseType_t xReturn = pdPASS;
    taskENTER_CRITICAL();           //进入临界区
    /* 创建LED_Task任务 */
    xReturn = xTaskCreate((TaskFunction_t	)LED_Task,			/* 任务入口函数 */
                          (const char* 	)"LED_Task",			/* 任务名字 */
                          (uint16_t 		)512,				/* 任务栈大小 */
                          (void* 		  	)NULL,				/* 任务入口函数参数 */
                          (UBaseType_t 	)2, 					/* 任务的优先级 */
                          (TaskHandle_t* )&LED_Task_Handle);	/* 任务控制块指针 */ 

    if(pdPASS == xReturn)/* 创建成功 */
        printf("LED_Task任务创建成功!\n");
    else
        printf("LED_Task任务创建失败!\n");

    vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务
    taskEXIT_CRITICAL();            //退出临界区
}
  1. 在main函数中创建AppTaskCreate任务,任务创建完后要开启任务调度器,这样任务才会开始运行。因为当一个任务创建好后,是处于任务就绪(Ready),在就绪态的任务可以参与操作系统的调度。
int main(void)
{	
    BaseType_t xReturn = pdPASS;
    BSP_Init();		//硬件初始化
    /* 创建 AppTaskCreate 任务 */
    xReturn = xTaskCreate((TaskFunction_t	)AppTaskCreate,			/* 任务入口函数 */
                          (const char* 	)"AppTaskCreate",			/* 任务名字 */
                          (uint16_t 		)512,					/* 任务栈大小 */
                          (void* 		  	)NULL,					/* 任务入口函数参数 */
                          (UBaseType_t 	)1, 						/* 任务的优先级 */
                          (TaskHandle_t* )&AppTaskCreate_Handle);	/* 任务控制块指针 */ 

    if(pdPASS == xReturn)				/* 创建成功 */
        vTaskStartScheduler();   		/* 启动任务,开启调度 */
    else
        return -1;
    while(1);   						/* 正常不会执行到这里 */    
}

整个main.c文件全貌

/*动态创建单个任务*/

/* FreeRTOS头文件 */
#include "FreeRTOS.h"
#include "task.h"
/* 开发板硬件bsp头文件 */
#include "bsp_led.h"
#include "bsp_usart.h"

static TaskHandle_t AppTaskCreate_Handle = NULL;	/* 定义创建任务句柄 */
static TaskHandle_t LED_Task_Handle = NULL;			/* 定义LED任务句柄 */

static void AppTaskCreate(void);				/* 用于创建任务 */
static void LED_Task(void* pvParameters);		/* LED_Task任务实现 */
static void BSP_Init(void);						/* 用于初始化板载相关资源 */

int main(void)
{	
    BaseType_t xReturn = pdPASS;
    BSP_Init();
    /* 创建 AppTaskCreate 任务 */
    xReturn = xTaskCreate((TaskFunction_t	)AppTaskCreate,			/* 任务入口函数 */
                          (const char* 	)"AppTaskCreate",			/* 任务名字 */
                          (uint16_t 		)512,					/* 任务栈大小 */
                          (void* 		  	)NULL,					/* 任务入口函数参数 */
                          (UBaseType_t 	)1, 						/* 任务的优先级 */
                          (TaskHandle_t* )&AppTaskCreate_Handle);	/* 任务控制块指针 */ 

    if(pdPASS == xReturn)		/* 创建成功 */
        vTaskStartScheduler();  /* 启动任务,开启调度 */
    else
        return -1;
    while(1);   				/* 正常不会执行到这里 */    
}

static void AppTaskCreate(void)
{
    BaseType_t xReturn = pdPASS;
    taskENTER_CRITICAL();           //进入临界区
    /* 创建LED_Task任务 */
    xReturn = xTaskCreate((TaskFunction_t	)LED_Task,			/* 任务入口函数 */
                          (const char* 	)"LED_Task",			/* 任务名字 */
                          (uint16_t 		)512,				/* 任务栈大小 */
                          (void* 		  	)NULL,				/* 任务入口函数参数 */
                          (UBaseType_t 	)2, 					/* 任务的优先级 */
                          (TaskHandle_t* )&LED_Task_Handle);	/* 任务控制块指针 */ 

    if(pdPASS == xReturn)/* 创建成功 */
        printf("LED_Task任务创建成功!\n");
    else
        printf("LED_Task任务创建失败!\n");

    vTaskDelete(AppTaskCreate_Handle); 		//删除AppTaskCreate任务
    taskEXIT_CRITICAL();            		//退出临界区
}

static void LED_Task(void* parameter)
{
    while(1)
    {
        LED1_ON;
        vTaskDelay(500);   /* 延时500个tick */
        printf("LED_Task Running,LED1_ON\r\n");

        LED1_OFF;     
        vTaskDelay(500);   /* 延时500个tick */		 		
        printf("LED_Task Running,LED1_OFF\r\n");	
    }
}

static void BSP_Init(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
    LED_GPIO_Config();
    USART_Config();
}

五. 静态创建和动态创建的区别

任务使用的栈和任务控制块是预先定义好的全局变量。

静态创建任务函数

static TaskHandle_t xxx_Task_Handle;		//创建任务句柄
static StackType_t  xxx_Task_Stack[num];	//创建任务堆栈
static StaticTask_t xxx_Task_TCB;			//创建任务控制块
//在config中配置
configSUPPORT_STATIC_ALLOCATION == 1

    TaskHandle_t xTaskCreateStatic(	TaskFunction_t pxTaskCode,			//任务函数
                                   const char * const pcName,			//任务名称
                                   const uint32_t ulStackDepth,			//任务堆栈大小
                                   void * const pvParameters,			//给任务函数的参数
                                   UBaseType_t uxPriority,				//任务优先级
                                   StackType_t * const puxStackBuffer,	//任务堆栈
                                   StaticTask_t * const pxTaskBuffer )	//任务控制块

创建任务—SRAM 动态内存,任务使用的栈和任务控制块是在创建任务的时候FreeRTOS 动态分配的,一般我们用动态创建。

动态创建任务函数

static TaskHandle_t xxx_Task_Handle = NULL;	//要定义任务句柄

//在config中配置
configSUPPORT_DYNAMIC_ALLOCATION == 1

    BaseType_t xTaskCreate(	TaskFunction_t pxTaskCode,					//任务函数
                           const char * const pcName,					//任务名称
                           const uint16_t usStackDepth,					//任务堆栈大小
                           void * const pvParameters,					//给任务函数的参数
                           UBaseType_t uxPriority,						//任务优先级
                           TaskHandle_t * const pxCreatedTask )			//任务控制块指针
  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-02-26 11:46:32  更:2022-02-26 11:48:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 9:57:53-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码