一区二区三区三上|欧美在线视频五区|国产午夜无码在线观看视频|亚洲国产裸体网站|无码成年人影视|亚洲AV亚洲AV|成人开心激情五月|欧美性爱内射视频|超碰人人干人人上|一区二区无码三区亚洲人区久久精品

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

什么是FreeRTOS的延時

汽車電子技術(shù) ? 來源:物聯(lián)網(wǎng)IoT開發(fā) ? 作者:杰杰mcu ? 2023-02-14 09:45 ? 次閱讀

01

FreeRTOS 時間管理

時間管理包括兩個方面:系統(tǒng)節(jié)拍以及任務(wù)延時管理。

2

系統(tǒng)節(jié)拍:

在前面的文章也講得很多,想要系統(tǒng)正常運(yùn)行,那么時鐘節(jié)拍是必不可少的,FreeRTOS的時鐘節(jié)拍通常由SysTick提供,它周期性的產(chǎn)生定時中斷,所謂的時鐘節(jié)拍管理的核心就是這個定時中斷的服務(wù)程序。FreeRTOS的時鐘節(jié)拍isr中核心的工作就是調(diào)用 vTaskIncrementTick() 函數(shù)。具體見上之前的文章。

3

今天主要講解延時的實(shí)現(xiàn)

FreeRTOS提供了兩個系統(tǒng)延時函數(shù):

**相對延時函數(shù)vTaskDelay() **

絕對延時函數(shù)vTaskDelayUntil()。

這些延時函數(shù)可不像我們以前用裸機(jī)寫代碼的延時函數(shù)操作系統(tǒng)不允許CPU在死等消耗著時間,因?yàn)檫@樣效率太低了。

同時,要告誡學(xué)操作系統(tǒng)的同學(xué),千萬別用裸機(jī)的思想去學(xué)操作系統(tǒng)。

4

任務(wù)延時

任務(wù)可能需要延時,兩種情況,一種是任務(wù)被vTaskDelay或者vTaskDelayUntil延時,另外一種情況就是任務(wù)等待事件(比如等待某個信號量、或者某個消息隊(duì)列)時候指定了 timeout (即最多等待timeout時間,如果等待的事件還沒發(fā)生,則不再繼續(xù)等待),在每個任務(wù)的循環(huán)中都必須要有阻塞的情況出現(xiàn),否則比該任務(wù)優(yōu)先級低的任務(wù)就永遠(yuǎn)無法運(yùn)行。

5

相對延時與絕對延時的區(qū)別

相對延時:vTaskDelay():

相對延時是指每次延時都是從任務(wù)執(zhí)行函數(shù) vTaskDelay() 開始,延時指定的時間結(jié)束

絕對延時:vTaskDelayUntil():

絕對延時是指調(diào)用 vTaskDelayUntil() 的任務(wù)每隔x時間運(yùn)行一次。也就是任務(wù)周期運(yùn)行。

6

相對延時:vTaskDelay()

相對延時 vTaskDelay() 是從調(diào)用 vTaskDelay() 這個函數(shù)的時候開始延時,但是任務(wù)執(zhí)行的時候,可能發(fā)生了中斷,導(dǎo)致任務(wù)執(zhí)行時間變長了,但是整個任務(wù)的延時時間還是1000tick ,這就不是周期性了,簡單看看下面代碼:

void vTaskA( void * pvParameters )  
 {  
    while(1) 
     {  
         //  ...
         //  這里為任務(wù)主體代碼
         //  ...

         /* 調(diào)用相對延時函數(shù),阻塞1000個tick */
         vTaskDelay( 1000 );  
     }  
}

可能說的不夠明確,可以看看圖解。

圖片

當(dāng)任務(wù)運(yùn)行的時候,假設(shè)被某個高級任務(wù)或者是中斷打斷了,那么任務(wù)的執(zhí)行時間就更長了,然而延時還是延時1000tick這樣子,整個系統(tǒng)的時間就混亂了。

如果還不夠明確,看看 vTaskDelay() 的源碼

void vTaskDelay( const TickType_t xTicksToDelay )
    {
    BaseType_t xAlreadyYielded = pdFALSE;

        /* 延遲時間為零只會強(qiáng)制切換任務(wù)。 */
        if( xTicksToDelay > ( TickType_t ) 0U )     (1)
        {
            configASSERT( uxSchedulerSuspended == 0 );
            vTaskSuspendAll();                      (2)
            {
                traceTASK_DELAY();
                /*將當(dāng)前任務(wù)從就緒列表中移除,并根據(jù)當(dāng)前系統(tǒng)節(jié)拍
                計(jì)數(shù)器值計(jì)算喚醒時間,然后將任務(wù)加入延時列表 */
                prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE );
            }
            xAlreadyYielded = xTaskResumeAll();
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }

        /* 強(qiáng)制執(zhí)行一次上下文切換 */
        if( xAlreadyYielded == pdFALSE )
        {
            portYIELD_WITHIN_API();
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }

(1):如果傳遞進(jìn)來的延時時間是0,只能進(jìn)行強(qiáng)制切換任務(wù)了,調(diào)用的是 portYIELD_WITHIN_API() ,它其實(shí)是一個宏,真正起作用的是 portYIELD() ,下面是它的源碼:

#define portYIELD()                                                \\
{                                                                \\
    /* 設(shè)置PendSV以請求上下文切換。 */                         \\
    portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;             \\

    __dsb( portSY_FULL_READ_WRITE );                            \\
    __isb( portSY_FULL_READ_WRITE );                            \\
}

(2):掛起當(dāng)前任務(wù)

然后將當(dāng)前任務(wù)從就緒列表刪除,然后加入到延時列表。是調(diào)用函數(shù) prvAddCurrentTaskToDelayedList() 完成這一過程的。由于這個函數(shù)篇幅過長,就不講解了,有興趣可以看看,我就簡單說說過程。在FreeRTOS中有這么一個變量,是用來記錄systick的值的。

PRIVILEGED_DATA static volatile TickType_t xTickCount     = ( TickType_t ) 0U;

在每次tick中斷時xTickCount加一,它的值表示了系統(tǒng)節(jié)拍中斷的次數(shù),那么啥時候喚醒被加入延時列表的任務(wù)呢?其實(shí)很簡單,FreeRTOS的做法將 xTickCount (當(dāng)前系統(tǒng)時間)+ xTicksToDelay (要延時的時間)即可。當(dāng)這個相對的延時時間到了之后就喚醒了,這個 (xTickCount+ xTicksToDelay) 時間會被記錄在該任務(wù)的任務(wù)控制塊中。

看到這肯定有人問,這個變量是TickType_t類型(32位)的,那肯定會溢出啊,沒錯,是變量都會有溢出的一天,可是FreeRTOS乃是世界第一的操作系統(tǒng)啊,FreeRTOS使用了兩個延時列表:

** xDelayedTaskList1和xDelayedTaskList2,**

并使用兩個列表指針類型變量pxDelayedTaskListpxOverflowDelayedTaskList分別指向上面的延時列表1和延時列表2(在創(chuàng)建任務(wù)時將延時列表指針指向延時列表)如果內(nèi)核判斷出xTickCount+xTicksToDelay溢出,就將當(dāng)前任務(wù)掛接到列表指針 pxOverflowDelayedTaskList指向的列表中,否則就掛接到列表指針pxDelayedTaskList指向的列表中。當(dāng)時間到了,就會將延時的任務(wù)從延時列表中刪除,加入就緒列表中,當(dāng)然這時候就是由調(diào)度器覺得任務(wù)能不能運(yùn)行了,如果任務(wù)的優(yōu)先級大于當(dāng)前運(yùn)行的任務(wù),那么調(diào)度器才會進(jìn)行任務(wù)的調(diào)度。

