日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

双任务延时原理与空闲任务

發布時間:2025/4/5 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 双任务延时原理与空闲任务 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • 1 雙任務延時原理與空閑任務
      • 1.1 設計目標
      • 1.2 任務延時原理
      • 1.3 設計實現

1 雙任務延時原理與空閑任務

1.1 設計目標

目標:提供一個任務延時接口,使得延時過程中可放棄CPU而轉而運行其它任務,提高CPU利用率。

1.2 任務延時原理

在整個延時過程中,任務暫停運行,釋放CPU的控制權給其它任務:

  • 開始時,啟動定時器,暫停任務運行:
  • 計時結束,關閉計時器,恢復任務運行。

    任務數量不限,而硬件計時器資源數量非常有限,不得不采用“軟計時器”,如下:

    對于硬件定時器:
  • 使用SysTick,RTOS啟動后立即啟動。
  • 周期性的中斷修改計時器計數值。
  • 中斷處理需要時間,所以不能太過頻繁,如10ms。

對于軟計時器:

  • 遞減計數。
  • 每個任務包含1個軟計時器。

延時精度問題:

  • 由于使用“軟計時器”,加之延時處理需要時間,所以延時精度有限,使用時需要注意場合。


空閑任務的引入:

  • 如果所有任務都進入延時狀態,那么RTOS應該運行什么呢?我們可以設計一個空閑任務,可以簡化RTOS的代碼結構,可以在空閑任務中方便的添加自定義代碼。

1.3 設計實現

為任務添加計時器:

添加空閑任務:

在時鐘節拍中斷中遞減計時器,并調用tTaskSched()切換任務:

最后修改tTaskSched(),僅切換到delayTicks數為0的任務運行。

添加延時接口taskDelay():

核心代碼如下:

