freertos学习笔记

ps:stack即栈堆的意思
变量名定义:
KEIL中代码自动补全没有打开需要自己手动打开,具体方法参考网上教程。
在 FreeRTOS 中,int 型从不使用,只使用 short 和 long 型。在 FreeRTOS 中,定义变量的时候往往会把变量的类型当作前缀加在变量上,如 char 型变量的前缀是 c,short 型变量的前缀是 s,long 型变量的前缀是 l,portBASE_TYPE 类型变量的前缀是 x,函数名包含了函数返回值的类型、函数所在的文件名和函数的功能,如果是私有的函数则会加一个 prv(private)的前缀。
特别的,在函数名中加入了函数所在的文件名,这大大的帮助了用户提高寻找函数定义的效率和了解函数作用的目的,如vTaskPrioritySet()函数的返回值为 void 型,在task.c 这个文件中定义,xQueueReceive()函数的返回值为 portBASE_TYPE 型,在 queue.c 这个文件中定义。
轮询和前后台系统:轮询系统即是在裸机编程的时候,先初始化好相关的硬件,然后让主程序在一个死循环里面不断循环,顺序地做各种事情。轮询系统只适合顺序执行的功能代码,当有外部事件驱动时,实时性就会降低。
相比轮询系统,前后台系统是在轮询系统的基础上加入了中断。外部事件的响应在中断里面完成,事件的处理还是回到轮询系统中完成,中断在这里我们称为前台,main 函数里面的无限循环我们称为后台。
多任务系统
相比前后台系统,多任务系统的事件响应也是在中断中完成的,但是事件的处理是在任务中完成的。在多任务系统中,任务跟中断一样,也具有优先级,优先级高的任务会被优先执行。
相比前后台系统中后台顺序执行的程序主体,在多任务系统中,根据程序的功能,我们把这个程序主体分割成一个个独立的,无限循环且不能返回的小程序,这个小程序我们称之为任务。每个任务都是独立的,互不干扰的,且具备自身的优先级,它由操作系统调度管理。加入操作系统后,我们在编程的时候不需要精心地去设计程序的执行流,不用担心每个功能模块之间是否存在干扰。
每个任务块都是无限循环不可返回的,其有对应的任务控制块控制。相当于任务的身份证,里面存有任务的所有信息,比如任务的栈指针,任务名称,任务的形参等。
1创建任务
创建任务:任务的栈,任务的函数实体,任务的控制块最终需要联系起来才能由系统进行统一调度。那么这个联系的工作就由任务创建函数 xTaskCreateStatic()来实现,该函数在 task.c。
在创建如任务之前需要定义任务的栈和任务控制块,如
StackType_t Task2Stack[TASK2_STACK_SIZE];任务栈 TCB_t Task2TCB,任务块;在这里插入图片描述 2.任务就绪
创建好任务后需要加入任务就绪系统,表示任务已经就绪,系统随时可以调度。其中包含就绪列表等效于有限度列表,数组的大小由决定最 大 任 务 优 先 级最大支持 256 个优先级。数组的下标对应了任务的优先级。
将任务插入
到就绪列表里面,就是通过将任务控制块的 xStateListItem 这个节点插入到就绪列表中来实现的,一般加入就绪列表需要在创建任务之前初始化任务相关的列表如下。

/*初始化任务相关的列表,如就绪列表*/
prvInitialiseTaskLists();
...创建任务
/*将任务添加到就绪列表*/
 vListInsertEnd( &( pxReadyTasksLists[1] ), &( ((TCB_t *)(&Task1TCB))->xStateListItem ) );

