中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

視覺設(shè)計(jì)網(wǎng)站推薦世界球隊(duì)最新排名

視覺設(shè)計(jì)網(wǎng)站推薦,世界球隊(duì)最新排名,廣州17做網(wǎng)站,wordpress jquery ui寫在前面 本文是基于野火 RTOS 教程對(duì)空閑任務(wù)和阻塞延時(shí)的詳解。 一、什么是任務(wù)中的阻塞延時(shí) 說到阻塞延時(shí),筆者的第一反應(yīng)就是在單片機(jī)的 while 循環(huán)中,使用一個(gè) for 循環(huán)不斷遞減一個(gè)大數(shù),通過 CPU 不斷執(zhí)行一條指令的耗時(shí)進(jìn)行延時(shí)。這…

寫在前面

本文是基于野火 RTOS 教程對(duì)空閑任務(wù)和阻塞延時(shí)的詳解。

一、什么是任務(wù)中的阻塞延時(shí)

  • 說到阻塞延時(shí),筆者的第一反應(yīng)就是在單片機(jī)的 while 循環(huán)中,使用一個(gè) for 循環(huán)不斷遞減一個(gè)大數(shù),通過 CPU 不斷執(zhí)行一條指令的耗時(shí)進(jìn)行延時(shí)。這種延時(shí)會(huì)占用 CPU 資源執(zhí)行指令,在延時(shí)的時(shí)候 CPU 不能執(zhí)行其他的指令。
  • 但是注意,我們現(xiàn)在是想在 RTOS 中的任務(wù)實(shí)現(xiàn)阻塞延時(shí),RTOS 可以有多個(gè)任務(wù),所有所謂任務(wù)中的阻塞延時(shí)雖然也是阻塞其后的代碼運(yùn)行,但是只阻塞了他所在的那個(gè)任務(wù)中阻塞延時(shí)函數(shù)后面的代碼。
  • 也就是說,RTOS 中,任務(wù)中的阻塞延時(shí)就是先阻塞一下這個(gè)任務(wù),然后把 CPU 使用權(quán)交給其他代碼,雖然也是阻塞下文的代碼執(zhí)行,但是只阻塞這個(gè)任務(wù)的下文,CPU 在這個(gè)過程中可以執(zhí)行其他任務(wù)中的指令,大大提高 CPU 利用率,和筆者印象中的阻塞延時(shí)并不一樣。

二、空閑任務(wù)有什么用

  • 空閑任務(wù)的優(yōu)先級(jí)是所有任務(wù)中優(yōu)先級(jí)最低的,當(dāng)其他任務(wù)都在阻塞延時(shí)中,CPU 就會(huì)切換到空閑任務(wù)運(yùn)行。
  • 一般來說在空閑任務(wù)里面運(yùn)行一些系統(tǒng)內(nèi)存的清理工作,或者在空閑任務(wù)中讓單片機(jī)休眠或者進(jìn)入低功耗模式。

三、空閑任務(wù)的實(shí)現(xiàn)

  1. 定義空閑任務(wù)的任務(wù)棧
  2. 定義空閑任務(wù)的 TCB
  3. 空閑任務(wù)的創(chuàng)建

注意,空閑任務(wù)的任務(wù)棧和 TCB 變量我們都在 main.c 中聲明為全局變量,但是同時(shí),我們想在開啟任務(wù)調(diào)度器的時(shí)候自動(dòng)創(chuàng)建一個(gè)空閑任務(wù),而 RTOS 的開發(fā)人員不用顯式地去創(chuàng)建空閑任務(wù),所以我們把空閑任務(wù)的創(chuàng)建集成在 void vTaskStartScheduler( void ) 這個(gè)函數(shù)中。這樣,我們?cè)趩?dòng)調(diào)度器的同時(shí)就會(huì)自動(dòng)創(chuàng)建一個(gè)空閑任務(wù)。代碼如下:

