什么是阻塞延时、为什么需要空闲任务
RTOS中的延时叫阻塞延时,即任务需要延时时,任务会放弃cpu使用权,cpu转而去做其他的事,当任务延时时间到后,任务重新请求获得cpu使用权。
但当所有的任务都处于阻塞后,为了不让cpu空闲没事干就需要一个空闲任务让cpu干活。
空闲任务的实现
空闲任务实现和创建普通任务没区别,空闲任务在调用vTaskStartScheduler
函数内部创建,如下
//定义空闲栈
#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 128 )
StackType_t IdleTaskStack[configMINIMAL_STACK_SIZE];
//空闲任务任务控制块
TCB_t IdleTaskTCB;
//设置空闲任务的参数
void vApplicationGetIdleTaskMemory( TCB_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer,
uint32_t *pulIdleTaskStackSize )
{
*ppxIdleTaskTCBBuffer=&IdleTaskTCB;
*ppxIdleTaskStackBuffer=IdleTaskStack;
*pulIdleTaskStackSize=configMINIMAL_STACK_SIZE;
}
void vTaskStartScheduler(void)
{
TCB_t *pxIdleTaskTCBBuffer = NULL;//空闲任务控制块指针
StackType_t *pxIdleTaskStackBuffer = NULL;//空闲任务栈指针
uint32_t ulIdleTaskStackSize; //空闲任务栈大小
//设置空闲任务参数
vApplicationGetIdleTaskMemory(&pxIdleTaskTCBBuffer,
&pxIdleTaskStackBuffer,
&ulIdleTaskStackSize);
//创建空闲任务
xIdleTaskHandle = xTaskCreateStatic((TaskFunction_t)prvIdleTask,
(char *)"IDLE",
(uint32_t)ulIdleTaskStackSize,
(void*)NULL,
(StackType_t*)pxIdleTaskStackBuffer,
(TCB_t*)pxIdleTaskTCBBuffer);
//将空闲任务添加到就绪列表
vListInsertEnd(&(pxReadyTasksLists[0]),&(((TCB_t *)pxIdleTaskTCBBuffer)->xStateListItem));
//手动指定第一个要运行的任务
pxCurrentTCB = &Task1TCB;
//启动调度器
if(xPortStartScheduler()!=pdFALSE)
{
//启动成功则不会运行到这里
}
}
阻塞延时的实现
阻塞延时需要用xTicksToDelay
,这个时TCB中的一个成员,用于记录还要阻塞多久。
typedef struct tskTaskControlBlock
{
volatile StackType_t * pxTopOfStack;
ListItem_t xStateListItem;
StackType_t * pxStack; ·
char pcTaskName[configMAX_TASK_NAME_LEN];
TickType_t xTicksToDelay; //用于延时
}tskTCB;
所以阻塞延时就是这样实现
void vTaskDelay(const TickType_t xTicksToDelay)
{
TCB_t *pxTCB = NULL;
pxTCB = pxCurrentTCB;
//设置延时时间
pxTCB->xTicksToDelay = xTicksToDelay;
//进行一次任务切换
taskYIELD();
}
由于引入了阻塞延时,所以任务切换函数需要改写,因为当所有任务阻塞后,需要切换至空闲任务运行
void vTaskSwitchContext( void )
{ //如果当前时空闲任务,尝试去执行任务1或任务2,如果他们延时时间都没到则继续执行空闲任务
if( pxCurrentTCB == &IdleTaskTCB )
{
if(Task1TCB.xTicksToDelay == 0)
{
pxCurrentTCB =&Task1TCB;
}
else if(Task2TCB.xTicksToDelay == 0)
{
pxCurrentTCB =&Task2TCB;
}
else
{
return;
}
}
else //当前任务不是空闲任务会执行到这里
{ //当前任务时任务1或任务2的话,检查另一个任务
//如果另外的任务不在延时中,会切换到该任务
//否则,判断当前任务是否在延时中,是则切换到空闲任务,
//否则,不进行任何切换
if (pxCurrentTCB == &Task1TCB)
{
if (Task2TCB.xTicksToDelay == 0)
{
pxCurrentTCB =&Task2TCB;
}
else if (pxCurrentTCB->xTicksToDelay != 0)
{
pxCurrentTCB = &IdleTaskTCB;
}
else
{
return;
}
}
else if (pxCurrentTCB == &Task2TCB)
{
if (Task1TCB.xTicksToDelay == 0)
{
pxCurrentTCB =&Task1TCB;
}
else if (pxCurrentTCB->xTicksToDelay != 0)
{
pxCurrentTCB = &IdleTaskTCB;
}
else
{
return;
}
}
}
}
xTicksToDelay 递减
vTaskDelay中设置了xTicksToDelay成员后,是通过SystTick中断来实现递减操作的
void xPortSysTickHandler( void )
{
int x = portSET_INTERRUPT_MASK_FROM_ISR();
xTaskIncrementTick();
portCLEAR_INTERRUPT_MASK_FROM_ISR(x);
}
void xTaskIncrementTick( void )
{
TCB_t *pxTCB = NULL;
BaseType_t i = 0;
const TickType_t xConstTickCount = xTickCount + 1;
xTickCount = xConstTickCount;
for (i=0; i<configMAX_PRIORITIES; i++)
{
pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &pxReadyTasksLists[i] ) );
if (pxTCB->xTicksToDelay > 0)
{
pxTCB->xTicksToDelay --; //这里递减
}
}
portYIELD();
}
SysTick初始化
//systick控制寄存器
#define portNVIC_SYSTICK_CTRL_REG (*((volatile uint32_t *) 0xe000e010 ))
//systick重装载寄存器
#define portNVIC_SYSTICK_LOAD_REG (*((volatile uint32_t *) 0xe000e014 ))
//systick时钟源选择
#ifndef configSYSTICK_CLOCK_HZ
#define configSYSTICK_CLOCK_HZ configCPU_CLOCK_HZ
#define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL )
#else
#define portNVIC_SYSTICK_CLK_BIT ( 0 )
#endif
#define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL )
#define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL )
void vPortSetupTimerInterrupt( void )
{
//重装载计数器值
portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;
//设置systick时钟使用内核时钟
//使能systick定时器中断
//使能systick定时器
portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT |
portNVIC_SYSTICK_INT_BIT |
portNVIC_SYSTICK_ENABLE_BIT );
}
在FreeRTOSConfig.h
中
#define configCPU_CLOCK_HZ (( unsigned long ) 25000000)
#define configTICK_RATE_HZ (( TickType_t ) 100)
configSYSTICK_CLOCK_HZ是没有定义的,所以configSYSTICK_CLOCK_HZ使用的是configCPU_CLOCK_HZ
仿真
portCHAR flag1;
portCHAR flag2;
TaskHandle_t Task1_Handle;
StackType_t Task1Stack[128];
TCB_t Task1TCB;
TaskHandle_t Task2_Handle;
StackType_t Task2Stack[128];
TCB_t Task2TCB;
void Task1_Fntry(void *arg)
{
while(1)
{
flag1=1;
vTaskDelay( 2 );
flag1=0;
vTaskDelay( 2 );
}
}
void Task2_Fntry(void *arg)
{
while(1)
{
flag2=1;
vTaskDelay( 2 );
flag2=0;
vTaskDelay( 2 );
}
}
int main(void)
{
prvInitialiseTaskLists();
Task1_Handle = xTaskCreateStatic(Task1_Fntry,"task1",128,NULL,Task1Stack,&Task1TCB);
vListInsertEnd(&pxReadyTasksLists[1],&((&Task1TCB)->xStateListItem));
Task2_Handle = xTaskCreateStatic(Task2_Fntry,"task2",128,NULL,Task2Stack,&Task2TCB);
vListInsertEnd(&pxReadyTasksLists[2],&((&Task2TCB)->xStateListItem));
vTaskStartScheduler();
for(;;)
{}
}
可以看到2个task是同步运行的,且延时是20ms
以上就是FreeRTOS实时操作系统空闲任务的阻塞延时实现的详细内容,更多关于FreeRTOS空闲任务阻塞延时的资料请关注编程网其它相关文章!