静态创建
1.定义任务函数(一个无限循环且不带返回值的函数)
任务必须是一个死循环,否则任务将通过 LR 返回,如果 LR 指向了非法的内存就会产生 HardFault_Handler,而 FreeRTOS 指向一个死循环,那么任务返回之后就在死循环中执行,这样子的任务是不安全的。
所以避免这种情况,任务一般都是死循环并且无返回值的。如果任务,执行一次之后就进行删除,则不影响系统运行,所以,只执行一次的任务在执行完毕要记得及时删除。
任务里面的延时函数必须使用 FreeRTOS 里面提供的延时函数,
并不能使用我们裸机编程中的那种延时。这两种的延时的区别是 FreeRTOS 里面的延时是阻塞延时,即调用 vTaskDelay()函数的时候,当前任务会被挂起,调度器会切换到其它就绪的任务,从而实现多任务。
如果还是使用裸机编程中的那种延时,那么整个任务就成为了一个死循环,如果恰好该任务的优先级是最高的,那么系统永远都是在这个任务中运行,比它优先级更低的任务无法运行,根本无法实现多任务。
当任务进入延时的时候,因为没有另外就绪的用户任务,那么系统就会进入空闲任务,空闲任务是 FreeRTOS 系统自己启动的一个任务,优先级最低。当整个系统都没有就绪任务的时候,系统必须保证有一个任务在运行,空闲任务就是为这个设计的。当用户任务延时到期,又会从空闲任务切换回用户任务。
2.定义任务栈
任务使用的栈和任务控制块是预先定义好的全局变量。
在 FreeRTOS 系统中,每一个任务都是独立的,他们的运行环境都单独的保存在他们的栈空间当中。那么在定义好任务函数之后,我们还要为任务定义一个栈任务的栈占用的是 MCU 内部的 RAM,当任务越多的时候,需要使用的栈空间就越大,即需要使用的RAM空间就越多。一个 MCU 能够支持多少任务,就得看你的 RAM空间有多少。
在大多数系统中需要做栈空间地址对齐,在 FreeRTOS 中是以 8 字节大小对齐。当然用户可以在 portmacro .h中修改宏的值为 1、2、4、8、16、32 等字节对齐
3.定义任务控制块
定义好任务函数和任务栈之后,我们还需要为任务定义一个任务控制块,通常我们称这个任务控制块为任务的身份证。在 C 代码上,任务控制块就是一个结构体,里面有非常多的成员,这些成员共同描述了任务的全部信息
4.创建任务
一个任务的三要素是任务主体函数,任务栈,任务控制块,那么怎么样把这三个要素联合在一起?FreeRTOS 里面有一个叫静态任务创建函数 xTaskCreateStatic(),它就是干这个活的。它将任务主体函数,任务栈(静态的)和任务控制块(静态的)这三者联系在一起,让任务可以随时被系统启动。
5.启动任务
当任务创建好后,是处于任务就绪( Ready ) ,处于就绪态的任务可以参与操作系统的调度。
但是此时任务仅仅是创建了,还未开启任务调度器,也没创建空闲任务与定时器任务(如果使能了 configUSE_TIMERS 这个宏定义),那这两个任务就是在启动任务调度器中实现。每个操作系统,任务调度器只启动一次,之后就不会再次执行了。
FreeRTOS 中启动任务调度器的函数是 vTaskStartScheduler(),并且启动任务调度器的时候就不会返回,从此任务管理都由FreeRTOS 管理,此时才是真正进入实时操作系统中的第一步
动态创建
创建任务—SRAM 动态内存,任务使用的栈和任务控制块是在创建任务的时候FreeRTOS 动态分配的。
动态内存空间的堆从哪里来?
动态内存,即堆,其实堆也是内存,也属于 SRAM。FreeRTOS 做法是在 SRAM 里面定义一个大数组,也就是堆内存,供 FreeRTOS 的动态内存分配函数使用,
在第一次使用的时候,系统会将定义的堆内存进行初始化,这些代码在 FreeRTOS 提供的内存管理方案中实现(heap_1.c、heap_2.c、heap_4.c等)
1.定义任务函数
使用动态内存的时候,任务的主体函数与使用静态内存时是一样的。
2.定义任务栈
使用动态内存的时候,任务栈在任务创建的时候创建,不用跟使用静态内存那样要预先定义好一个全局的静态的栈空间,动态内存就是按需分配内存,随用随取。
3.定义任务控制块指针
使用动态内存时候,不用跟使用静态内存那样要预先定义好一个全局的静态的任务控制块空间。任务控制块是在任务创建的时候分配内存空间创建,任务创建函数会返回一个指针,用于指向任务控制块,所以要预先为任务栈定义一个任务控制块指针,也是我们常说的任务句柄
4.动态创建任务
使用静态内存时,使用 xTaskCreateStatic()来创建一个任务,而使用动态内存的时,则使用 xTaskCreate()函数来创建一个任务,两者的函数名不一样,具体的形参也有区别。
1 /* 创建 AppTaskCreate 任务 */
2 xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate, /* 任务入口函数 */(1)
3 (const char* )"AppTaskCreate",/* 任务名字 */(2)
4 (uint16_t )512, /* 任务栈大小 */ (3)
5 (void* )NULL,/* 任务入口函数参数 */ (4)
6 (UBaseType_t )1, /* 任务的优先级 */ (5)
7 (TaskHandle_t* )&AppTaskCreate_Handle);/* 任务控制块指针 */ (6)
(1) :任务入口函数,即任务函数的名称,需要我们自己定义并且实现。
(2) :任务名字,字符串形式, 最大长度由 FreeRTOSConfig .h 中定义的
(3) :任务堆栈大小,单位为字,在 32 位的处理器下(STM32),一个字等于 4 个字节,那么任务大小就为 128 * 4 字节。
(4) :任务入口函数形参,不用的时候配置为 0 或者 NULL即可。
(5) : 任务的优先级,数值越大优先级越高,0 代表最低优先级。
(6) :任务控制块指针,在使用内存的时候,需要给任务初始化函数
xTaskCreateStatic()传递预先定义好的任务控制块的指针。在使用动态内存的时候,任务创建函数 xTaskCreate()会返回一个指针指向任务控制块,该任务控制块是 xTaskCreate()函数里面动态分配的一块内存。
6.启动任务
同上