FreeRTOS系统知识点分析

一.FreeRTOS系统概念

        FreeRTOS是RTOS(实时操作系统)的其中一种,类似的还有uc/OS,RTX等,区别于linux & window系统;

.FreeRTOS的基本概念

        1.任务:被称为task,可看做linux系统中的线程,是一个简单的程序。每个任务被赋予一定的优先级,有它自己的一套CPU寄存器的值和自己的栈空间。典型的,每个任务都是一个无限循环,每个任务都处于以下五个状态下:运行态,就绪态,阻塞态(定时解除),挂起态(需被手动解除),被中断态

        2.任务切换:将当前正在运行任务的当前状态(CPU相应寄存器值的全部内容)保存在任务自己的栈区,然后把下一个要运行的任务的当前状态从该任务的栈中重新装入CPU寄存器,并开始下一个任务的运行,既上下文切换

        3.堆栈作用:可看做是一个临时的数据寄存、交换的内存区;方便用于保护和恢复调用现场;其中栈由上向下增长,既先定义的放在高地址,后定义的放在低地址;堆由下向上增长;

        4.内核:负者管理各个任务,为每个任务分配CPU的时间,并负者任务之间的通信;调度策略采用支持抢占式和时间片调度,既不同优先级的任务采用抢占式,同一优先级的任务采用时间片调度(允许一个任务运行一个时钟节拍后,滴答定时器中断周期,让出CPU的使用权,让拥有同优先级的下一个任务运行)。

         5. 优先级翻转:共享资源的分配导致优先级低的任务先运行,优先级高的后运行;解决办法采取优先级继承的方法来临时改变任务优先级。高优先级等待低优先级释放信号量,在等待的期间,将低优先级的任务优先级提高到与其相同的优先级,使其不会被中等的优先级抢占(适用于抢占式内核任务调度。)

          6.空闲任务:任务调度器开启后启动的第一个任务,优先级最低,使处理器始终都有任务可以运行,主要用来清除那些删除自己本身任务的task控制块和堆栈的内存空间,可以自定义空闲任务的钩子函数(类似于回调函数),比如使系统进入tickless模式,开启节电模式。

   PS:任何时刻都要保证系统中至少有一个任务在运行,因此绝不能在空闲任务钩子函数中调用任何可以阻塞空闲任务的API函数,比如vTaskDelay()函数,或者其他带有阻塞时间的信号量或队列等操作函数

        7.任务控制块TCB_t中栈顶指针和当前堆栈栈顶指针区别:指针pxStack指向堆栈的起始位置,任务创建时会分配指定数目的任务堆栈,申请堆栈内存函数返回的指针就被赋给该变量。很多刚接触FreeRTOS的人会分不清指针pxTopOfStack和pxStack的区别,这里简单说一下:pxTopOfStack指向当前堆栈栈顶,随着进栈出栈,pxTopOfStack指向的位置是会变化的;pxStack指向当前堆栈的起始位置,一经分配后,堆栈起始位置就固定了,不会被改变了。那么为什么需要pxStack变量呢,这是因为随着任务的运行,堆栈可能会溢出,在堆栈向下增长的系统中,这个变量可用于检查堆栈是否溢出;如果在堆栈向上增长的系统中,要想确定堆栈是否溢出,还需要另外一个变量pxEndOfStack来辅助诊断是否堆栈溢出。

注:指针pxTopOfStack必须位于任务控制块结构体的第一项,指向当前堆栈的栈顶,对于向下增长的堆栈,pxTopOfStack总是指向最后一个入栈的项目         

.FreeRTOS的中断配置临界区

        1.Cortex-M内核提供了一个用于中断管理的嵌套向量中断控制器(NVIC),实际上是多个用于管理中断和异常的可编程寄存器所定义的结构体。

        2.使用中断优先级配置寄存器来决定抢占式优先级和响应式优先级的数量;

                    抢占式优先级中断,顾名思义就是一个中断正在运行,抢占式优先级高的会打断抢占式优先级低的中断运行,既中断嵌套。

                    响应式优先级中断,在抢占式相同的情况下,中断同时发生,响应优先级高的;或者一个中断正在ing中,等待该中断处理完才进行下一个;

                    注:抢占式优先级相同的中断之间,没有中断嵌套的概念。

                    #define NVIC_PRIORITYGROUP   4    那STM32的4位优先级就全是抢占优先级

        中断优先级是数值越小,优先级越高;其中PendSV中断和SysTick中断的优先级最低;宏configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIOTITY用来设置FreeRTOS可管理的最大优先级。       

3.中断优先级寄存器用于配置优先级数

        4.临界区指必须完整运行,不能被打断的代码段;任务级临界代码保护,进入API函数:taskENTER_CRITICAL( ),退出API函数taskEXIT_CRIEICAL( );   中断级的临界代码保护API函数。。。。。

        1.进入和退出API函数必须同时成对出现;所谓的进出临界区是通过开关中断实现,将宏configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIOTITY写入到BASEPRI寄存器中来开关系统可管理的中断;

                  2.挂起调度器vTaskSuspendAll( )进入临界区,只能保护代码不被其他任务打断,但中断可以;因为任务切换(上下文切换)只能在PendSV中断中进行,因此调用taskENTER_CRIEICAL的代码不会被任务和中断打断。

                  3.中断响应时间:关中断的最长时间 + 保护CPU内部寄存器的时间 + 进入中断服务函数的执行时间 + 开始执行中断服务程序(ISR)的第一条指令时间(从中断信号发生,CPU完成当前现场保护,而进入此中断信号对应处理程序入口出的时刻,所经历的时间)  


    注:任务优先级和中断优先级是两个不同的概念;任务优先级数值越大,优先级越高;中断优先级是数值越小,优先级越高;最低的中断(系统滴答定时器中断SysTick)可以打断任何任务。

.FreerRTOS任务切换

        FreeRTOS是通过PendSV异常来处理上下文切换;切换发生的场合:1.执行了一个系统调用(调用了任务切换函数,taskYIELD( ),或者调用了该函数的函数);2.系统滴答定时器(SysTick)中断,中断服务函数中,增加时钟计数器xTickCount的值成功后,向ICSR的bit28写入1挂起PendSV来启动PendSV中断,并在PendSV中断服务函数中进行任务切换。也就是说,每次SysTick滴答定时器都会触发任务切换,有优先级高的就切换或者切换到同一优先级的其它任务,使其运行一个时间片(滴答定时器的定时周期)

.时间管理

    task中调用延时函数,任务进入阻塞态,并进行任务切换,直到延时完成,任务重新进入进入就绪态。

            1.函数vTaskDelay( const TickType_t  xTicksToDelay)  //参数为要延时的时间节拍数,既滴答定时的心跳次数;延时为相对模式,从该延迟函数调用出开始计数;

            2.函数vTaskDelayUntil( const TickType_t  xTicksToDelay ),延时为绝对时间,从任务开始唤醒时的xCountTick计数,此函数能使任务按一定的频率运行。

六.任务与任务、任务与中断之间的通信

            1.队列:主要用于实现信息的传递,主要是值传递。

            2.信号量:二值信号量、计数型信号量、互斥信号量(一定程度上解决优先级翻转的问题),递归信号量(信号量时基于队列实现,队列长度为1,队列项为0),主要用于资源的资源管理和任务同步。

            3.事件标志组:主要用于单个任务与多个任务或中断进行同步,为一对多的关系(信号量只能用于一对一)

            4.任务通知:可以用来取代消息队列,信号量,事件标志组等这些信息,是一个强大的功能。




猜你喜欢

转载自blog.csdn.net/caozhigang129/article/details/79851278