void vTaskStartScheduler( void )
{
/*======================================創(chuàng)建空閑任務(wù)start==============================================*/     TCB_t *pxIdleTaskTCBBuffer = NULL;               /* 用于指向空閑任務(wù)控制塊 */StackType_t *pxIdleTaskStackBuffer = NULL;       /* 用于空閑任務(wù)棧起始地址 */uint32_t ulIdleTaskStackSize;/* 獲取空閑任務(wù)的內(nèi)存:任務(wù)棧和任務(wù)TCB */vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize );    xIdleTaskHandle = xTaskCreateStatic( (TaskFunction_t)prvIdleTask,              /* 任務(wù)入口 */(char *)"IDLE",                           /* 任務(wù)名稱,字符串形式 */(uint32_t)ulIdleTaskStackSize ,           /* 任務(wù)棧大小,單位為字 */(void *) NULL,                            /* 任務(wù)形參 */(StackType_t *)pxIdleTaskStackBuffer,     /* 任務(wù)棧起始地址 */(TCB_t *)pxIdleTaskTCBBuffer );           /* 任務(wù)控制塊 *//* 將任務(wù)添加到就緒列表 */                                 vListInsertEnd( &( pxReadyTasksLists[0] ), &( ((TCB_t *)pxIdleTaskTCBBuffer)->xStateListItem ) );
/*======================================創(chuàng)建空閑任務(wù)end================================================*//* 手動(dòng)指定第一個(gè)運(yùn)行的任務(wù) */pxCurrentTCB = &Task1TCB;/* 初始化系統(tǒng)時(shí)基計(jì)數(shù)器 */xTickCount = ( TickType_t ) 0U;/* 啟動(dòng)調(diào)度器 */if( xPortStartScheduler() != pdFALSE ){/* 調(diào)度器啟動(dòng)成功,則不會(huì)返回,即不會(huì)來到這里 */}
}

上面這段代碼調(diào)用了 xTaskCreateStatic() 這個(gè)函數(shù)進(jìn)行空閑任務(wù)的創(chuàng)建,但是這個(gè)函數(shù)需要傳入空閑任務(wù)的任務(wù)棧和 TCB 變量,而我們把這些變量定義在了 main.c 中,所以需要使用 vApplicationGetIdleTaskMemory() 這個(gè)函數(shù)來使 vTaskStartScheduler() 函數(shù)中的任務(wù)指針等等變量指向定義在 main.c 中的任務(wù)棧和 TCB,然后再把這些任務(wù)指針等傳入 xTaskCreateStatic() 中。vApplicationGetIdleTaskMemory() 的具體代碼如下:

void vApplicationGetIdleTaskMemory( TCB_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )
{*ppxIdleTaskTCBBuffer=&IdleTaskTCB;*ppxIdleTaskStackBuffer=IdleTaskStack; *pulIdleTaskStackSize=configMINIMAL_STACK_SIZE;
}

四、任務(wù)中的阻塞延時(shí)怎么實(shí)現(xiàn)