3.调度器
调度器是操作系统的核心,其主要功能就是实现任务的切换,即从就绪列表里面找到优先级最高的任务,然后去执行该任务。
调度器的启动由 vTaskStartScheduler()函数来完成,该函数在 task.c 中定义。
同时当这个任务执行完不需要再在这个任务循环无限下去,可以启用任务切换,切换进入另一个任务,任务切换函数 taskYIELD()即可。
4.临界段
临界段用一句话概括就是一段在执行的时候不能被中断的代码段。在 FreeRTOS 里面,这个临界段最常出现的就是对全局变量的操作。
在这里插入图片描述
在这里插入图片描述
阻塞时延
当一个任务需要延迟,cpu不可能空等着这个任务,而是这个任务放弃CPU使用权,cpu去干其他事情,等延迟时间到,重新获得cpu使用权。cpu空闲的时候会进入空闲任务,主要就是对系统内存进行处理,阻塞时延使用函数vTaskDelay ()函数即可。
支持多优先级
c语言链表学习
链表就好比一个圆形的晾衣架晾衣架上面有很多钩子,钩子首尾相连。链表也是,链表由节点组成,节点与节点之间首尾相连。晾衣架的钩子本身不能代表很多东西,但是钩子本身却可以挂很多东西。
链表最大的作用是通过节点把离散的数据链接在一起,组成一个表。通常一条链表我们会人为地规定一个根节点,这个根节点称为生产者,方便节点的插入和删除。
链表有单向链表和双向链表,两者的区别是双向链表的节点中有两个节点指针,分别指向前后两个节点。
链表和数组的区别,链表是通过节点把离散的数据链接成一个表,通过对节点的插入和删除操作从而实现对数据的存取。而数组是通过开辟一段连续的内存来存储数据。数组有起始地址和结束地址,而链表是一个圈,没有头和尾之分,但是为了方便节点的插入和删除操作会人为的规定一个根节点。
多优先级
pxCurrenTCB 是一个全局的 TCB 指针,用于指向优先级最高的就绪任务的 TCB。任务支 持优先 级,即只 要解决 在任务切 换(taskYIELD)的时候,让 pxCurrenTCB 指向最高优先级的就绪任务的 TCB 就可以。
前面是用的YIELD手动进行切换,即在任务的栈“地址”上进行上下文切换,并不是找到最高优先级切换。要实现多优先级,先使用taskSELECT_HIGHEST_PRIORITY_TASK()寻找到优先级最高的就绪任务的 TCB,然后更新到 pxCurrentTCB即可。
任务时延表
前面实现任务的阻塞延时,每当任务需要延时的时候,就初始化 xTicksToDelay 需要延时的时间,然后将任务挂起,这里的挂起只是将任务在优先级位图表 uxTopReadyPriority 中对应的位清零,并不会将任务从就绪列表中删除。当每次时基中断(SysTick 中断)来临时,就扫描就绪列表中的每个任务的 xTicksToDelay,如果 xTicksToDelay 大于 0 则递减一次,然后判断xTicksToDelay 是否为 0,如果为 0 则表示延时时间到,将该任务就绪,然后进行任务切换,这样做太费时间。
采用任务时延表,当任务需要延时的时候,则先将任务挂起,即先将任务从就绪列表删除,然后插入到任务延时列表,同时更新下一个任务的解锁时刻变量:xNextTaskUnblockTime 的值。
xNextTaskUnblockTime 的值等于系统时基计数器的值 xTickCount 加上任务需要延时的值 xTicksToDelay。当系统时基计数器 xTickCount 的值与 xNextTaskUnblockTime 相等时,就表示有任务延时到期了,需要将该任务就绪,这里即系统运行了需要延时的值 xTicksToDelay的时间,即延迟确定时间到了,跟前面相比这里直接删除就绪列表,然后比较系统时间和xNextTaskUnblockTime ,相比于前面的更简洁。
具体实现方法是prvAddCurrentTaskToDelayedList()函数,在这里插入图片描述
支持时间片
所谓时间片就
是同一个优先级下可以有多个任务,每个任务轮流地享有相同的 CPU 时间,享有 CPU 的时间我们叫时间片。在 RTOS 中,最小的时间单位为一个 tick,即 SysTick 的中断周期。
时间片即指分时复用,在很短的一段时间即最小的时间单位为一个 tick内运行任务1,下一个tick运行任务2,因为时间间隔短,宏观看就是同时运行,即并行。
STM32移植
FreeRTOS/ Source 文件夹下的文件包含的是 FreeRTOS 的通用的头文件和 C 文件,这两部分的文件试用于各种编译器和处理器,是通用的。需要移植的头文件和 C文件放在编号portblle 这个文件夹,其中portblle内是对应的各种编译器如keil,CCS,IAR和gcc等。
一般移植rtos时需要在该工程下加入两个文件夹,一个source和port,src文件夹用于保存 FreeRTOS 中的核心源文件,也就是我们常说的‘.c 文件’,port 文件夹用于保存内存管理以及处理器架构相关代码,这些代码都在freertos源文件夹里有,只需要移植即可。
1.src的目录在E:\STM32\FreeRTOS\FreeRTOSv9.0.0\FreeRTOS\Source下的所有.c文件
2.打开 FreeRTOS V9.0.0 源码,在“FreeRTOSv9.0.0\FreeRTOS\Source\portable”目录下找到“MemMang”文件夹与“RVDS”文件夹,将它们拷贝到我们新建的port 文件夹中
3.在 FreeRTOS V9.0.0 源码中,source目录下找到include文件移植到自己的include文件中
4.拷贝一份FreeRTOSConfig.h 文件到user文件夹下,FreeRTOSConfig.h 文件是 FreeRTOS 的工程配置文件,因为 FreeRTOS 是可以裁剪的实时操作内核,应用于不同的处理器平台,用户可以通过修改这个 FreeRTOS 内核的配置头文件来裁剪 FreeRTOS 的功能。
上面的三个文件就是FreeRTOS的核心文件,但是这上面只是加入电脑的文件夹内,并没有添加到开发环境里面的组文件夹里面,FreeRTOS 也就没有移植到我们的工程中去。
1.先在工程中右键add group,创建附属文件夹,FreeRTOS/Ports和FreeRTOS/Source,然后点击两个文件夹在里面添加文件,如在port中添加内存管理文件,heap-4.c和对应的芯片接口源文件port.c,这个port一定要对应芯片型号不能弄错,如stm32f429是M4架构所以用的port是ARM_CM4_MPU里面的port.c。
2.再加载对应的include,你需要用哪些头文件就加载哪些,不然编译要编译好久。
3.然后还要加载配置文件,这里需要对应芯片型号和编译器平台,如CORTEX_STM32F103_Keil里的FreeRTOSConfig.h,这里用的stm32f429所以先用f103的然后再改一下。
4.指定头文件路径,不然编译会报错,前面只是将头文件添加到开发环境的组文件夹下面,需要指定三个路径,第一个是include路径,第二个是USER下的FreeRTOSConfig.h 文件,指定user路径即可,第三个是port里面有也有一个头文件,在FreeRTOS\port\RVDS\ARM_CM里面。
5.修改FreeRTOSConfig.h文件,这里直接使用野火的即可。
创建任务:
硬件初始化我们可以在 main.c 文件中创建一个 BSP_Init()函数,专门用于存放板级外设初始化函数,一般任务都是一个死循环且无返回,如果有返回值就会非法产生内存,回到FreeRTOS,FreeRTOS指向一个死循环,那么任务返回之后就在死循环中执行,这样子的任务是不安全的,一般而言,任务执行完需要删除。
任务里面的延时函数必须使用 FreeRTOS 里面提供的延时函数,并不能使用我们裸机编程中的那种延时。这两种的延时的区别是 FreeRTOS 里面的延时是阻塞延时,即调用 vTaskDelay()函数的时候,当前任务会被挂起,调度器会切换到其它就绪的任务,从而实现多任务。如果还是使用裸机编程中的那种延时,那么整个任务就成为了一个死循环,如果恰好该任务的优先级是最高的,那么系统永远都是在这个任务中运行,比它优先级更低的任务无法运行,根本无法实现多任务,其中空闲任务的优先级最低。
定义任务栈:在 FreeRTOS 系统中,每一个任务都是独立的,他们的运行环境都单独的保存在他们的栈空间当中。那么在定义好任务函数之后,我们还要为任务定义一个栈,目前我们使用的是静态内存,所以任务栈是一个独立的全局变量。
静态创建任务:xTaskCreateStatic()在这里插入图片描述
启动任务:当任务创建好后,是处于任务就绪(Ready),在就绪态的任务可以参与操作系统的调度。但是此时任务仅仅是创建了,还未开启任务调度器,也没创建空闲任务与定时器任务(如果使能了 configUSE_TIMERS 这个宏定义),那这两个任务就是在启动任务调度器中实现,每个操作系统,任务调度器只启动一次,之后就不会再次执行了,FreeRTOS 中启动任务调度器的函
数是 vTaskStartScheduler(),并且启动任务调度器的时候就不会返回,从此任务管理都由
FreeRTOS管理,此时才是真正进入实时操作系统中的第一步。、在这里插入图片描述
开启任务调度后进入任务,在任务中死循环,当一个任务接受就会被删除,然后按照任务优先级进行下一个任务,最低优先级是空闲任务,不能回到主函数中因为主函数是死循环没办法退出的,而任务的死循环是可以中断并且删除的。
动态创建任务:在创建单任务—SRAM 静态内存的例程中,任务控制块和任务栈的内存空间都是从内部的 SRAM 里面分配的,具体分配到哪个地址由编译器决定。动态创建就是在 SRAM 里面定义一个大数组,也就是堆内存,供 FreeRTOS 的动态内存分配函数使用,可以自己定义内存地址和位置。
使用动态内存的时候,任务栈在任务创建的时候创建,不用跟使用静态内存那样要预先定义好一个全局的静态的栈空间,动态内存就是按需分配内存,随用随取。使用静态内存时,使用 xTaskCreateStatic()来创建一个任务,而使用动态内存的时,则使用 xTaskCreate()函数来创建一个任务。在这里插入图片描述
进入一个app任务创建任务,创建两个任务,然后进入临界区,即不会被打断,最后需要删除apptaskccreate任务,退出临界区。
创建任务并不会立刻启动,需要运行任务调度才会运行任务。
freertos启动流程:
第一种:
1.RTOS 系统初始化。比如 RTOS 里面的全局变量的初始化,空闲任务的创建等。不同的 RTOS,它们的初始化有细微的差别。
2.创建各种任务。这里把所有要用到的任务都创建好,但还不会进入调度,因为这个时候 RTOS 的调度器还没有开启
3.启动 RTOS 调度器,开始任务调度。这个时候调度器就从刚刚创建好的任务中选择一个优先级最高的任务开始运行
4.任务实体通常是一个不带返回值的无限循环的 C 函数,函数体必须有阻塞的情况出现,不然任务(如果优先权恰好是最高)会一直在 while 循环里面执行,导致其它任务没有执行的机会
在这里插入图片描述
第二种:在这里插入图片描述
第二种和第一种的区别是,开启一个开始任务去创建其他子任务,当打开了任务调度,先进入开始任务,创建其他子任务,之后再删除开始任务,这时系统只有两个进程运行任务1和任务2,开始任务结束。
任务管理:
FreeRTOS 的任务可认为是一系列独立任务的集合。每个任务在自己的环境中运行。在任何时刻,只有一个任务得到运行,FreeRTOS 调度器决定运行哪个任务。调度器会不断的启动、停止每一个任务,宏观看上去所有的任务都在同时在执行。在任务切入切出时保存上下文环境(寄存器值、堆栈内容)是调度器主要的职责。为了实现这点,每个 FreeRTOS 任务都需要有自己的栈空间。当任务切出时,它的执行环境会被保存在该任务的栈空间中,这样当任务再次运行时,就能从堆栈中正确的恢复上次的运行环境,任务越多,需要的堆栈空间就越大,而一个系统能运行多少个任务,取决于系统的可用的 SRAM。FreeRTOS 中的任务是抢占式调度机制,高优先级的任务可打断低优先级任务,低优先级任务必须在高优先级任务阻塞或结束后才能得到调度。同时 FreeRTOS 也支持时间片轮转调度方式,只不过时间片的调度是不允许抢占任务的 CPU 使用权,即可以只运行单个任务也可以分时复用多任务并行。任务通常会运行在一个死循环中,也不会退出,如果一个任务不再需要,可以调用FreeRTOS 中的任务删除 API 函数接口显式地将其删除。FreeRTOS 内核中也允许创建相同优先级的任务。相同优先级的任务采用时间片轮转方
式进行调度(也就是通常说的分时调度器),时间片轮转调度仅在当前系统中无更高优先
级就绪任务存在的情况下才有效。

任务状态切换:
就绪(Ready),运行(Running),阻塞(Blocked),挂起态(Suspended):在这里插入图片描述
挂起态:任务可以通过调用 vTaskSuspend() API 函数都可以将处于任何状态的任务挂起,被挂起的任务得不到CPU 的使用权,也不会参与调度,除非它从挂起态中解除。把 一 个 挂 起 状态 的 任 务 恢复的 唯 一 途 径 就 是调 用 vTaskResume() 或 vTaskResumeFromISR() API 函数,如果此时被恢复任务的优先级高于正在运行任务的优先级,则会发生任务切换。
阻塞态:正在运行的任务发生阻塞(挂起、延时、读信号量等待)时,该任务会从就绪列表中删除,任务状态由运行态变成阻塞态,然后发生任务切换,运行就绪列表中当前最高优先级任务。
就绪态:创建任务自动进入就绪态

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值