/*************************************** Copyright (c)****************************************************** ** File name : main.c ** Latest modified Date : 2016-06-01 ** Latest Version : 0.1 ** Descriptions : 主文件,包含應用代碼 ** **-------------------------------------------------------------------------------------------------------- ** Created by : 01課堂 lishutong ** Created date : 2016-06-01 ** Version : 1.0 ** Descriptions : The original version ** **-------------------------------------------------------------------------------------------------------- ** Copyright : 版權所有,禁止用于商業用途 ** Author Blog : http://ilishutong.com **********************************************************************************************************/ #include "tinyOS.h" #include "ARMCM3.h"// 當前任務:記錄當前是哪個任務正在運行 tTask * currentTask;// 下一個將即運行的任務:在進行任務切換前,先設置好該值,然后任務切換過程中會從中讀取下一任務信息 tTask * nextTask;// 空閑任務 tTask * idleTask;// 所有任務的指針數組:簡單起見,只使用兩個任務 tTask * taskTable[2];/********************************************************************************************************** ** Function name : tTaskInit ** Descriptions : 初始化任務結構 ** parameters : task 要初始化的任務結構 ** parameters : entry 任務的入口函數 ** parameters : param 傳遞給任務的運行參數 ** Returned value : 無 ***********************************************************************************************************/ void tTaskInit (tTask * task, void (*entry)(void *), void *param, uint32_t * stack) {// 為了簡化代碼,tinyOS無論是在啟動時切換至第一個任務,還是在運行過程中在不同間任務切換// 所執行的操作都是先保存當前任務的運行環境參數(CPU寄存器值)的堆棧中(如果已經運行運行起來的話),然后再// 取出從下一個任務的堆棧中取出之前的運行環境參數,然后恢復到CPU寄存器// 對于切換至之前從沒有運行過的任務,我們為它配置一個“虛假的”保存現場,然后使用該現場恢復。// 注意以下兩點:// 1、不需要用到的寄存器,直接填了寄存器號,方便在IDE調試時查看效果;// 2、順序不能變,要結合PendSV_Handler以及CPU對異常的處理流程來理解*(--stack) = (unsigned long)(1<<24); // XPSR, 設置了Thumb模式,恢復到Thumb狀態而非ARM狀態運行*(--stack) = (unsigned long)entry; // 程序的入口地址*(--stack) = (unsigned long)0x14; // R14(LR), 任務不會通過return xxx結束自己,所以未用*(--stack) = (unsigned long)0x12; // R12, 未用*(--stack) = (unsigned long)0x3; // R3, 未用*(--stack) = (unsigned long)0x2; // R2, 未用*(--stack) = (unsigned long)0x1; // R1, 未用*(--stack) = (unsigned long)param; // R0 = param, 傳給任務的入口函數*(--stack) = (unsigned long)0x11; // R11, 未用*(--stack) = (unsigned long)0x10; // R10, 未用*(--stack) = (unsigned long)0x9; // R9, 未用*(--stack) = (unsigned long)0x8; // R8, 未用*(--stack) = (unsigned long)0x7; // R7, 未用*(--stack) = (unsigned long)0x6; // R6, 未用*(--stack) = (unsigned long)0x5; // R5, 未用*(--stack) = (unsigned long)0x4; // R4, 未用task->stack = stack; // 保存最終的值task->delayTicks = 0; }/********************************************************************************************************** ** Function name : tTaskSched ** Descriptions : 任務調度接口。tinyOS通過它來選擇下一個具體的任務,然后切換至該任務運行。 ** parameters : 無 ** Returned value : 無 ***********************************************************************************************************/ void tTaskSched () { // 空閑任務只有在所有其它任務都不是延時狀態時才執行// 所以,我們先檢查下當前任務是否是空閑任務if (currentTask == idleTask) {// 如果是的話,那么去執行task1或者task2中的任意一個// 當然,如果某個任務還在延時狀態,那么就不應該切換到他。// 如果所有任務都在延時,那么就繼續運行空閑任務,不進行任何切換了if (taskTable[0]->delayTicks == 0) {nextTask = taskTable[0];} else if (taskTable[1]->delayTicks == 0) {nextTask = taskTable[1];} else {return;}} else {// 如果是task1或者task2的話,檢查下另外一個任務// 如果另外的任務不在延時中,就切換到該任務// 否則,判斷下當前任務是否應該進入延時狀態,如果是的話,就切換到空閑任務。否則就不進行任何切換if (currentTask == taskTable[0]) {if (taskTable[1]->delayTicks == 0) {nextTask = taskTable[1];}else if (currentTask->delayTicks != 0) {nextTask = idleTask;} else {return;}}else if (currentTask == taskTable[1]) {if (taskTable[0]->delayTicks == 0) {nextTask = taskTable[0];}else if (currentTask->delayTicks != 0) {nextTask = idleTask;}else {return;}}}tTaskSwitch(); }/********************************************************************************************************** ** Function name : tTaskSystemTickHandler ** Descriptions : 系統時鐘節拍處理。 ** parameters : 無 ** Returned value : 無 ***********************************************************************************************************/ void tTaskSystemTickHandler () {// 檢查所有任務的delayTicks數,如果不0的話,減1。int i;for (i = 0; i < 2; i++) {if (taskTable[i]->delayTicks > 0){taskTable[i]->delayTicks--;}}// 這個過程中可能有任務延時完畢(delayTicks = 0),進行一次調度。tTaskSched(); }/********************************************************************************************************** ** Function name : tTaskDelay ** Descriptions : 使當前任務進入延時狀態。 ** parameters : delay 延時多少個ticks ** Returned value : 無 ***********************************************************************************************************/ void tTaskDelay (uint32_t delay) {// 配置好當前要延時的ticks數currentTask->delayTicks = delay;// 然后進行任務切換,切換至另一個任務,或者空閑任務// delayTikcs會在時鐘中斷中自動減1.當減至0時,會切換回來繼續運行。tTaskSched(); }/********************************************************************************************************* ** 系統時鐘節拍定時器System Tick配置 ** 在我們目前的環境(模擬器)中,系統時鐘節拍為12MHz ** 請務必按照本教程推薦配置,否則systemTick的值就會有變化,需要查看數據手冊才了解 **********************************************************************************************************/ void tSetSysTickPeriod(uint32_t ms) {SysTick->LOAD = ms * SystemCoreClock / 1000 - 1; NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);SysTick->VAL = 0; SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |SysTick_CTRL_TICKINT_Msk |SysTick_CTRL_ENABLE_Msk; }/********************************************************************************************************** ** Function name : SysTick_Handler ** Descriptions : SystemTick的中斷處理函數。 ** parameters : 無 ** Returned value : 無 ***********************************************************************************************************/ void SysTick_Handler () {tTaskSystemTickHandler(); }/********************************************************************************************************** ** 應用示例 ** 有兩個任務,分別執行task1Entry和task2Entry。功能是分別對相應的變量進行周期性置0置1. ** 每個任務都可以占用一段時間的CPU,一旦用完了,就會被強制暫停,切換到另一個任務中去。 **********************************************************************************************************/ int task1Flag; void task1Entry (void * param) {tSetSysTickPeriod(10);for (;;) {task1Flag = 1;tTaskDelay(1);task1Flag = 0;tTaskDelay(1);} }int task2Flag; void task2Entry (void * param) {for (;;) {task2Flag = 1;tTaskDelay(1);task2Flag = 0;tTaskDelay(1);} }// 任務1和任務2的任務結構,以及用于堆棧空間 tTask tTask1; tTask tTask2; tTaskStack task1Env[1024]; tTaskStack task2Env[1024];// 用于空閑任務的任務結構和堆棧空間 tTask tTaskIdle; tTaskStack idleTaskEnv[1024];void idleTaskEntry (void * param) {for (;;){// 空閑任務什么都不做} }int main () {// 初始化任務1和任務2結構,傳遞運行的起始地址,想要給任意參數,以及運行堆棧空間tTaskInit(&tTask1, task1Entry, (void *)0x11111111, &task1Env[1024]);tTaskInit(&tTask2, task2Entry, (void *)0x22222222, &task2Env[1024]);// 接著,將任務加入到任務表中taskTable[0] = &tTask1;taskTable[1] = &tTask2;// 創建空閑任務tTaskInit(&tTaskIdle, idleTaskEntry, (void *)0, &idleTaskEnv[1024]);idleTask = &tTaskIdle;// 我們期望先運行tTask1, 也就是void task1Entry (void * param) nextTask = taskTable[0];// 切換到nextTask, 這個函數永遠不會返回tTaskRunFirst();return 0; }

參考資料:

  • 【李述銅】從0到1自己動手寫嵌入式操作系統
  • 總結

    以上是生活随笔為你收集整理的双任务延时原理与空闲任务的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。