在STM32F103上實現基於事件優先級的任務系統


  這個應該屬於是狀態機的范疇,看了一本《UML 狀態機圖的使用C/C++設計》里面介紹的Vannilla內核跟介紹的實現思想很像。基於狀態機思想設計的程序,可以有效運行於裸機上,基於OS的任務調度的思想,可以設置優先級,打亂程序的執行順序。與操作系統不同的是,操作系統可以打斷任務運行,並把保留每個任務的棧數據,而這個系統即使新加入的任務的優先級比正在運行的任務優先級高,也只能等待上一個任務執行完畢才會被運行。應避免使用延時函數,對於占用時間長的任務,可以對其進行狀態分解,比如按鍵任務:在狀態1,首次檢測到按下;狀態2,下次循環時檢測到按鍵還是處於按下狀態時,切換到該狀態;狀態3,下次循環還是檢測按鍵被按下時切換到改狀態,並執行安檢任務。利用循環做其他事情的時間所占的時間用來和消抖的時間做抵消,這就是一個簡單的狀態機任務。

以下程序盡量做到與硬件相關。

  具體實現方法:構建一個任務表示任務屬性的結構體

 1 typedef uint32_t event_t;
 2 typedef uint32_t tsTaskPriority_t;
 3 
 4 typedef void ( *pfTsTaskEventHandler_t )( event_t );
 5 
 6  typedef struct tsTaskTableEntry_tag {
 7    event_t events;          //任務事件
 8    tsTaskPriority_t priority; //任務優先級
 9    pfTsTaskEventHandler_t pfTaskEventHandler; //任務執行函數
10  } tsTaskTableEntry_t;

接口頭文件 interface.h:

 1 #ifndef _INTERFACE_H_
 2 #define _INTERFACE_H_
 3 
 4 #include "sys.h"
 5 
 6 #define FALSE            0
 7 #define TRUE             1
 8 
 9 typedef uint32_t event_t;
10 typedef uint32_t tsTaskID_t;
11 typedef uint32_t tsTaskPriority_t;
12 typedef uint32_t index_t;
13 typedef uint8_t  bool_t;
14 
15 #define gTsMaxTasks_c                   10                  //最大任務數
16 #define gTsIdleTaskID_c                 (( tsTaskID_t )  0 ) //空閑任務ID
17 #define gTsInvalidTaskID_c              (( tsTaskID_t ) -1 )  //空任務指針
18 #define gTsIdleTaskPriority_c           (( tsTaskPriority_t )  0 ) //空閑任務優先級
19 #define gTsInvalidTaskPriority_c        (( tsTaskPriority_t ) -1 ) //空任務優先級
20 
21 
22 #define _Interrupt_enable()              INTX_ENABLE()             //打開總中斷
23 #define _Interrupt_disable()             INTX_DISABLE()            //關閉總中斷
24 
25 typedef void ( *pfTsTaskEventHandler_t )( event_t );               //函數指針
26 typedef void ( *pfTimerEventHandler_t )( event_t );                //時間函數指針
27 
28 
29 #define NumberOfElements(array)     ((sizeof(array) / (sizeof(array[0])))) //獲取任務隊列的大小
30     
31 #endif

既然是基於事件在跑任務,接下來就是事件處理的頭文件

