在充分了解FreeRTOS工作的内核原理之后,学习FreeRTOS的原理会更加轻松,接下来就开始FreeRTOS各种功能的学习。
那么在这一章节,我们开始学习任务管理。
任务管理
FreeRTOS 的任务可认为是一系列独立任务的集合。每个任务在自己的环 境中运行。在任何时刻,只有一个任务得到运行,FreeRTOS调度器决定运行哪个任务。调 度器会不断的启动、停止每一个任务,宏观看上去所有的任务都在同时在执行。作为任务, 不需要对调度器的活动有所了解,在任务切入切出时保存上下文环境(寄存器值、堆栈内 容)是调度器主要的职责。为了实现这点,每个 FreeRTOS任务都需要有自己的栈空间。 当任务切出时,它的执行环境会被保存在该任务的栈空间中,这样当任务再次运行时,就 能从堆栈中正确的恢复上次的运行环境,任务越多,需要的堆栈空间就越大,而一个系统 能运行多少个任务,取决于系统的可用的SRAM。
FreeRTOS中的任务是抢占式调度机制,高优先级的任务可打断低优先级任务,低优先 级任务必须在高优先级任务阻塞或结束后才能得到调度。同时 FreeRTOS 也支持时间片轮 转调度方式,只不过时间片的调度是不允许抢占任务的CPU使用权。
任务通常会运行在一个死循环中,也不会退出,如果一个任务不再需要,可以调用 FreeRTOS中的任务删除API函数接口显式地将其删除。
不能在中断里面去挂起某一任务,但是可以在中断里面回复某一任务,原因是没有中断保护版本的任务挂起函数,但是却有中断保护版本的任务恢复函数。
如果在中断中去挂起任务会报错,Error:..\..\FreeRTOS\port\RVDS\ARM_CM4F\port.c,441
任务调度
FreeRTOS 的任务调度是基于时间片轮转算法实现的。每个任务被分配一个时间片,在时间片结束前,任务会被挂起,CPU 开始调度下一个任务。当所有任务的时间片都耗尽后,系统会重新从最高优先级的任务开始调度。
在 FreeRTOS 中,任务调度的具体实现是通过调用内核中的 vTaskSwitchContext() 函数来实现的。该函数会挂起当前正在执行的任务,并找到一个最高优先级的就绪任务来执行。
需要注意的是,在 FreeRTOS 中,任务调度的开销是比较小的,因此任务切换的频率可以适当加快,以提高系统的响应速度。但是,过多的任务切换也会增加系统的开销,影响系统的性能。因此,在设计系统时,需要权衡任务切换频率和系统性能之间的关系。
当发生任务切换时,当前任务的执行状态(寄存器值、堆栈信息等)需要保存起来,以便稍后能够恢复执行。在上下文切换之前,FreeRTOS 会将当前任务的执行状态保存在其任务控制块(TCB)中。
在任务切换时,FreeRTOS 调度器会选择下一个要执行的任务。该选择是基于任务的优先级和调度策略进行的。一旦选择了下一个任务,FreeRTOS 将从其任务控制块中恢复该任务的执行状态。
一旦下一个任务被选择,FreeRTOS 会从其任务控制块中获取保存的执行状态,然后将其恢复到相应的 CPU 寄存器和堆栈中。这一过程也通常在汇编代码中完成。
最后,FreeRTOS 会通过汇编代码实现将 CPU 控制权转交给下一个任务,使其继续执行。
在 Cortex-M3 架构中,FreeRTOS 为了任务启动和任务切换使用了三个异常:SVC、 PendSV和SysTick:
SVC(系统服务调用,亦简称系统调用)用于任务启动,有些操作系统不允许应用程 序直接访问硬件,而是通过提供一些系统服务函数,用户程序使用 SVC 发出对系统服务函 数的呼叫请求,以这种方法调用它们来间接访问硬件,它就会产生一个 SVC 异常。
PendSV(可挂起系统调用)用于完成任务切换,它是可以像普通的中断一样被挂起的, 它的最大特性是如果当前有优先级比它高的中断在运行,PendSV会延迟执行,直到高优先 级中断执行完毕,这样子产生的PendSV中断就不会打断其他中断的运行。
SysTick 用于产生系统节拍时钟,提供一个时间片,如果多个任务共享同一个优先级, 则每次SysTick中断,下一个任务将获得一个时间片。关于详细的SVC、PendSV异常描述, 推荐《Cortex-M3权威指南》一书的“异常”部分。 这里将PendSV和 SysTick 异常优先级设置为最低,这样任务切换不会打断某个中 断服务程序,中断服务程序也不会被延迟,这样简化了设计,有利于系统稳定。有人可能 会问,那 SysTick 的优先级配置为最低,那延迟的话系统时间会不会有偏差?答案是不会 的,因为SysTick只是当次响应中断被延迟了,而SysTick是硬件定时器,它一直在计时, 这一次的溢出产生中断与下一次的溢出产生中断的时间间隔是一样的,至于系统是否响应 还是延迟响应,这个与SysTick无关,它照样在计时。
抢占式调度
抢占式调度主要时针对优先级不同的任务,每个任务都有一个优先级,优先级高的任务可 以抢占优先级低的任务,只有当优先级高的任务发生阻塞或者被挂起,低优先级的任务才可以 运行。
时间片调度
时间片调度主要针对优先级相同的任务,当多个任务的优先级相同时,任务调度器会在每 一次系统时钟节拍到的时候切换任务,也就是说 CPU 轮流运行优先级相同的任务,每个任务运 行的时间就是一个系统时钟节拍。
任务状态的改变
- 创建任务→就绪态(Ready):任务创建完成后进入就绪态,表明任务已 准备就绪,随时可以运行,只等待调度器进行调度。
- 就绪态→运行态(Running):发生任务切换时,就绪列表中最高优先级 的任务被执行,从而进入运行态。
- 运行态→就绪态:有更高优先级任务创建或者恢复后,会发生任务调度, 此刻就绪列表中最高优先级任务变为运行态,那么原先运行的任务由运行态变为就绪态, 依然在就绪列表中,等待最高优先级的任务运行完毕继续运行原来的任务(此处可以看做 是CPU使用权被更高优先级的任务抢占了)。
- 运行态→阻塞态(Blocked):正在运行的任务发生阻塞(挂起、延时、 读信号量等待)时,该任务会从就绪列表中删除,任务状态由运行态变成阻塞态,然后发 生任务切换,运行就绪列表中当前最高优先级任务。
- 阻塞态→就绪态:阻塞的任务被恢复后(任务恢复、延时时间超时、读 信号量超时或读到信号量等),此时被恢复的任务会被加入就绪列表,从而由阻塞态变成 就绪态;如果此时被恢复任务的优先级高于正在运行任务的优先级,则会发生任务切换, 将该任务将再次转换任务状态,由就绪态变成运行态。
- 就绪态、阻塞态、运行态→挂起态(Suspended):任务可以通 过调用 vTaskSuspend() API 函数都可以将处于任何状态的任务挂起,被挂起的任务得不到 CPU的使用权,也不会参与调度,除非它从挂起态中解除。
- 挂起态→就绪态:把 一 个 挂 起 状态 的 任 务 恢复的 唯 一 途 径 就 是 调 用 vTaskResume() 或vTaskResumeFromISR() API 函数,如果此时被恢复任务的优先级高 于正在运行任务的优先级,则会发生任务切换,将该任务将再次转换任务状态,由就绪态 变成运行态。
任务状态的概念
FreeRTOS系统中的每一任务都有多种运行状态。系统初始化完成后,创建的任务就可 以在系统中竞争一定的资源,由内核进行调度。 任务状态通常分为以下四种:
1.就绪(Ready):
该任务在就绪列表中,就绪的任务已经具备执行的能力,只等 待调度器进行调度,新创建的任务会初始化为就绪态。
2.运行(Running):
该状态表明任务正在执行,此时它占用处理器,FreeRTOS 调 度器选择运行的永远是处于最高优先级的就绪态任务,当任务被运行的一刻,它 的任务状态就变成了运行态。
如果一个任务得到 CPU 的使用权,即任务被实际执行时,那么这个任务处于运行态。如果 运行 RTOS 的 MCU 只有一个处理器核心,那么在任何时刻,都只能有一个任务处于运行态。
3. 阻塞(Blocked):
如果任务当前正在等待某个时序或外部中断,我们就说这个任 务处于阻塞状态,该任务不在就绪列表中。包含任务被挂起、任务被延时、任务 正在等待信号量、读写队列或者等待读写事件等。
4. 挂起态(Suspended):
处于挂起态的任务对调度器而言是不可见的,让一个任务进 入挂起状态的唯一办法就是调用 vTaskSuspend()函数;而 把 一 个 挂 起 状态 的任 务 恢复的 唯 一 途 径 就 是 调 用 vTaskResume() 或vTaskResumeFromISR()函 数,我们可以这么理解挂起态与阻塞态的区别,当任务有较长的时间不允许运行 的时候,我们可以挂起任务,这样子调度器就不会管这个任务的任何信息,直到 我们调用恢复任务的API函数;而任务处于阻塞态的时候,系统还需要判断阻塞 态的任务是否超时,是否可以解除阻塞。
常用函数
一.任务创建函数
BaseType_t xTaskCreate(TaskFunction_t pxTaskCode,
constchar * const pcName,
const configSTACK_DEPTH_TYPE usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask);
参数:
pxTaskCode: 指向任务函数的指针。
pcName: 任务名称的字符串。
usStackDepth: 任务堆栈的大小,以字节为单位。
pvParameters: 传递给任务函数的参数指针。
uxPriority: 任务的优先级,数值越大表示优先级越高。
pxCreatedTask: 任务句柄的指针,用于返回创建的任务句柄。
二.任务挂起函数
1.vTaskSuspend ( TaskHandle_t xTaskToSuspend )
可以在创建任务时就调用此函数将任务设置为挂起态。可以在其他任务中恢复任务之后延时一段时间继续挂起任务,达到控制任务执行时间的效果。
参数:xTaskToSuspend 是挂起指定任务的任务句柄,任务必须为已创建 的任务,可以通过传递NULL来挂起任务自己。
要求:如果想要使用任务挂起函数vTaskSuspend()则必须将宏定义 INCLUDE_vTaskSuspend配置为1。
功能:vTaskSuspend() 函数可以将指定任务暂停,任务状态变为 eSuspended。被暂停的任务将不会被调度器调度执行,但是该任务所占用的系统资源(如栈、堆内存等)仍然保留,任务的状态、优先级等信息也不会被修改。该函数需要传入被暂停任务的任务句柄。被挂起的任务绝不会得到CPU的使用权,不管该任务具有什么优先级。 任务可以通过调用vTaskSuspend()函数都可以将处于任何状态的任务挂起,被挂起的 任务得不到CPU的使用权,也不会参与调度,它相对于调度器而言是不可见的,除非它从挂起态中解除。
2. vTaskSuspendAll ( void )
将所有的任务都挂起,调度器恢复可 以调用 xTaskResumeAll()函数,调用了多少次的 vTaskSuspendAll()就要调用多少次 xTaskResumeAll()进行恢复。
三.任务恢复函数
任务恢复就是让挂 起的任务重新进入就绪状态,恢复的任务会保留挂起前的状态信息,在恢复的时候根据挂 起时的状态继续运行。如果被恢复任务在所有就绪态任务中,处于最高优先级列表的第一 位,那么系统将进行任务上下文的切换。
1.vTaskResume ( TaskHandle_t xTaskToResume )
参数:xTaskToResume是恢复指定任务的任务句柄。
要求:如果想要使用任务恢复函数 vTaskResume()则必须将宏定义 INCLUDE_vTaskSuspend 配置为 1,因为任务挂起只能通过调用vTaskSuspend()函数进行挂 起,没挂起的任务就无需恢复,当然需要调用 vTaskSuspend()函数就必须使能 INCLUDE_vTaskSuspend 这个宏定义,所以想要使用 FreeRTOS 的任务挂起与恢复函数就 必须将这个宏定义配置为1。
功能:vTaskResume() 函数可以将指定任务恢复运行,任务状态变为 eReady。被恢复的任务会重新被调度器调度执行,但是如果其他任务的优先级更高,调度器仍然会先执行优先级更高的任务。该函数需要传入被恢复任务的任务句柄。无论任务在挂起时候调用过多少次这个vTaskSuspend()函数,也只需调用一次 vTaskResume ()函数即可将任务恢复运行,当然,无论调用多少次的 vTaskResume()函数,也只在任务是挂起态的时候才进行恢复。
2. xTaskResumeFromISR( TaskHandle_t xTaskToResume )
参数:xTaskToResume是恢复指定任务的任务句柄。
要求:要想使用该函数必须在FreeRTOSConfig.h 中把INCLUDE_vTaskSuspend 和 INCLUDE_vTaskResumeFromISR 都定义为 1 才有效。任务还没有处于挂起态的时候,调用 xTaskResumeFromISR()函数是没有任何意义的。
功能:xTaskResumeFromISR()与 vTaskResume()一样都是用于恢复被挂起的任务,不一样的 是 xTaskResumeFromISR()专门用在中断服务程序中。
无论通过调用一次或多次 vTaskSuspend()函数而被挂起的任务,也只需调用一次 xTaskResumeFromISR()函数即可解 挂。
3. xTaskResumeAll()
功能:当调用了vTaskSuspendAll()函数将调度 器挂起,想要恢复调度器的时候我们就需要调用xTaskResumeAll()函数
四.任务删除函数
vTaskDelete( TaskHandle_t xTaskToDelete )
参数:利用任务句柄xTaskToDelete来获取任务控制块,通过调用 prvGetTCBFromHandle()函数得到对应的任务控制块。如果如果xTaskToDelete为NULL则 会删除任务自身。
功能:vTaskDelete()用于删除一个任务。当一个任务删除另外一个任务时,形参为要删除任 务创建时返回的任务句柄,如果是删除自身, 则形参为 NULL。 要想使用该函数必须在 FreeRTOSConfig.h 中把 INCLUDE_vTaskDelete 定义为 1,删除的任务将从所有就绪,阻塞, 挂起和事件列表中删除
五.任务延时函数
vTaskDelay( const TickType_t xTicksToDelay )
参数:xTicksToWait表示要延时多长时间,单位为系统节拍周期。
要求:要想使用FreeRTOS中的vTaskDelay() 函数必须在 FreeRTOSConfig.h 中把 INCLUDE_vTaskDelay 定义为 1 来使能。
功能:vTaskDelay()用于阻塞延时,调用该函数后,任务将进入阻塞状态,进入阻塞态的任务 将让出 CPU资源。延时的时长由形参 xTicksToDelay决定,单位为系统节拍周期, 比如系 统的时钟节拍周期为1ms,那么调用 vTaskDelay(1)的延时时间则为 1ms。
在FreeRTOS中,除了相对延时函数,还有绝对延时函数vTaskDelayUntil(),这个绝 对延时常用于较精确的周期运行任务,比如我有一个任务,希望它以固定频率定期执行, 而不受外部的影响,任务从上一次运行开始到下一次运行开始的时间间隔是绝对的,而不是相对的。
vTaskDelayUntil( TickType_t * const pxPreviousWakeTime ,
const TickType_t xTimeIncrement )
参数:TickType_t * const pxPreviousWakeTime:指针,指向一个变量,该变量保存任务最后一次解除阻塞的的时 刻。第一次使用时,该变量必须初始化为当前时间,之后这个变量会在 vTaskDelayUntil() 函数内自动更新。
//正如文中所说,先让一个变量获取一次系统时间,之后把这个变量放在函数中作为第一个参数
u32 lastWakeTime = getSysTickCnt();
while(1)
{
vTaskDelayUntil(&lastWakeTime, F2T(RATE_10_HZ));//This task runs at 10Hz //此任务以10Hz的频率运行
}
//获取系统当前时间函数
/********************************************************
*getSysTickCnt()
*调度开启之前 返回 sysTickCnt
*调度开启之前 返回 xTaskGetTickCount()
*********************************************************/
u32 getSysTickCnt(void)
{
if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED) /*系统已经运行*/
return xTaskGetTickCount();
else
return sysTickCnt;
}
const TickType_t xTimeIncrement :周期循环时间。当时间等于(*pxPreviousWakeTime + xTimeIncrement)时,任务解除阻塞。如果不改变参数 xTimeIncrement 的值,调用该函数的 任务会按照固定频率执行。
要求:要想使用该函数必须在FreeRTOSConfig.h中把INCLUDE_vTaskDelayUntil定义为1来 使能。
功能:任务会先调用vTaskDelayUntil()使任务进入阻塞态,等到时间到了就从阻塞中解除, 然后执行主体代码,任务主体代码执行完毕。会继续调用vTaskDelayUntil()使任务进入阻 塞态,然后就是循环这样子执行。即使任务在执行过程中发生中断,那么也不会影响这个 任务的运行周期,仅仅是缩短了阻塞的时间而已,到了要唤醒的时间依旧会将任务唤醒。
vTaskDelayUntil()与vTaskDelay ()一样都是用来实现任务的周期性延时。但 vTaskDelay ()的延时是相对的,是不确定的,它的延时是等vTaskDelay ()调用完毕后开始 计算的。并且vTaskDelay ()延时的时间到了之后,如果有高优先级的任务或者中断正在执 行,被延时阻塞的任务并不会马上解除阻塞,所有每次执行任务的周期并不完全确定。而 vTaskDelayUntil()延时是绝对的,适用于周期性执行的任务。当(*pxPreviousWakeTime + xTimeIncrement)时间到达后,vTaskDelayUntil()函数立刻返回,如果任务是最高优先级的, 那么任务会立马解除阻塞,所以说vTaskDelayUntil()函数的延时是绝对性的,
FreeRTOS中程序运行的上下文包括: 中断服务函数。普通任务。 空闲任务。
1. 中断服务函数:
中断服务函数是一种需要特别注意的上下文环境,它运行在非任务的执行环境下(一 般为芯片的一种特殊运行模式(也被称作特权模式)),在这个上下文环境中不能使用挂 起当前任务的操作,不允许调用任何会阻塞运行的API函数接口。另外需要注意的是,中 断服务程序最好保持精简短小,快进快出,一般在中断服务函数中只做标记事件的发生, 然后通知任务,让对应任务去执行相关处理,因为中断服务函数的优先级高于任何优先级 的任务,如果中断处理时间过长,将会导致整个系统的任务无法正常运行。所以在设计的 时候必须考虑中断的频率、中断的处理时间等重要因素,以便配合对应中断处理任务的工 作。
2. 任务:
任务看似没有什么限制程序执行的因素,似乎所有的操作都可以执行。但是做为一个 优先级明确的实时系统,如果一个任务中的程序出现了死循环操作(此处的死循环是指没 有阻塞机制的任务循环体),那么比这个任务优先级低的任务都将无法执行,当然也包括 了空闲任务,因为死循环的时候,任务不会主动让出CPU,低优先级的任务是不可能得到CPU的使用权的,而高优先级的任务就可以抢占 CPU。这个情况在实时操作系统中是必须 注意的一点,所以在任务中不允许出现死循环。如果一个任务只有就绪态而无阻塞态,势 必会影响到其他低优先级任务的执行,所以在进行任务设计时,就应该保证任务在不活跃 的时候,任务可以进入阻塞态以交出CPU使用权,这就需要我们自己明确知道什么情况下 让任务进入阻塞态,保证低优先级任务可以正常运行。在实际设计中,一般会将紧急的处 理事件的任务优先级设置得高一些。
3. 空闲任务:
空闲任务(idle任务)是FreeRTOS系统中没有其他工作进行时自动进入的系统任务。 因为处理器总是需要代码来执行——所以至少要有一个任务处于运行态。FreeRTOS为了保证这一点,当调用 vTaskStartScheduler()时,调度器会自动创建一个空闲任务,空闲任务是 一个非常短小的循环。用户可以通过空闲任务钩子方式,在空闲任务上钩入自己的功能函 数。通常这个空闲任务钩子能够完成一些额外的特殊功能,例如系统运行状态的指示,系 统省电模式等。除了空闲任务钩子,FreeRTOS系统还把空闲任务用于一些其他的功能,比 如当系统删除一个任务或一个动态任务运行结束时,在执行删除任务的时候,并不会释放 任务的内存空间,只会将任务添加到结束列表中,真正的系统资源回收工作在空闲任务完 成,空闲任务是唯一一个不允许出现阻塞情况的任务,因为 FreeRTOS需要保证系统永远 都有一个可运行的任务。
对于空闲任务钩子上挂接的空闲钩子函数,它应该满足以下的条件: 1.永远不会挂起空闲任务; 任务的执行时间: 2.不应该陷入死循环,需要留出部分时间用于系统处理系统资源回收。
4. 任务的执行时间一般是指两个方面,一是任务从开始到结束的时间,二是任务的周期。
在系统设计的时候这两个时间候我们都需要考虑,例如,对于事件 A对应的服务任务 Ta,系统要求的实时响应指标是10ms,而Ta的最大运行时间是1ms,那么10ms就是任务 Ta的周期了,1ms则是任务的运行时间,简单来说任务Ta在10ms内完成对事件A的响应 即可。此时,系统中还存在着以 50ms为周期的另一任务 Tb,它每次运行的最大时间长度 是 100us。在这种情况下,即使把任务 Tb的优先级抬到比 Ta更高的位置,对系统的实时 性指标也没什么影响,因为即使在Ta的运行过程中,Tb抢占了Ta的资源,等到Tb执行 完毕,消耗的时间也只不过是 100us,还是在事件 A规定的响应时间内(10ms),Ta能够安 全完成对事件A的响应。但是假如系统中还存在任务Tc,其运行时间为20ms,假如将Tc 的优先级设置比Ta更高,那么在Ta运行的时候,突然间被Tc打断,等到Tc执行完毕, 那 Ta已经错过对事件 A(10ms)的响应了,这是不允许的。所以在我们设计的时候,必 须考虑任务的时间,一般来说处理时间更短的任务优先级应设置更高一些。
相关宏定义
1. configUSE_PREEMPTION 此宏用于设置系统的调度方式。当宏 configUSE_PREEMPTION 设置为 1 时,系统使用抢 占式调度;当宏 configUSE_PREEMPTION 设置为 0 时,系统使用协程式调度。抢占式调度和 协程式调度的区别在于,协程式调度是正在运行的任务主动释放 CPU 后才能切换到下一个任 务,任务切换的时机完全取决于正在运行的任务。协程式的优点在于可以节省开销,但是功能 比较有限,现在的 MCU 性能都比较强大,建议使用抢占式调度。
2. configUSE_PORT_OPTIMISED_TASK_SELECTION FreeRTOS 支持两种方法来选择下一个要执行的任务,分别为通用方法和特殊方法。 当宏configUSE_PORT_OPTIMISED_TASK_SELECTION 设置为 0 时,使用通用方法。通 用方法是完全使用 C 实现的软件算法,因此支持所用硬件,并且不限制任务优先级的最大值, 但效率相较于特殊方法低。 当宏 configUSE_PORT_OPTIMISED_TASK_SELECTION 设置为 1 时,使用特殊方法。特 殊方法的效率相较于通用方法高,但是特殊方法依赖于一个或多个特定架构的汇编指令(一般 是类似计算前导零[CLZ]的指令),因此特殊方法并不支持所有硬件,并且对任务优先级的最大 值一般也有限制,通常为 32。
3. configUSE_TICKLESS_IDLE 当宏 configUSE_TICKLESS_IDLE 设置为 1 时,使能 tickless 低功耗模式;设置为 0 时, tick 中断则会移植运行。tickless 低功耗模式并不适用于所有硬件。
4. configCPU_CLOCK_HZ 此宏应设置为 CPU 的内核时钟频率,单位为 Hz。
5. configSYSTICK_CLOCK_HZ 此宏应设置为 SysTick 的时钟频率,当 SysTick 的时钟源频率与内核时钟频率不同时才可 以定义,单位为 Hz。
6. configTICK_RATE_HZ 此宏用于设置 FreeRTOS 系统节拍的中断频率,单位为 Hz。
7. configMAX_PRIORITIES 此宏用于定义系统支持的最大任务优先级 数 量 , 最 大 任 务 优 先 级 数 值 为 configMAX_PRIORITIES-1。
8. configMINIMAL_STACK_SIZE 此宏用于设置空闲任务的栈空间大小,单位为 word。
9. configMAX_TASK_NAME_LEN 此宏用于设置任务名的最大字符数。
10. configUSE_16_BIT_TICKS 此宏用于定义系统节拍计数器的数据类型,当宏 configUSE_16_BIT_TICKS 设置为 1 时, 系统节拍计数器的数据类型为 16 位无符号整形;当宏 configUSE_16_BIT_TICKS 设置为 0 时, 系统节拍计数器的数据类型为 32 为无符号整型。
11. configIDLE_SHOULD_YIELD 当宏 configIDLE_SHOULD_YIELD 设置为 1 时,在抢占调度下,同等优先级的任务可抢占 空闲任务,并延用空闲任务剩余的时间片。
写到这里的时间是2024的5月20号晚上十点二十一,md,没有对象陪,呜呜呜。
我目前打算的路线是走嵌入式软件方向,后面写完这篇文章就去弄linux和补充32和C语言的相关知识点。昨天和恩师去的那个机器人比赛还进国赛了我去,还没想好实物的技术方案,也不知道省赛的奖状什么时候下来。再看吧。