什麼是阻塞延時、爲什麼需要空閒任務
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