event.h

 1 #ifndef _EVENT_H_
 2 #define _EVENT_H_
 3 #include "sys.h"
 4 #include "stdint.h"
 5 #include "interface.h"
 6 
 7 
 8 typedef struct tsTaskTableEntry_tag {
 9   event_t events;               //任務事件
10   tsTaskPriority_t priority;    //任務優先級
11   pfTsTaskEventHandler_t pfTaskEventHandler; //任務執行的函數
12 } tsTaskTableEntry_t;
13 
14 extern tsTaskTableEntry_t maTsTaskTable[gTsMaxTasks_c];    //任務隊列  容量為10 可以設置宏定義修改
15 extern tsTaskID_t maTsTaskIDsByPriority[NumberOfElements(maTsTaskTable)]; //優先級列表
16 
17 void IdleTask(event_t events); //空閑任務優先級最低切必須實現,可以在其中實現低功耗功能
18 void TS_ClearEvent(tsTaskID_t taskID,event_t events); //清除任務的事件
19 void TS_Init(void); //任務列表初始化 
20 tsTaskID_t TS_CreateTask(tsTaskPriority_t taskPriority,pfTsTaskEventHandler_t pfTaskEventHandler); //向添加任務到任務列表
21 void TS_DestroyTask  (tsTaskID_t taskID); //從任務列表刪除任務
22 bool_t TS_PendingEvents(void);  //查詢任務列表是否有可執行的任務
23 void TS_SendEvent(tsTaskID_t taskID,event_t events); //發送消息給制指定的任務 也就是 任務屬性中 event
24 void TS_Scheduler(void);  //任務調度
25 
26 #endif

接下來是事件函數的實現:

event.c

  1 #include "event.h"
  2 #include "stdint.h"
  3 #include "global.h"
  4 #include "task.h"
  5 #include "string.h"
  6 #include "delay.h"
  7 #include "timer_event.h"
  8 
  9 static tsTaskTableEntry_t maTsTaskTable[gTsMaxTasks_c];
 10 static tsTaskID_t         maTsTaskIDsByPriority[NumberOfElements(maTsTaskTable)];
 11 
 12 static tsTaskID_t         gIdleTaskID = 0;
 13 
 14 // 清除任務事件標志
 15 void TS_ClearEvent(tsTaskID_t taskID,event_t events)
 16 {
 17 
 18   _Interrupt_disable();            //關閉總中斷
 19   maTsTaskTable[taskID].events &= ~events;  // 清標志位,置0
 20   _Interrupt_enable();              //打開總中斷
 21 }
 22 
 23 /* Initialize the task scheduler. */
 24 void TS_Init(void) {
 25   memset(maTsTaskTable, (u8)gTsInvalidTaskPriority_c, sizeof(maTsTaskTable));            // 清除任務表為無效
 26   memset(maTsTaskIDsByPriority, (u8)gTsInvalidTaskID_c, sizeof(maTsTaskIDsByPriority));  // 清除優先級表為無效
 27 
 28   gIdleTaskID = TS_CreateTask(IDLE_TASK_PRIORITY, IdleTask);  //創建空閑任務  優先級 0 
 29   gIdleTaskID = TS_CreateTask(PRINTF_TASK_PRIORITY,test_printf);  //打印任務函數 優先級 1 
 30 // gIdleTaskID = TS_CreateTask(LED_FLASH_PRIORITY,LED_Flash);  //打印任務函數 優先級 2
 31   addTimerEvent(CYCLE_EVENT,1,1000,LED_Flash);  //添加定時器事件
 32   
 33     
 34     
 35     
 36     if(gTsInvalidTaskID_c == gIdleTaskID)
 37     {
 38       printf("Task is create failed\r\n");  
 39     }
 40   
 41 }  
 42 /* TS_Init() */
 43 
 44 
 45 /****************************************************************************/
 46 
 47 /* Add a task to the task table.
 48  * Return the task ID, or gTsInvalidTaskID_c if the task table is full.
 49  *
 50  * taskPriority == 0 is reserved for the idle task, and must never be specified
 51  * for any other task. TS_CreateTask() does not check for this.
 52  *
 53  * Note that TS_CreateTask() does not prevent a given event handler function
 54  * pointer from being added more than once to the task table.
 55  *
 56  * Note that if TS_CreateTask() is called with a taskPriority that is the
 57  * same as the priority of a task that is already in the task table, the
 58  * two tasks will end up in adjacent slots in the table. Which one is
 59  * called first by the scheduler is not specified.
 60  */
 61 tsTaskID_t TS_CreateTask(tsTaskPriority_t taskPriority,pfTsTaskEventHandler_t pfTaskEventHandler)
 62 {
 63   index_t i;
 64   index_t freeSlot = gTsInvalidTaskID_c;    //默認為空ID  -1
 65   index_t sizeofTaskId = sizeof(maTsTaskIDsByPriority[0]);
 66 
 67   /* Try to find a free slot in the task table. 發現任務表的空閑位置,索引保存到freeSlot*/
 68   for (i = 0, freeSlot = gTsInvalidTaskID_c;(i < NumberOfElements(maTsTaskTable)); ++i) {
 69     if (maTsTaskTable[i].priority == gTsInvalidTaskPriority_c) {
 70       freeSlot = i;
 71       break;
 72     }
 73   }                                     /* for (i = 0, freeSlot = 0xFF; ... */
 74 
 75   if (freeSlot == gTsInvalidTaskID_c) {
 76     return gTsInvalidTaskID_c;  // 任務表已滿
 77   }
 78 
 79   // 初始化任務條目
 80   maTsTaskTable[freeSlot].events = 0;
 81   maTsTaskTable[freeSlot].pfTaskEventHandler = pfTaskEventHandler;
 82   maTsTaskTable[freeSlot].priority = taskPriority;
 83 
 84   /* maTsTaskIDsByPriority is maintained in sorted order, so 1) find the */
 85   /* place where this new task should go; 2) move everything up; and 3) add */
 86   /* the new task. */
 87 
 88   /* 優先級從小到大,等級從高到低 */
 89   for (i = 0; i < NumberOfElements(maTsTaskIDsByPriority); i++) {
 90     /* If the end of the table is reached, just add the new task. */
 91     if (maTsTaskIDsByPriority[i] == gTsInvalidTaskPriority_c)
 92     {
 93       break;  // 如果里面存入的是非法的優先級,則說明結束
 94     }
 95     
 96     /* If all tasks from this point on have lower priorities than the task */
 97     /* being added, move the rest up and insert the new one. */
 98    
 99      /* 找到優先級比其大的第一個索引,之后將該索引(包含自己)后的數據都后移,空閑出此位置 */
100     if (maTsTaskTable[maTsTaskIDsByPriority[i]].priority < taskPriority) {
101       memcpy(&maTsTaskIDsByPriority[i + 1],
102                          &maTsTaskIDsByPriority[i],
103                          (NumberOfElements(maTsTaskIDsByPriority) - i - 1) * sizeofTaskId);
104       break;
105     }
106   }                                     /* for (i = 0; ... */
107   maTsTaskIDsByPriority[i] = freeSlot;  // 插入上面找到的索引位置
108 
109   return freeSlot;
110 }                                       /* TS_CreateTask() */
111 
112 
113 
114 /* Remove a task from the task table. */
115 void TS_DestroyTask(tsTaskID_t taskID)
116 {
117   index_t i;
118   index_t sizeofTaskId = sizeof(maTsTaskIDsByPriority[0]);
119 
120   if (maTsTaskTable[taskID].priority == gTsInvalidTaskPriority_c) {
121     return;
122   }
123 
124   /* Mark this slot in the task descriptor table as unused. */
125    /* 清除任務表中的記錄 */
126   maTsTaskTable[taskID].priority = gTsInvalidTaskPriority_c;
127 
128   /* Remove this task's ID from the priority table. Find it's position */
129   /* in the table, and shift everything else down. */
130   
131   /* 找到優先級中的索引,將后面的索引前移,並將最后一個位置的索引標記為無效 */
132   for (i = 0; i < NumberOfElements(maTsTaskIDsByPriority); i++) {
133     if (maTsTaskIDsByPriority[i] == taskID) {
134       memcpy(&maTsTaskIDsByPriority[i],
135                   &maTsTaskIDsByPriority[i + 1],
136                   (NumberOfElements(maTsTaskIDsByPriority) - i - 1) * sizeofTaskId);
137 
138       /* Note that exactly one entry was removed. */
139       maTsTaskIDsByPriority[NumberOfElements(maTsTaskIDsByPriority) - 1] = gTsInvalidTaskID_c;
140       break;
141     }
142   }
143 
144   return;
145 }                                       /* TS_DestroyTask() */
146 
147 // 查找是否有信號事件
148 bool_t TS_PendingEvents(void) {
149   index_t i;
150 
151   for (i = 0; i < NumberOfElements(maTsTaskTable); ++i) {
152     if (( maTsTaskTable[i].priority != gTsInvalidTaskPriority_c)
153         && maTsTaskTable[i].events) {
154       return TRUE;
155     }
156   }
157 
158   return FALSE;
159 }
160 // 發送信號事件
161 /* Send events to a task. */
162 void TS_SendEvent(tsTaskID_t taskID,event_t events)
163 {
164   _Interrupt_disable();   // 關中斷
165   maTsTaskTable[taskID].events |= events;
166   _Interrupt_enable();    //開中斷
167 }                                       /* TS_SendEvent() */
168 
169 // 任務輪詢函數,在主循環中調用 其他任務沒有事件發生時 就執行空閑任務
170 void TS_Scheduler(void) 
171     {
172     index_t activeTask;
173     event_t events;
174     index_t i;
175     index_t taskID;
176 
177   /* maTsTaskIDsByPriority[] is maintained in task priority order. If there */
178   /* are fewer than the maximum number of tasks, the first gInvalidTaskID_c */
179   /* marks the end of the table. */
180   for (;;) {
181     /* Look for the highest priority task that has an event flag set. */
182     activeTask = gTsIdleTaskID_c;                   // 如果未找到有效的信號事件,則調用空閑函數
183     for (i = 0; i < NumberOfElements(maTsTaskIDsByPriority); ++i) 
184       {
185       taskID = maTsTaskIDsByPriority[i];
186       if (taskID == gTsInvalidTaskID_c) {
187         break;
188       }
189 
190       if (maTsTaskTable[taskID].events) {
191         activeTask = taskID;                          // 找到有信號事件的任務ID
192         break;
193       }
194     }
195 
196     /* If there are no outstanding events, call the idle task. */
197     _Interrupt_disable();                              // 關中斷
198     events = maTsTaskTable[activeTask].events;        // 取出信號
199     maTsTaskTable[activeTask].events = 0;             // 清空原始信號
200     _Interrupt_enable();    //開中斷
201 
202     (*maTsTaskTable[activeTask].pfTaskEventHandler)(events); // 調用對用的事件函數
203   }                                                         /* for (;;) */
204 }                                                            /* TS_Scheduler() */
205 
206 /*
207 空閑任務 
  函數功能:統計空閑任務被執行的次數
208 */ 209 void IdleTask(event_t events) 210 { 211 static u16 count = 0; 212 delay_us(100); 213 count++; 214 if(10000 == count) 215 { 216 count = 0; 217 printf("Idle has ran 10000 times\r\n"); 218 } 219 }

