启动条件:前提要把FreertosConfig.h的INCLUDE_vTaskDelay置 1
建议:只要是Freertos项目就要把死延时换成VtaskDelay延时,以免造成程序跑飞
void VtaskDelay(TickType_t xTicksToDelay)
单片机——stm32f103xx系列-----72MHZ。
仅是自己的观点,如有缺陷还请指教。
VtaskDelay函数称相对延时函数
主要功能
作用是让调用该函数的任务进入阻塞状态(blocked)、暂时让出 CPU 使用权,供其他任务执行。
至少等待指定个数的Tick Interrupt才能变为就绪状态
函数原型:
void vTaskDelay( TickType_t xTicksToDelay )
代码:xTicksToDelay 这个参数就是延迟的tick数量,tick 是 FreeRTOS 内部的时间单位,
主要来源于FreertosConfig.h里的系统节拍数,通常为1000。
#define configTICK_RATE_HZ 1000 /* 定义系统时钟节拍频率, 单位: Hz, 无默认需定义 */
内部代码:
其核心部分实际上是通过 vTaskPlaceOnEventList() 和 vTaskRemoveFromEventList() 来控制任务的挂起与唤醒。具体过程如下:
void vTaskDelay( const TickType_t xTicksToDelay )
{
BaseType_t xAlreadyYielded = pdFALSE; //已经执行过一次任务切换(任务的上下文切换)
//if (xTicksToDelay > 0) 就会进入函数
//typedef uint32_t TickType_t,TickType_t本质就是unsigned long int
//延时时间检查
if( xTicksToDelay > ( TickType_t ) 0U )
{
configASSERT( uxSchedulerSuspended == 0 ); //断言
vTaskSuspendAll();
//任务挂起,防止在修改任务状态(如将任务加入延迟队列)时发生上下文切换
{
traceTASK_DELAY();
prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE );
/* 这行代码将当前任务加入延迟队列,并设置延迟时间为 xTicksToDelay。
pdFALSE 表示该任务没有被强制唤醒。
该函数的主要功能是将当前任务挂起,直到指定的延迟时间到达:也就是绝对延时
然后他会根据优先级来选择下一个该是谁,如果说下一个任务里面也有vTaskDelay函数,也会这样子。任务会被添加到 延迟队列
*/
}
xAlreadyYielded = xTaskResumeAll(); //任务恢复 是否已经进行了上下文切换
}
else
{
mtCOVERAGE_TEST_MARKER();
}
if( xAlreadyYielded == pdFALSE )//没有发生上下文切换
{
//手动触发一次上下文切换来确保调度器能够根据新的任务状态来重新调度任务
portYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE );
说白了就是给在延时中的任务进入延时队列
如果没有发生上下文切换
则会进入portYIELD_WITHIN_API();函数
#define portYIELD()
{
/*
中断控制及状态寄存器ICSR 0xE000_ED04
#define portNVIC_INT_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000ed04 ) )
主要是获取PendSV的那一位,PendSV 中断是专门用于执行上下文切换的中断
#define portNVIC_PENDSVSET_BIT ( 1UL << 28UL )
*/
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
__dsb( portSY_FULL_READ_WRITE );
__isb( portSY_FULL_READ_WRITE );
}
紧接着,此时它就会进入PendSV中断函数xPortPendSVHandler
这一段就是很任务切换关键的代码了!
当任务切换的时候,先保存当前任务的栈指针(PSP),以及一些寄存器(r4-r11)的值,保证任务切换时不会丢失当前任务的上下文,调用 vTaskSwitchContext,它会决定下一个任务是谁,并更新任务控制块(TCB)。
切换到新的任务后,恢复新任务的栈指针和寄存器,使得新任务能够从它上次保存的地方继续执行
在任务切换时,保存当前任务的上下文(栈、寄存器等),并恢复 下一个任务的上下文使用。
basepri 寄存器确保任务切换期间不会被中断打扰。
__asm void xPortPendSVHandler( void )
{
extern uxCriticalNesting;
extern pxCurrentTCB;
extern vTaskSwitchContext;
PRESERVE8
mrs r0, psp
isb
ldr r3, =pxCurrentTCB /* Get the location of the current TCB. */
ldr r2, [r3]
stmdb r0!, {r4-r11} /* Save the remaining registers. */
str r0, [r2] /* Save the new top of stack into the first member of the TCB. */
stmdb sp!, {r3, r14}
mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY
msr basepri, r0
dsb
isb
bl vTaskSwitchContext
mov r0, #0
msr basepri, r0
ldmia sp!, {r3, r14}
ldr r1, [r3]
ldr r0, [r1] /* The first item in pxCurrentTCB is the task top of stack. */
ldmia r0!, {r4-r11} /* Pop the registers and the critical nesting count. */
msr psp, r0
isb
bx r14
nop
}
void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime,const TickType_t xTimeIncrement );
VtaskDelayUntil函数称绝对延时函数,等待到指定的绝对时刻,才能变为就绪态。
主要来源于FreertosConfig.h里的系统节拍数,通常为1000(1毫秒)使任务延迟到一个 指定的时间点,而不是简单地延迟固定的时间
使用xTaskDelayUntil(&Pre, n)时,前后两次退出xTaskDelayUntil的时间至少是n个Tick中断
-
pxPreviousWakeTime: 指向上次任务唤醒时间的指针。每次延迟后,都会更新这个时间。 -
xTimeIncrement: 延迟的时间增量,表示任务每次应该延迟的时间。
4594

被折叠的 条评论
为什么被折叠?