具體想法如下:

  1. 為 TCB 添加記錄延時(shí)時(shí)間的參數(shù)
  2. 在任務(wù)中調(diào)用阻塞延時(shí)函數(shù)時(shí),會(huì)給 TCB 記錄延時(shí)時(shí)間的參數(shù)進(jìn)行賦值,然后調(diào)用任務(wù)切換函數(shù)
  3. 調(diào)用任務(wù)切換函數(shù)會(huì)產(chǎn)生 PendSV 中斷,在 PendSV中斷服務(wù)函數(shù)中會(huì)調(diào)用上下文切換函數(shù) vTaskSwitchContext()
  4. 在上下文切換函數(shù)中,我們更新當(dāng)前執(zhí)行任務(wù)的指針?,F(xiàn)在我們的思想是,如果當(dāng)前任務(wù)是空閑任務(wù),那么查看其他任務(wù)的延時(shí)是否結(jié)束,如果沒有結(jié)束就繼續(xù)執(zhí)行空閑任務(wù);如果當(dāng)前執(zhí)行的不是空閑任務(wù),那么檢查一下其他任務(wù)是否在延時(shí)中,如果不在延時(shí)中,就不忘初心進(jìn)行任務(wù)切換,如果在延時(shí)中,就判斷現(xiàn)在這個(gè)任務(wù)是否要延時(shí),如果要延時(shí)就切換到空閑任務(wù),否則就不進(jìn)行任何切換。
  5. 上面檢查任務(wù)是否在延時(shí)狀態(tài)都是通過檢查 TCB 的延時(shí)參數(shù)是否為 0 來實(shí)現(xiàn)的,我們使用 SysTick 中斷來對(duì) TCB 的延時(shí)參數(shù)進(jìn)行定時(shí)修改
  6. 在每次 SysTick 中斷觸發(fā)時(shí),我們更新一下系統(tǒng)時(shí)基計(jì)數(shù)器(以后有用),然后掃描一下就緒列表中所有 TCB 的延時(shí)參數(shù),不為 0 就減 1,最后嘗試任務(wù)切換

1. 為 TCB 添加記錄延時(shí)時(shí)間的參數(shù)

typedef struct tskTaskControlBlock
{volatile StackType_t    *pxTopOfStack;    /* 棧頂 */ListItem_t			    xStateListItem;   /* 任務(wù)節(jié)點(diǎn) */StackType_t             *pxStack;         /* 任務(wù)棧起始地址 *//* 任務(wù)名稱,字符串形式 */char                    pcTaskName[ configMAX_TASK_NAME_LEN ];  TickType_t xTicksToDelay; /* 用于延時(shí) */
} tskTCB;
typedef tskTCB TCB_t;

2. 阻塞延時(shí)函數(shù) vTaskDelay()

給 TCB 記錄延時(shí)時(shí)間的參數(shù)進(jìn)行賦值,然后調(diào)用任務(wù)切換函數(shù)。

void vTaskDelay( const TickType_t xTicksToDelay )
{TCB_t *pxTCB = NULL;/* 獲取當(dāng)前任務(wù)的TCB */pxTCB = pxCurrentTCB;/* 設(shè)置延時(shí)時(shí)間 */pxTCB->xTicksToDelay = xTicksToDelay;/* 任務(wù)切換 */taskYIELD();
}

3. 上下文切換函數(shù) vTaskSwitchContext()

  • 如果當(dāng)前任務(wù)是空閑任務(wù)
    • 查看其他任務(wù)的延時(shí)是否結(jié)束
      • 沒有結(jié)束 -> 繼續(xù)執(zhí)行空閑任務(wù)
      • 結(jié)束 -> 跳轉(zhuǎn)到其他任務(wù)
  • 如果當(dāng)前執(zhí)行的不是空閑任務(wù)
    • 檢查一下其他任務(wù)是否在延時(shí)中
      • 不在延時(shí)中 -> 進(jìn)行任務(wù)切換
      • 在延時(shí)中 -> 判斷現(xiàn)在這個(gè)任務(wù)是否要延時(shí)
        • 要延時(shí)就切換到空閑任務(wù)
        • 否則就不進(jìn)行任何切換
void vTaskSwitchContext( void )
{/* 如果當(dāng)前線程是空閑線程,那么就去嘗試執(zhí)行線程1或者線程2,看看他們的延時(shí)時(shí)間是否結(jié)束,如果線程的延時(shí)時(shí)間均沒有到期,那就返回繼續(xù)執(zhí)行空閑線程 */if( pxCurrentTCB == &IdleTaskTCB ){if(Task1TCB.xTicksToDelay == 0){            pxCurrentTCB =&Task1TCB;}else if(Task2TCB.xTicksToDelay == 0){pxCurrentTCB =&Task2TCB;}else{return;		/* 線程延時(shí)均沒有到期則返回,繼續(xù)執(zhí)行空閑線程 */} }else{/*如果當(dāng)前線程是線程1或者線程2的話,檢查下另外一個(gè)線程,如果另外的線程不在延時(shí)中,就切換到該線程否則,判斷下當(dāng)前線程是否應(yīng)該進(jìn)入延時(shí)狀態(tài),如果是的話,就切換到空閑線程。否則就不進(jìn)行任何切換 */if(pxCurrentTCB == &Task1TCB){if(Task2TCB.xTicksToDelay == 0){pxCurrentTCB =&Task2TCB;}else if(pxCurrentTCB->xTicksToDelay != 0){pxCurrentTCB = &IdleTaskTCB;}else {return;		/* 返回,不進(jìn)行切換,因?yàn)閮蓚€(gè)線程都處于延時(shí)中 */}}else if(pxCurrentTCB == &Task2TCB){if(Task1TCB.xTicksToDelay == 0){pxCurrentTCB =&Task1TCB;}else if(pxCurrentTCB->xTicksToDelay != 0){pxCurrentTCB = &IdleTaskTCB;}else {return;		/* 返回,不進(jìn)行切換,因?yàn)閮蓚€(gè)線程都處于延時(shí)中 */}}}
}

4. SysTick 中斷對(duì) TCB 的延時(shí)參數(shù)進(jìn)行定時(shí)修改

/*
*************************************************************************
*                             SysTick中斷服務(wù)函數(shù)
*************************************************************************
*/
void xPortSysTickHandler( void )
{/* 關(guān)中斷 */vPortRaiseBASEPRI();/* 更新系統(tǒng)時(shí)基 */xTaskIncrementTick();/* 開中斷 */vPortClearBASEPRIFromISR();
}

每次 SysTick 中斷觸發(fā)時(shí),我們更新一下系統(tǒng)時(shí)基計(jì)數(shù)器(以后有用),然后掃描一下就緒列表中所有 TCB 的延時(shí)參數(shù),不為 0 就減 1,最后嘗試任務(wù)切換:

void xTaskIncrementTick( void )
{TCB_t *pxTCB = NULL;BaseType_t i = 0;/* 更新系統(tǒng)時(shí)基計(jì)數(shù)器xTickCount,xTickCount是一個(gè)在port.c中定義的全局變量 */const TickType_t xConstTickCount = xTickCount + 1;xTickCount = xConstTickCount;/* 掃描就緒列表中所有線程的xTicksToDelay,如果不為0,則減1 */for(i=0; i<configMAX_PRIORITIES; i++){pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &pxReadyTasksLists[i] ) );if(pxTCB->xTicksToDelay > 0){pxTCB->xTicksToDelay --;}}/* 任務(wù)切換 */portYIELD();
}

關(guān)于上面這段代碼,有一段寫得很奇怪:

    /* 更新系統(tǒng)時(shí)基計(jì)數(shù)器xTickCount,xTickCount是一個(gè)在port.c中定義的全局變量 */const TickType_t xConstTickCount = xTickCount + 1;xTickCount = xConstTickCount;

筆者剛開始看到的時(shí)候想問:直接遞增xTickCount不行嗎,為什么要寫成
const TickType_t xConstTickCount = xTickCount + 1;
xTickCount = xConstTickCount;
這樣不是畫蛇添足嗎?使代碼更復(fù)雜。

其實(shí)不然,在任務(wù)調(diào)度器中,xTickCount 變量用于記錄系統(tǒng)的時(shí)基計(jì)數(shù)器。它的目的是跟蹤系統(tǒng)運(yùn)行的時(shí)間,并且根據(jù)需要遞增。

直接遞增 xTickCount 可能會(huì)導(dǎo)致并發(fā)問題。在多線程或多任務(wù)的情況下,如果有多個(gè)任務(wù)同時(shí)嘗試遞增 xTickCount,并且中間存在競(jìng)爭(zhēng)條件,可能會(huì)導(dǎo)致計(jì)數(shù)不準(zhǔn)確或不一致。

為了避免這種并發(fā)問題,代碼中將遞增操作分解為兩個(gè)步驟:

首先,通過 const TickType_t xConstTickCount = xTickCount + 1; 將 xTickCount 的值復(fù)制到一個(gè)中間變量 xConstTickCount 中,并遞增這個(gè)中間變量。

然后,將中間變量 xConstTickCount 的值賦回給 xTickCount,完成遞增操作。

這樣做的好處是,無論何時(shí)進(jìn)行遞增操作,代碼都使用了一個(gè)穩(wěn)定的中間值 xConstTickCount 來執(zhí)行計(jì)算和更新。這確保了計(jì)數(shù)器 xTickCount 在整個(gè)遞增過程中保持一致,并且不會(huì)受到其他任務(wù)的干擾。這樣可以避免并發(fā)問題,提高代碼的可靠性和正確性。

5. 最后是 SysTick 的相關(guān)初始化代碼

在調(diào)度器啟動(dòng)函數(shù) xPortStartScheduler() 函數(shù)中調(diào)用 vPortSetupTimerInterrupt():

/*
*************************************************************************
*                              調(diào)度器啟動(dòng)函數(shù)
*************************************************************************
*/BaseType_t xPortStartScheduler( void )
{/*PendSV是一個(gè)用于低優(yōu)先級(jí)任務(wù)切換的軟件中斷。通過觸發(fā)PendSV中斷,可以請(qǐng)求處理器在合適的時(shí)間切換到更高優(yōu)先級(jí)的任務(wù)。PendSV中斷具有最低的中斷優(yōu)先級(jí),因此可以在其他中斷處理完成后立即執(zhí)行。*//* 配置PendSV 和 SysTick 的中斷優(yōu)先級(jí)為最低 */portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;//初始化SysTick中斷vPortSetupTimerInterrupt();/* 啟動(dòng)第一個(gè)任務(wù),不再返回 */prvStartFirstTask();/* 不應(yīng)該運(yùn)行到這里 */return 0;
}

初始化 SysTick 的函數(shù) vPortSetupTimerInterrupt():

/*
*************************************************************************
*                             初始化SysTick
*************************************************************************
*/
void vPortSetupTimerInterrupt( void )
{/* 設(shè)置重裝載寄存器的值 */portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;/* 設(shè)置系統(tǒng)定時(shí)器的時(shí)鐘等于內(nèi)核時(shí)鐘使能SysTick 定時(shí)器中斷使能SysTick 定時(shí)器 */portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT |portNVIC_SYSTICK_ENABLE_BIT ); 
}

這里解釋一下重裝載寄存器的值怎么設(shè)置。計(jì)時(shí)器實(shí)際上是一個(gè)計(jì)數(shù)器,當(dāng)接收到設(shè)定數(shù)量的脈沖后進(jìn)行一次中斷,而這個(gè)設(shè)定的數(shù)量就是重裝載寄存器的值。

我們把計(jì)時(shí)器接入到 CPU 晶振后,由于晶振每隔一段固定時(shí)間發(fā)出一個(gè)脈沖信號(hào),此時(shí)計(jì)時(shí)器就將重裝載寄存器的值減 1,當(dāng)重裝載寄存器的值減到 0 后,就觸發(fā)一次中斷,由此完成了對(duì)晶振的高頻率信號(hào)的分頻。

注意,重裝載寄存器的值是從 0 開始減的,所以設(shè)置時(shí)要減 1。

可以看到,我們使用 configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL 進(jìn)行設(shè)置,configSYSTICK_CLOCK_HZ 實(shí)際上就是 CPU 的晶振頻率,而 configTICK_RATE_HZ 就是我們?cè)O(shè)置 SysTick 的中斷頻率。

其中的宏定義為:

#define configCPU_CLOCK_HZ			( ( unsigned long ) 25000000 )	
#define configTICK_RATE_HZ			( ( TickType_t ) 100 )/* SysTick 配置寄存器 */
#define portNVIC_SYSTICK_CTRL_REG			( * ( ( volatile uint32_t * ) 0xe000e010 ) )
#define portNVIC_SYSTICK_LOAD_REG			( * ( ( volatile uint32_t * ) 0xe000e014 ) )#ifndef configSYSTICK_CLOCK_HZ#define configSYSTICK_CLOCK_HZ configCPU_CLOCK_HZ/* 確保SysTick的時(shí)鐘與內(nèi)核時(shí)鐘一致 */#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 )

后記

如果您覺得本文寫得不錯(cuò),可以點(diǎn)個(gè)贊激勵(lì)一下作者!
如果您發(fā)現(xiàn)本文的問題,歡迎在評(píng)論區(qū)或者私信共同探討!
共勉!

http://www.risenshineclean.com/news/50742.html

相關(guān)文章:

  • 網(wǎng)站建設(shè)維修服務(wù)流程seo的作用是什么
  • 個(gè)人網(wǎng)站主機(jī)選擇別做網(wǎng)絡(luò)推廣員
  • 管理系統(tǒng)和網(wǎng)站哪個(gè)好做網(wǎng)站優(yōu)化排名推廣
  • 專題網(wǎng)站建設(shè)策劃全球訪問量top100網(wǎng)站
  • 抖音代運(yùn)營內(nèi)容關(guān)鍵詞優(yōu)化推廣
  • 杭州怎樣建設(shè)網(wǎng)站巨量廣告投放平臺(tái)
  • 做網(wǎng)站分成青島網(wǎng)站
  • wordpress新窗口打開所有外鏈游戲優(yōu)化大師官網(wǎng)
  • 石家莊做網(wǎng)站seo宣傳鏈接怎么做
  • 客戶可以自主發(fā)帖的網(wǎng)站建設(shè)百度網(wǎng)絡(luò)營銷中心app
  • 合肥營銷網(wǎng)站建設(shè)聯(lián)系方式百度站長工具平臺(tái)登錄
  • 杭州微信網(wǎng)站制作seo用什么工具
  • 南京專業(yè)做網(wǎng)站的公司哪家好網(wǎng)絡(luò)平臺(tái)宣傳方式有哪些
  • jsp做的網(wǎng)站可以用的站長分析工具
  • 崇文門網(wǎng)站建設(shè)做游戲推廣怎么找客戶
  • 瀏覽量代碼wordpressseo全稱英文怎么說
  • 西昌網(wǎng)站制作杭州網(wǎng)站優(yōu)化效果
  • 自建站怎么搭建廈門網(wǎng)站流量優(yōu)化價(jià)格
  • 新建網(wǎng)站推廣優(yōu)化疫情政策
  • 凡科做網(wǎng)站怎么樣競(jìng)價(jià)推廣賬戶競(jìng)價(jià)托管
  • 做網(wǎng)站需要的圖片鄭州關(guān)鍵詞網(wǎng)站優(yōu)化排名
  • 發(fā)布網(wǎng)站需要備案嗎cdq百度指數(shù)
  • 淄博建網(wǎng)站哪家好今日要聞10條
  • 安亭做網(wǎng)站公司核心關(guān)鍵詞和長尾關(guān)鍵詞舉例
  • 軍隊(duì)房地產(chǎn)與建設(shè)工程法律實(shí)務(wù)在哪個(gè)網(wǎng)站可以購買惠州seo網(wǎng)站管理
  • 東莞網(wǎng)站開發(fā)教程百度識(shí)圖搜索
  • 全椒網(wǎng)站建設(shè)八零云自助建站免費(fèi)建站平臺(tái)
  • 用記事本做電影介紹的網(wǎng)站口碑營銷ppt
  • 調(diào)用文章wordpress常州百度搜索優(yōu)化
  • 婚紗攝影網(wǎng)站應(yīng)該如何做優(yōu)化應(yīng)用商店app下載