7

絕對延時:vTaskDelayUntil()

vTaskDelayUntil() 的參數(shù)指定了確切的滴答計(jì)數(shù)值

調(diào)用 vTaskDelayUntil() 是希望任務(wù)以固定頻率定期執(zhí)行,而不受外部的影響,任務(wù)從上一次運(yùn)行開始到下一次運(yùn)行開始的時間間隔是絕對的,而不是相對的。

圖片

下面看看 vTaskDelayUntil() 的使用方法,注意了,這 vTaskDelayUntil() 的使用方法與 vTaskDelay() 不一樣:

void vTaskA( void * pvParameters )  
 {  
    /* 用于保存上次時間。調(diào)用后系統(tǒng)自動更新 */
    static portTickType PreviousWakeTime;
    /* 設(shè)置延時時間,將時間轉(zhuǎn)為節(jié)拍數(shù) */
    const portTickType TimeIncrement = pdMS_TO_TICKS(1000); 
    /* 獲取當(dāng)前系統(tǒng)時間 */
    PreviousWakeTime = xTaskGetTickCount(); 
    while(1) 
     {  

         /* 調(diào)用絕對延時函數(shù),任務(wù)時間間隔為1000個tick */
         vTaskDelayUntil( &PreviousWakeTime,TimeIncrement );  

         //  ...
         //  這里為任務(wù)主體代碼
         //  ...

     }  
}

在使用的時候要將延時時間轉(zhuǎn)化為系統(tǒng)節(jié)拍,在任務(wù)主體之前要調(diào)用延時函數(shù)。

任務(wù)會先調(diào)用 vTaskDelayUntil() 使任務(wù)進(jìn)入阻塞態(tài),等到時間到了就從阻塞中解除,然后執(zhí)行主體代碼,任務(wù)主體代碼執(zhí)行完畢。會繼續(xù)調(diào)用 vTaskDelayUntil() 使任務(wù)進(jìn)入阻塞態(tài),然后就是循環(huán)這樣子執(zhí)行。即使任務(wù)在執(zhí)行過程中發(fā)生中斷,那么也不會影響這個任務(wù)的運(yùn)行周期,僅僅是縮短了阻塞的時間而已。

下面來看看vTaskDelayUntil()的源碼:

void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement )
    {
    TickType_t xTimeToWake;
    BaseType_t xAlreadyYielded, xShouldDelay = pdFALSE;

        configASSERT( pxPreviousWakeTime );
        configASSERT( ( xTimeIncrement > 0U ) );
        configASSERT( uxSchedulerSuspended == 0 );

        vTaskSuspendAll();                                  (1)
        {
            /* 保存系統(tǒng)節(jié)拍中斷次數(shù)計(jì)數(shù)器 */
            const TickType_t xConstTickCount = xTickCount;

            /* 生成任務(wù)要喚醒的滴答時間。*/
            xTimeToWake = *pxPreviousWakeTime + xTimeIncrement;

        /* pxPreviousWakeTime中保存的是上次喚醒時間,喚醒后需要一定時間執(zhí)行任務(wù)主體代碼,
            如果上次喚醒時間大于當(dāng)前時間,說明節(jié)拍計(jì)數(shù)器溢出了 具體見圖片 */
            if( xConstTickCount < *pxPreviousWakeTime )
            {
                / *由于此功能,滴答計(jì)數(shù)已溢出
                    持續(xù)呼喚。 在這種情況下,我們唯一的時間
                    實(shí)際延遲是如果喚醒時間也溢出,
                    喚醒時間大于滴答時間。 當(dāng)這個
                    就是這樣,好像兩個時間都沒有溢出。*/
                if( ( xTimeToWake < *pxPreviousWakeTime ) && ( xTimeToWake > xConstTickCount ) )
                {
                    xShouldDelay = pdTRUE;
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
            else
            {
                /  *滴答時間沒有溢出。 在這種情況下,如果喚醒時間溢出,
                    或滴答時間小于喚醒時間,我們將延遲。*/

                if( ( xTimeToWake < *pxPreviousWakeTime ) || ( xTimeToWake > xConstTickCount ) )
                {
                    xShouldDelay = pdTRUE;
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }

            /* 更新喚醒時間,為下一次調(diào)用本函數(shù)做準(zhǔn)備. */
            *pxPreviousWakeTime = xTimeToWake;

            if( xShouldDelay != pdFALSE )
            {
                traceTASK_DELAY_UNTIL( xTimeToWake );

                /* prvAddCurrentTaskToDelayedList()需要塊時間,而不是喚醒時間,因此減去當(dāng)前的滴答計(jì)數(shù)。 */
                prvAddCurrentTaskToDelayedList( xTimeToWake - xConstTickCount, pdFALSE );
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
        xAlreadyYielded = xTaskResumeAll();

        /* 如果xTaskResumeAll尚未執(zhí)行重新安排,我們可能會讓自己入睡。*/
        if( xAlreadyYielded == pdFALSE )
        {
            portYIELD_WITHIN_API();
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }

與相對延時函數(shù)vTaskDelay不同,本函數(shù)增加了一個參數(shù)pxPreviousWakeTime用于指向一個變量,變量保存上次任務(wù)解除阻塞的時間,此后函數(shù) vTaskDelayUntil() 在內(nèi)部自動更新這個變量。由于變量xTickCount可能會溢出,所以程序必須檢測各種溢出情況,并且要保證延時周期不得小于任務(wù)主體代碼執(zhí)行時間。

就會有以下3種情況,才能將任務(wù)加入延時鏈表中。

請記住這幾個單詞的含義:

** xTimeIncrement:任務(wù)周期時間

pxPreviousWakeTime:上一次喚醒的時間點(diǎn)

xTimeToWake:下一次喚醒的系統(tǒng)時間點(diǎn)

xConstTickCount:進(jìn)入延時的時間點(diǎn)**

第三種情況:常規(guī)無溢出的情況。

以時間為橫軸,上一次喚醒的時間點(diǎn)小于下一次喚醒的時間點(diǎn),這是很正常的情況。

圖片

第二種情況:喚醒時間計(jì)數(shù)器 (xTimeToWake) 溢出情況。

也就是代碼中*if( ( xTimeToWake < pxPreviousWakeTime ) || ( xTimeToWake > xConstTickCount ) )

圖片

第一種情況:喚醒時間 (xTimeToWake) 與進(jìn)入延時的時間點(diǎn) (xConstTickCount) 都溢出情況。

也就是代碼中*if( ( xTimeToWake < pxPreviousWakeTime ) && ( xTimeToWake > xConstTickCount ) )

圖片

從圖中可以看出不管是溢出還是無溢出,都要求在下次喚醒任務(wù)之前,當(dāng)前任務(wù)主體代碼必須被執(zhí)行完。也就是說任務(wù)執(zhí)行的時間不允許大于延時的時間,總不能存在每10ms就要執(zhí)行一次20ms時間的任務(wù)吧。計(jì)算的喚醒時間合法后,就將當(dāng)前任務(wù)加入延時列表,同樣延時列表也有兩個。每次系統(tǒng)節(jié)拍中斷,中斷服務(wù)函數(shù)都會檢查這兩個延時列表,查看延時的任務(wù)是否到期,如果時間到期,則將任務(wù)從延時列表中刪除,重新加入就緒列表。如果新加入就緒列表的任務(wù)優(yōu)先級大于當(dāng)前任務(wù),則會觸發(fā)一次上下文切換。

8

總結(jié)

如果任務(wù)調(diào)用相對延時,其運(yùn)行周期完全是不可測的,如果任務(wù)的優(yōu)先級不是最高的話,其誤差更大,就好比一個必須要在5ms內(nèi)相應(yīng)的任務(wù),假如使用了相對延時 1ms ,那么很有可能在該任務(wù)執(zhí)行的時候被更高優(yōu)先級的任務(wù)打斷,從而錯過5ms內(nèi)的相應(yīng),但是調(diào)用絕對延時,則任務(wù)會周期性將該任務(wù)在阻塞列表中解除,但是,任務(wù)能不能運(yùn)行,還得取決于任務(wù)的優(yōu)先級,如果優(yōu)先級最高的話,任務(wù)周期還是比較精確的(相對vTaskDelay來說),如果想要更加想精確周期性執(zhí)行某個任務(wù),可以使用系統(tǒng)節(jié)拍鉤子函數(shù) vApplicationTickHook() ,它在tick中斷服務(wù)函數(shù)中被調(diào)用,因此這個函數(shù)中的代碼必須簡潔,并且不允許出現(xiàn)阻塞的情況。

本文是杰杰原創(chuàng),轉(zhuǎn)載請說明出處。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • FreeRTOS
    +關(guān)注

    關(guān)注

    12

    文章

    485

    瀏覽量

    63494
  • 定時中斷
    +關(guān)注

    關(guān)注

    0

    文章

    19

    瀏覽量

    8661
  • Systick
    +關(guān)注

    關(guān)注

    0

    文章

    63

    瀏覽量

    13431
收藏 人收藏

    評論

    相關(guān)推薦

    stm32中FREERTOS延時函數(shù)osDelayUntil()死機(jī)的原因?

    我在使用STM32F4跑freertos的時候發(fā)現(xiàn)一旦使用osDelayUntil()函數(shù),就會死機(jī),但是用osDelay()函數(shù)就不會,按理說不是都可以用的嗎?有知道原因的嗎,謝謝!
    發(fā)表于 03-22 07:56

    【設(shè)計(jì)技巧】從單片機(jī)到操作系統(tǒng)(7)-FreeRTOS延時介紹

    本文轉(zhuǎn)自公眾號物聯(lián)網(wǎng)IoT開發(fā)1 FreeRTOS 時間管理時間管理包括兩個方面:系統(tǒng)節(jié)拍以及任務(wù)延時管理。2系統(tǒng)節(jié)拍:在前面的文章也講得很多,想要系統(tǒng)正常運(yùn)行,那么時鐘節(jié)拍是必不可少
    發(fā)表于 08-01 08:00

    FreeRTOS如何使用delay作為系統(tǒng)延時、任務(wù)調(diào)度

    請教一個問題,最近在學(xué)習(xí)使用FreeRTOS,想像原子一樣在delay.c里添加RTOS的系統(tǒng)支持,即使用tick時鐘作延時?,F(xiàn)在有幾個問題:1、在啟動任務(wù)調(diào)度器前,如果調(diào)用了delay_ms
    發(fā)表于 06-10 04:37

    FreeRTOSV8.2.3在探索者開發(fā)板上的移植怎么實(shí)現(xiàn)?

    、首先定義任務(wù)函數(shù) [C] 純文本查看 復(fù)制代碼2、編寫main函數(shù) [C] 純文本查看 復(fù)制代碼vTaskDelay(2000/portTICK_PERIOD_MS);//FreeRTOS延時函數(shù)
    發(fā)表于 07-30 08:02

    esp32s3延時出現(xiàn)問題求解

    不管是用的官方模板,還是自己寫的,使用的是freertos延時,或者是while(i--),都有問題。主要程序是這樣的:while(1){ vTaskDELAY(100); printf(“run
    發(fā)表于 02-10 06:43

    怎么去解決esp32s3延時出錯的問題?

    不管是用的官方模板,還是自己寫的,使用的是freertos延時,或者是while(i--),都有問題。主要程序是這樣的:while(1){ vTaskDELAY(100); printf(“run
    發(fā)表于 03-03 08:25

    esp32s3延時問題如何解決?

    不管是用的官方模板,還是自己寫的,使用的是freertos延時,或者是while(i--),都有問題。主要程序是這樣的:while(1){ vTaskDELAY(100); printf(“run
    發(fā)表于 03-08 08:38

    ETH-CH32v20x_v307在Freertos中添加以太網(wǎng)

    Freertos中,主函數(shù)作為一個線程,同時將中斷函數(shù)復(fù)制過來,增加freertos進(jìn)入和釋放中斷函數(shù),中斷聲明也不要忘記 修改驅(qū)動文件即eth_driver.c中的延時函數(shù),修改成fre
    發(fā)表于 08-09 10:41

    請問FreeRTOS中GPIO模擬SPI延時如何處理?

    FreeRTOS中想使用GPIO模擬SPI與設(shè)備進(jìn)行通訊,SPI傳輸速度要求在100K以上,FreeRTOS延時不能實(shí)現(xiàn)微妙級別的延時。模擬時的gpio翻轉(zhuǎn)
    發(fā)表于 11-10 07:56

    FreeRTOS中相對延時與絕對延時的區(qū)別

    FreeRTOS中相對延時和絕對延時的區(qū)別
    的頭像 發(fā)表于 03-12 10:32 ?8864次閱讀
    <b class='flag-5'>FreeRTOS</b>中相對<b class='flag-5'>延時</b>與絕對<b class='flag-5'>延時</b>的區(qū)別

    FreeRTOS中相對延時和絕對延時的區(qū)別

    嵌入式軟件代碼中延時是很常見的,只是延時種類有很多,看你用什么延時。 1 一個延時的問題 問題:周期性(固定一個時間)去處理某一件事情。你會通過什么方式去實(shí)現(xiàn)? 比如:間隔10ms去采
    的頭像 發(fā)表于 11-24 15:44 ?2147次閱讀
    <b class='flag-5'>FreeRTOS</b>中相對<b class='flag-5'>延時</b>和絕對<b class='flag-5'>延時</b>的區(qū)別

    FreeRTOS中相對延時和絕對延時的區(qū)別

    對計(jì)時精度要求比較高的地方適合定時器,像本章節(jié)說的周期性采集傳感器數(shù)據(jù),要求不適合很高,那么就引入本文說的絕對延時
    的頭像 發(fā)表于 11-29 10:19 ?2049次閱讀

    初入FreeRTOS

    目錄一、FreeRTOS介紹1、初識FreeRTOS,什么是 FreeRTOS2、FreeRTOS的特點(diǎn)二、FreeRTOS移植1、
    發(fā)表于 12-06 21:06 ?43次下載
    初入<b class='flag-5'>FreeRTOS</b>

    FreeRTOS高級篇9---FreeRTOS系統(tǒng)延時分析

    FreeRTOS提供了兩個系統(tǒng)延時函數(shù):相對延時函數(shù)vTaskDelay()和絕對延時函數(shù)vTaskDelayUntil()。相對延時是指每
    發(fā)表于 01-26 17:34 ?6次下載
    <b class='flag-5'>FreeRTOS</b>高級篇9---<b class='flag-5'>FreeRTOS</b>系統(tǒng)<b class='flag-5'>延時</b>分析

    FreeRTOS系列第11篇---FreeRTOS任務(wù)控制

    FreeRTOS任務(wù)控制API函數(shù)主要實(shí)現(xiàn)任務(wù)延時、任務(wù)掛起、解除任務(wù)掛起、任務(wù)優(yōu)先級獲取和設(shè)置等功能。
    發(fā)表于 01-26 17:54 ?12次下載
    <b class='flag-5'>FreeRTOS</b>系列第11篇---<b class='flag-5'>FreeRTOS</b>任務(wù)控制