測試函數:test_printf 和 LED_Flash

代碼如下:

enum TASK_ID //任務優先級枚舉
 { 
  IDLE_TASK_PRIORITY   = 0,     
  PRINTF_TASK_PRIORITY = 1,
  LED_FLASH_PRIORITY   = 2,
      
 };    

  /*
 打印測試程序
  */
  void test_printf(event_t events)
  {
      printf("hello world ...%02d\r\n",events); //打印事件
  }
  /*
 LED燈閃爍
 */
 void LED_Flash(event_t events)
 {
    LED0 = !LED0;  //燈閃爍
 }

//定時器3 1秒鍾 中斷服務程序     
void TIM3_IRQHandler(void)
{      
    static u8 count = 0;
    if(TIM3->SR&0X0001)//溢出中斷
    {       
        LED1=!LED1;    
          TS_SendEvent( LED_FLASH_PRIORITY,0x01); //發送事件到制定的任務
         count++;
         if(count%2)
         {
             
          TS_SendEvent(PRINTF_TASK_PRIORITY,count); //發送事件到制定的任務
         
         }            
    }                   
    TIM3->SR&=~(1<<0);//清除中斷標志位         
}

在主函數初始化區域執行TS_Init();在while循環調用 TS_Scheduler();就可以了

附上時間事件,類似於操作系統中的定時任務:

timer_event.h

 1 #ifndef _TIMER_EVENT_H_
 2 #define _TIMER_EVENT_H_
 3 #include "sys.h"
 4 #include "interface.h"
 5 
 6 #define     MAX_TIMER_EVENT_Q       5
 7 
 8 #define     CYCLE_EVENT             1L
 9 #define     COMMON_EVENT            0L
10 
11 typedef struct
12 {
13      u8 type;           // 事件類別,循環事件還是延時事件
14      u16 id;            // 定時器編號
15      u16 timer;         // 延時或周期時間計數器
16      u16 timerBackup;   // 用於周期事件的時間計數備份
17      pfTimerEventHandler_t pfTimerEventHandler; //時間事件處理函數 盡量簡短
18      
19 }timerEventType;
20 
21 
22 void SysTickHandler(void);    //系統滴答時鍾處理函數
23 void addTimerEvent(u8 type, u16 id, u16 timer,pfTimerEventHandler_t fun);//添加時間事件
24 void delTimerEvent(u16 id);      //刪除時間事件
25 
26 #endif

實現過程,使用時在丹皮阿尼中添加一個1ms或者10ms的時間片的定時器任務,用來驅動時間事件:

#include "timer_event.h"
#include "event.h"


static u8 _timerEventTail = 0;  //記錄當前隊列中時間事件的個數
static timerEventType _timerEvent[MAX_TIMER_EVENT_Q];
/* 
 函數描述:系統時間片處理函數
 輸入參數; 無
 輸出參數:無
 返回值:  無
*/

void SysTickHandler(void) 
 {
    u8 i = 0;
     for(i=0; i<_timerEventTail; i++)
     {
         _timerEvent[i].timer--;
         if(_timerEvent[i].timer == 0)
         {
             _timerEvent[i].pfTimerEventHandler(_timerEvent[i].id);// 事件觸發處理函數
 
           if(_timerEvent[i].type == CYCLE_EVENT)
             {
                // 循環事件,重新計數
                 _timerEvent[i].timer = _timerEvent[i].timerBackup;
             }
             else
             {
                 // 延時事件,觸發后刪除
                 delTimerEvent(_timerEvent[i].id);
             }
         }
     }
 }
 /*
 函數描述:添加定時器事件
 輸入參數:
         type:定時器類型 
         id:定時器ID 
         timer:定時時間 
         fun:回調函數
 輸出參數:無
 返回值:無
 */
 void addTimerEvent(u8 type, u16 id, u16 timer,pfTimerEventHandler_t fun)
{
     _timerEvent[_timerEventTail].type = type;            //事件類型
     _timerEvent[_timerEventTail].id = id;          //時間事件編號
     _timerEvent[_timerEventTail].timer = timer;          // 時間單位是系統Tick間隔時間
     _timerEvent[_timerEventTail].timerBackup = timer;    // 延時事件並不使用
      _timerEvent[_timerEventTail].pfTimerEventHandler = fun;    // 延時事件並不使用
     _timerEventTail++;
}
/*
函數描述:刪除定時器任務
輸入參數:定時器ID
輸出參數: 無
返回值:無
*/
void delTimerEvent(u16 id)
{
    u8 i = 0,j; 
    for(i=0; i<_timerEventTail; i++)
    {
        if(_timerEvent[i].id == id)
        {
            for(j=i; j<_timerEventTail; j++)
            {
                _timerEvent[j] = _timerEvent[j+1];
            }
            _timerEventTail--;
        }
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM