亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

FreeRTOS實(shí)時(shí)操作系統(tǒng)的任務(wù)創(chuàng)建與任務(wù)切換

 更新時(shí)間:2022年04月07日 10:44:49   作者:jiang_2018  
這篇文章主要為大家介紹了FreeRTOS實(shí)時(shí)操作系統(tǒng)的任務(wù)創(chuàng)建與任務(wù)切換,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪

 

 

任務(wù)控制塊數(shù)據(jù)結(jié)構(gòu)

任務(wù)控制塊數(shù)據(jù)結(jié)構(gòu)在task.c聲明

typedef struct tskTaskControlBlock
{
	volatile StackType_t * pxTopOfStack; //棧頂指針
	ListItem_t xStateListItem; //任務(wù)節(jié)點(diǎn)
	StackType_t * pxStack; //任務(wù)棧起始地址
	char pcTaskName[configMAX_TASK_NAME_LEN];//任務(wù)名稱
}tskTCB;

任務(wù)創(chuàng)建函數(shù)

下面是用靜態(tài)方式創(chuàng)建任務(wù)的函數(shù)

static void prvInitialiseNewTask(TaskFunction_t pxTaskCode,/*任務(wù)函數(shù)*/
                           const char * const pcName,/*任務(wù)名稱*/
						   const uint32_t ulStackDepth,/*任務(wù)棧大小,單位字*/
						   void * const pvParameters, /*任務(wù)形參*/
						   TaskHandle_t* const pxCreatedTask,/*任務(wù)句柄*/
						   TCB_t * pxNewTCB) /*任務(wù)控制塊指針*/
{
	StackType_t * pxTopOfStack;
	UBaseType_t x;
	//棧頂?shù)刂?
	pxTopOfStack = pxNewTCB->pxStack + (ulStackDepth - (uint32_t) 1);
	//項(xiàng)下做8字節(jié)對(duì)齊
	pxTopOfStack = (StackType_t*) (  (uint32_t)pxTopOfStack & (~(uint32_t)0x0007) );
	//存儲(chǔ)名字
	for(x=(UBaseType_t)0;x<(UBaseType_t)configMAX_TASK_NAME_LEN;x++)
	{
		pxNewTCB->pcTaskName[x] = pcName[x];
		if(pcName[x]=='\0')
			break;
	}
	pxNewTCB->pcTaskName[configMAX_TASK_NAME_LEN-1] = '\0';
	//初始化TCB中的xStateListItem列表項(xiàng)
	vListInitialiseItem(& (pxNewTCB->xStateListItem));
	//設(shè)置xStateListItem列表項(xiàng)的擁有者即為傳入的TCB
	listSET_LIST_ITEM_OWNER( &(pxNewTCB->xStateListItem), pxNewTCB );
	//初始化任務(wù)棧,并返回更新的棧頂指針
	pxNewTCB->pxTopOfStack = pxPortInitialiseStack(pxTopOfStack,pxTaskCode,pvParameters);
	//返回任務(wù)句柄
	if((void*)pxCreatedTask !=NULL )
	{
			*pxCreatedTask = (TaskHandle_t) pxNewTCB;
	}
}

下面看pxPortInitialiseStack是怎么初始化任務(wù)棧的
因?yàn)槭窍蛳律L(zhǎng)的滿棧,所以是--操作

#define portINITIAL_XPSR (0x01000000)
#define portSTART_ADDRESS_MASK ((StackType_t)0xfffffffeUL)
StackType_t * pxPortInitialiseStack(StackType_t* pxTopOfStack, TaskFunction_t pxCode, void*pvParameters)
{
    //-------------設(shè)置內(nèi)核會(huì)自動(dòng)加載的寄存器,且順序不能變
	pxTopOfStack--;
	*pxTopOfStack = portINITIAL_XPSR;//xPSR bit24
	pxTopOfStack--;
	*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK;//R15 PC
	pxTopOfStack--;
	*pxTopOfStack = ( StackType_t ) prvTaskExitError;//R14 LR
	pxTopOfStack -= 5; //R12 R3 R2 R1 
	*pxTopOfStack = ( StackType_t ) pvParameters;//R0
	
	//--------------下面8個(gè)需要手動(dòng)保存
	pxTopOfStack-=8;
	//返回更新后的棧頂指針
	return pxTopOfStack;
}

如下圖

在這里插入圖片描述

定義就緒表

task.c中,就緒表就是列表類型的數(shù)組,元素個(gè)數(shù)是configMAX_PRIORITIES,就緒表把同一優(yōu)先級(jí)的任務(wù)插入到同一優(yōu)先級(jí)的鏈表中,數(shù)組下標(biāo)表示優(yōu)先級(jí),如下圖

#define configMAX_PRIORITIES 5
List_t pxReadyTasksLists[configMAX_PRIORITIES];

就緒表初始化

就緒表初始化就是把數(shù)組每個(gè)元素(即列表)初始化(即調(diào)用vListInitialise),結(jié)果如下

void prvInitialiseTaskLists(void)
{
	UBaseType_t uxPriority;
	for(uxPriority = (UBaseType_t)0 ;
	    uxPriority < (UBaseType_t) configMAX_PRIORITIES;
	    uxPriority ++)
	{
			vListInitialise(&(pxReadyTasksLists[uxPriority]));
	}
}

在這里插入圖片描述

啟動(dòng)調(diào)度器

調(diào)用關(guān)系,這里手動(dòng)指定第一個(gè)運(yùn)行的任務(wù)

vTaskStartScheduler->xPortStartScheduler->prvStartFirstTask

void vTaskStartScheduler(void)
{
	//手動(dòng)指定一個(gè)和要運(yùn)行的任務(wù)
	pxCurrentTCB = &Task1TCB;
	//啟動(dòng)調(diào)度器
	if(xPortStartScheduler()!=pdFALSE)
	{
		//啟動(dòng)成功不會(huì)跑到這里
	}
}
//stm32 用4bit表示優(yōu)先級(jí),所以最低優(yōu)先級(jí)則是15
#define configKERNEL_INTERRUPT_PRIORITY 15 
#define portNVIC_SYSPRI2_REG (*((volatile uint32_t*) 0xe000ed20))
#define portNVIC_PENDSV_PRI  (((uint32_t)configKERNEL_INTERRUPT_PRIORITY)<<16UL)
#define portNVIC_SYSTICK_PRI  (((uint32_t)configKERNEL_INTERRUPT_PRIORITY)<<24UL)
BaseType_t xPortStartScheduler(void)
{
	//把pendsv systick中斷優(yōu)先級(jí)設(shè)置為最低
	portNVIC_SYSPRI2_REG |=portNVIC_PENDSV_PRI;
	portNVIC_SYSPRI2_REG |=portNVIC_SYSTICK_PRI;
	
	//啟動(dòng)第一個(gè)任務(wù),該函數(shù)不會(huì)返回
	prvStartFirstTask();
	//不會(huì)運(yùn)行到這里
	return 0;
}

在CM3中,0xE000ED08存放的是SCB_VTOR寄存器地址,SCB_VTOR是向量表的起始地址,即MSP的地址

__asm void prvStartFirstTask(void)
{
	PRESERVE8
	ldr r0, =0xE000ED08 //r0=0xE000ED08,相當(dāng)于r0=&SCB_VTOR
	ldr r0, [r0]        //r0=*r0,即r0=*((uint32_t*)0xE000ED08),即r0=0x0
	ldr r0, [r0]        //r0=*r0,即r0=*((uint32_t*)0x0),即r0=msp指針值
	msr msp, r0         //msp=r0,msp指針獲得msp地址
	cpsie i   //開中斷
	cpsie f   //開中斷
	dsb
	isb
	svc 0    //觸發(fā)SVC系統(tǒng)調(diào)用,此后會(huì)進(jìn)入SVCHandler
	nop      //下面這2個(gè)nop不會(huì)執(zhí)行
	nop
}
__asm void vPortSVCHandler(void)
{
	extern pxCurrentTCB;
	PRESERVE8
	//r3=&pxCurrentTCB,注意pxCurrentTCB本身就是一個(gè)指針,是指向一個(gè)任務(wù)的TCB指針
	ldr r3, =pxCurrentTCB  
	//r1=*r3,即r1=pxCurrentTCB
	ldr r1, [r3]           
	//r0=*r1,此時(shí)r1=pxCurrentTCB,
	//又結(jié)構(gòu)體第一個(gè)成員(棧頂指針)的地址和結(jié)構(gòu)體地址數(shù)值上是相同的,
	//所以r0=*(&(pxCurrentTCB.pxTopOfStack)),即r0=pxCurrentTCB.pxTopOfStack,
	//即r0此時(shí)指向當(dāng)前任務(wù)空閑棧頂位置
	ldr r0, [r1]           
	//以r0為開始把r4-r11依次保存到當(dāng)前任務(wù)的psp棧
	ldmia r0!,{r4-r11}
	//更新當(dāng)前任務(wù)psp棧指針
	msr psp,r0
	isb
	mov r0, #0
	msr basepri,r0 //開中斷
    //此時(shí)r14是0xFFFF_FFF9,表示返回后進(jìn)入Thread mode,使用MSP,返回Thmub狀態(tài),
    //這里其實(shí)是把bit2置1,要求返回后使用PSP
	orr r14,#0x0d  
	//返回,這里會(huì)自動(dòng)加載初始化任務(wù)棧填寫的xPSR、R15、R14、R12、R3-R0值到內(nèi)核對(duì)應(yīng)寄存器,
	//所以返回后就直接到pxCurrentTCB指向的任務(wù)
	bx r14  
}

任務(wù)切換

這里手動(dòng)觸發(fā)任務(wù)切換,其實(shí)就是向中斷控制及狀態(tài)寄存器ICSR(地址0xE000_ED04)PENDSVSET位(bit28)寫1懸起pendsv中斷,在pendsv_handler中尋找下一個(gè)要運(yùn)行的任務(wù)并做任務(wù)切換

在這里插入圖片描述

#define taskYIELD() portYIELD()
#define portNVIC_INT_CTRL_REG (*((volatile uint32_t*)0xe000ed04))
#define portNVIC_PENDSVSET_BIT (1<<28UL)
#define portSY_FULL_READ_WRITE (15)
#define portYIELD() \
{ \
   //觸發(fā)一次pendsv中斷
  	portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; \
	  __dsb(portSY_FULL_READ_WRITE); \
	  __isb(portSY_FULL_READ_WRITE); \
}
__asm void xPortPendSVHandler(void)
{
    //進(jìn)入中斷前會(huì)自動(dòng)保存xPSR、R15、R14、R12、R3-R0到當(dāng)前任務(wù)P棧中,此時(shí)任務(wù)棧頂指針指向要保存R11的位置
	extern pxCurrentTCB;
	extern vTaskSwitchContext;//這個(gè)函數(shù)是用來尋找下一個(gè)要運(yùn)行的任務(wù)
	PRESERVE8
	mrs r0,psp  //當(dāng)前任務(wù)棧psp指針存入r0,
	isb
	ldr r3,=pxCurrentTCB //r3=&pxCurrentTCB 
	ldr r2,[r3]          //r2=*r3, 即r2=pxCurrentTCB
	//以r0開始遞減的手動(dòng)保存r4-r11
	stmdb r0!,{r4-r11}
	//r0=*r2,此時(shí)r2=pxCurrentTCB,
	//又結(jié)構(gòu)體第一個(gè)成員(棧頂指針)的地址和結(jié)構(gòu)體地址數(shù)值上是相同的,
	//所以r0=*(&(pxCurrentTCB.pxTopOfStack)),即r0=pxCurrentTCB.pxTopOfStack,
	//即r0此時(shí)指向當(dāng)前任務(wù)空閑棧頂位置
	str r0,[r2]
	//到這里上文就保存完成
	//將r3、r14壓入棧保存起來(此時(shí)sp使用的是msp)
	//r14要保存是因?yàn)榈认乱{(diào)用函數(shù),避免r14被覆蓋無法從pendsv中斷正常返回
	//r3要保存是因?yàn)閞3保存的是當(dāng)前正在運(yùn)行任務(wù)控制塊的二級(jí)指針&pxCurrentTCB,后面要通過r3來操作pxCurrentTCB來切到下文
	stmdb sp!,{r3,r14}
	//關(guān)中斷,閾值是configMAX_SYSCALL_INTERRUPT_PRIORITY
	mov r0,#configMAX_SYSCALL_INTERRUPT_PRIORITY
	msr basepri,r0
	dsb
	isb
    //調(diào)用vTaskSwitchContext,功能是找到優(yōu)先級(jí)最高的任務(wù),然后讓pxCurrentTCB = &優(yōu)先級(jí)最高任務(wù)TCB,我們這里手動(dòng)指定
	bl vTaskSwitchContext
    //開中斷
	mov r0,#0
	msr basepri,r0
    //從msp中恢復(fù)r3、r14
	ldmia sp!,{r3,r14}
	//r1=*r3,此時(shí)r3=&pxCurrentTCB
	//即r1=pxCurrentTCB
	ldr r1,[r3] 
    //r0=*r1則r0=pxCurrentTCB.pxTopOfStack 理由前面講過
	ldr r0,[r1]
    //這里把pxCurrentTCB保存的需要手動(dòng)加載的值加載到內(nèi)核的r4-r11
	ldmia r0!,{r4-r11}
    //更新加載了r4-r11的任務(wù)棧到psp
	msr psp,r0 
	isb
	//返回,這里會(huì)以psp自動(dòng)加載保存了xPSR、R15、R14、R12、R3-R0值到內(nèi)核的xPSR、R15、R14、R12、R3-R0寄存器,所以返回的是pxCurrentTCB任務(wù)
	bx r14
	nop
}

這里為了簡(jiǎn)單手動(dòng)指定TCB

extern TCB_t Task1TCB;
extern TCB_t Task2TCB;
void vTaskSwitchContext(void)
{
  if(pxCurrentTCB == &Task1TCB)
	{
		pxCurrentTCB = &Task2TCB;
	}
	else
	{
		pxCurrentTCB = &Task1TCB;
	}
}

至此任務(wù)切換原理講完

main.c

extern List_t pxReadyTasksLists[configMAX_PRIORITIES];
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 delay(uint32_t x)
{
	for(;x!=0;x--);
}
void Task1_Fntry(void *arg)
{
	while(1)
	{
	  flag1=1;
	  delay(100);
	  flag1=0;
	  delay(100);
      taskYIELD();//手動(dòng)觸發(fā)切換任務(wù)
	}
}
void Task2_Fntry(void *arg)
{
	while(1)
	{
	 flag2=1;
	 delay(100);
	 flag2=0;
	 delay(100);
     taskYIELD();//手動(dòng)觸發(fā)切換任務(wù)
	}
}
int main(void)
{
    //初始化就緒列表
    prvInitialiseTaskLists();
    //創(chuàng)建任務(wù)1
	Task1_Handle = xTaskCreateStatic(Task1_Fntry,"task1",128,NULL,Task1Stack,&Task1TCB);
	//將任務(wù)添加到就緒列表
	vListInsertEnd(&pxReadyTasksLists[1],&((&Task1TCB)->xStateListItem));
	//創(chuàng)建任務(wù)2
	Task2_Handle = xTaskCreateStatic(Task2_Fntry,"task2",128,NULL,Task2Stack,&Task2TCB);
	//將任務(wù)添加到就緒列表
	vListInsertEnd(&pxReadyTasksLists[2],&((&Task2TCB)->xStateListItem));
	//啟動(dòng)調(diào)度器
	vTaskStartScheduler();
	for(;;);
}

以上就是FreeRTOS實(shí)時(shí)操作系統(tǒng)的任務(wù)創(chuàng)建與任務(wù)切換的詳細(xì)內(nèi)容,更多關(guān)于FreeRTOS任務(wù)創(chuàng)建與切換的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論