1 前言 在嵌入式MCU軟體開發過程中,程式任務調度架構的搭建尤為重要,直接關係到該程式能支持多少功能(隨著功能越多系統響應能力越弱,好的任務調度架構能夠在保持相同的系統響應能力前提下支持更多的功能),下麵介紹三種常用的程式任務調度框架設計方案: 前後臺順序執行法 時間片論法 操作系統 2 程式框架 ...
1 前言
在嵌入式MCU軟體開發過程中,程式任務調度架構的搭建尤為重要,直接關係到該程式能支持多少功能(隨著功能越多系統響應能力越弱,好的任務調度架構能夠在保持相同的系統響應能力前提下支持更多的功能),下麵介紹三種常用的程式任務調度框架設計方案:
- 前後臺順序執行法
- 時間片論法
- 操作系統
2 程式框架設計
2.1 前後臺順序執行法
這是初學者們常用的程式框架設計方案,不用考慮太多東西,代碼簡單,或者對系統的整體實時性和併發性要求不高;初始化後通過 while(1){ } 或 for(;;){ } 迴圈不斷調用自己編寫完成的函數,也基本不考慮每個函數執行所需要的時間,大部分情況下函數中或多或少都存在毫秒級別的延時等待。
- 優點:對於初學者來說,這是最容易也是最直觀的程式架構,邏輯簡單明瞭,適用於邏輯簡單,複雜度比較低的軟體開發。
- 缺點:實時性低,由於每個函數或多或少存在毫秒級別的延時,即使是1ms,也會造成其他函數間隔執行時間的不同,雖然可通過定時器中斷的方式,但是前提是中斷執行函數花的時間必須短。當程式邏輯複雜度提升時,會導致後來維護人員的大腦混亂,很難理清楚該程式的運行狀態。
以下是我在學校期間做的寢室防盜系統的主函數代碼(當時也存在部分BUG,沒有解決。現在再看,其實很多問題,而且比較嚴重,比如中斷服務函數內竟然有3000ms延時,這太可怕了,還有串口發送等等;由於實時性要求不算太高,因此主函數中的毫秒級別延時對系統運行沒有多大影響,當然除BUG外;若是後期需要維護,那就是一個大工程,還不如推翻重寫):
int main(void)
{
u8 temperature;
u8 humidity;
int a;
delay_init();
uart2_Init(9600);
TIM3_Int_Init(4999,7199);
ds1302_init();
while(DHT11_Init()); //DHT11初始化
a1602_init();
lcd12864_INIT();
LcdInit();
while(1)
{
for(a=0;a<11;a++)
{
num[a+3]=At24c02Read(a+2)-208;
delay_us(10);
}
for(a=0;a<6;a++)
{
shuru[a]=At24c02Read(a+13)-208;
delay_us(10);
}
delay_ms(10);
RED_Scan();
Ds1302ReadTime(); //讀取ds1302的日期時間
shi=At24c02Read(0); //讀取鬧鐘保存的數據
delay_ms(10);
fen=At24c02Read(1); //讀取鬧鐘保存的數據
usart2_scan(); //藍牙數據掃描
usart2_bian(); //藍牙處理數據
nao_scan();
k++;
if(k<20)
{
if(k==1)
LcdWriteCom(0x01); //清屏
LcdDisplay(); //顯示日期時間
}
if(RED==0)
RED_Scan();
if(k>=20&&k<30)
{
if(k==20)
LcdWriteCom(0x01); //清屏
Lcddisplay(); //顯示溫濕度
LcdWriteCom(0x80+6);
DHT11_Read_Data(&temperature,&humidity); //讀取溫濕度值
Temp=temperature;Humi=humidity;
LcdWriteData('0'+temperature/10);
LcdWriteData('0'+temperature%10);
LcdWriteCom(0x80+0X40+6);
LcdWriteData('0'+humidity/10);
LcdWriteData('0'+humidity%10);
}
if(k==30)
k=0;
lcd12864(); //顯示防盜鬧鐘狀態
}
}
//定時器3中斷服務程式
void TIM3_IRQHandler(void) //TIM3中斷
{
int i;
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //檢查TIM3更新中斷發生與否
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //清除TIMx更新中斷標誌
if(key1==1&&FEN-fen==0&&SHI-shi==0) //時間一到鬧鐘響起
{
f=1;
}
else
{
f=0;
}
if(USART_RX_BUF[0]=='R'&&USART_RX_BUF[1]=='I'&&USART_RX_BUF[2]=='N'&&USART_RX_BUF[3]=='G')
{
key0=1;
for(i=0;i<17;i++)
{
USART_SendData(USART1, num[i]);//向串口1發送數據
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待發送結束
USART_RX_STA=0;
}
delay_ms(3000);
for(i=0;i<3;i++)
{
USART_SendData(USART1, num1[i]);//向串口1發送數據
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待發送結束
USART_RX_STA=0;
}
}
}
}
2.2 時間片論法
介於“前後臺順序執行法”和“操作系統”之間的一種程式架構設計方案。該設計方案需能幫助嵌入式軟體開發者更上一層樓,在嵌入式軟體開發過程中,若遇到以下幾點,那麼該設計方案可以說是最優選擇,適用於程式較複雜的嵌入式系統;
- 目前的需求設計需要完全沒有必要上操作系統
- 任務函數無需時刻執行,存在間隔時間(比如按鍵,一般情況下,都需要軟體防抖,初學者的做法通常是延時10ms左右再去判斷,但10ms極大浪費了CPU的資源,在這段時間內CPU完全可以處理很多其他事情)
- 實時性有一定的要求
該設計方案需要使用一個定時器,一般情況下定時1ms即可(定時時間可隨意定,但中斷過於頻繁效率就低,中斷太長,實時性差),因此需要考慮到每個任務函數的執行時間,建議不能超過1ms(能通過程式優化縮短執行時間則最好優化,如果不能優化的,則必須保證該任務的執行周期必須遠大於任務所執行的耗時時間),同時要求主迴圈或任務函數中不能存在毫秒級別的延時。
如何確定每個函數的任務周期呢?根據任務的耗時和效果決定、如按鍵掃描任務周期為 10ms(為了提高響應),指示燈控制任務周期為 100ms(通常情況下最高100ms的閃爍頻率正好,特殊需求除外),LCD/OLED 顯示周期為 100ms(通過這種通過SPI/IIC等介面的方式耗時大約在 1~10ms,甚至更長,所以任務周期必須遠大於耗時,同時為了滿足人眼所能接受的刷屏效果,也不能太長,100ms 的任務周期比較合適)等
以下介紹兩種不同的實現方案,分別針對無函數指針概念的朋友和想進一步學習的朋友。
2.2.1 無函數指針的設計方式
/**
* @brief 主函數.
* @param None.
* @return None.
*/
int main(void)
{
System_Init();
while (1)
{
if (TIM_1msFlag) // 1ms
{
CAN_CommTask(); // CAN發送/接收通信任務
TIM_1msFlag = 0;
}
if (TIM_10msFlag) // 10ms
{
KEY_ScanTask(); // 按鍵掃描處理任務
TIM_10msFlag = 0;
}
if (TIM_20msFlag) // 20ms
{
LOGIC_HandleTask(); // 邏輯處理任務
TIM_20msFlag = 0;
}
if (TIM_100msFlag) // 100ms
{
LED_CtrlTask(); // 指示燈控制任務
TIM_100msFlag = 0;
}
if (TIM_500msFlag) // 500ms
{
TIM_500msFlag = 0;
}
if (TIM_1secFlag) // 1s
{
WDog_Task(); // 喂狗任務
TIM_1secFlag = 0;
}
}
}
/**
* @brief 定時器3中斷服務函數.
* @param None.
* @return None.
*/
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3,TIM_IT_Update) == SET) // 溢出中斷
{
sg_1msTic++;
sg_1msTic % 1 == 0 ? TIM_1msFlag = 1 : 0;
sg_1msTic % 10 == 0 ? TIM_10msFlag = 1 : 0;
sg_1msTic % 20 == 0 ? TIM_20msFlag = 1 : 0;
sg_1msTic % 100 == 0 ? TIM_100msFlag = 1 : 0;
sg_1msTic % 500 == 0 ? TIM_500msFlag = 1 : 0;
sg_1msTic % 1000 == 0 ? (TIM_1secFlag = 1, sg_1msTic = 0) : 0;
}
TIM_ClearITPendingBit(TIM3,TIM_IT_Update); // 清除中斷標誌位
}
2.2.2 含函數指針的設計方式
/**
* @brief 任務函數相關信息結構體定義.
*/
typedef struct{
uint8 m_runFlag; /*!< 程式運行標記:0-不運行,1運行 */
uint16 m_timer; /*!< 計時器 */
uint16 m_itvTime; /*!< 任務運行間隔時間 */
void (*m_pTaskHook)(void); /*!< 要運行的任務函數 */
} TASK_InfoType;
#define TASKS_MAX 5 // 定義任務數目
/** 任務函數相關信息 */
static TASK_InfoType sg_tTaskInfo[TASKS_MAX] = {
{0, 1, 1, CAN_CommTask}, // CAN通信任務
{0, 10, 10, KEY_ScanTask}, // 按鍵掃描任務
{0, 20, 20, LOGIC_HandleTask}, // 邏輯處理任務
{0, 100, 100, LED_CtrlTask}, // 指示燈控制任務
{0, 1000, 1000, WDog_Task}, // 喂狗任務
};
/**
* @brief 任務函數運行標誌處理.
* @note 該函數由1ms定時器中斷調用
* @param None.
* @return None.
*/
void TASK_Remarks(void)
{
uint8 i;
for (i = 0; i < TASKS_MAX; i++)
{
if (sg_tTaskInfo[i].m_timer)
{
sg_tTaskInfo[i].m_timer--;
if (0 == sg_tTaskInfo[i].m_timer)
{
sg_tTaskInfo[i].m_timer = sg_tTaskInfo[i].m_itvTime;
sg_tTaskInfo[i].m_runFlag = 1;
}
}
}
}
/**
* @brief 任務函數運行處理.
* @note 該函數由主迴圈調用
* @param None.
* @return None.
*/
void TASK_Process(void)
{
uint8 i;
for (i = 0; i < TASKS_MAX; i++)
{
if (sg_tTaskInfo[i].m_runFlag)
{
sg_tTaskInfo[i].m_pTaskHook(); // 運行任務
sg_tTaskInfo[i].m_runFlag = 0; // 標誌清0
}
}
}
/**
* @brief 主函數.
* @param None.
* @return None.
*/
int main(void)
{
System_Init();
while (1)
{
TASK_Process();
}
}
/**
* @brief 定時器3中斷服務函數.
* @param None.
* @return None.
*/
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3,TIM_IT_Update) == SET) // 溢出中斷
{
TASK_Remarks();
}
TIM_ClearITPendingBit(TIM3,TIM_IT_Update); // 清除中斷標誌位
}
2.3 操作系統
嵌入式操作系統EOS(Embedded OperatingSystem)是一種用途廣泛的系統軟體,過去它主要應用於工業控制和國防系統領域,而對於單片機來說,比較常用的有 UCOS、FreeRTOS、RT-Thread Nano 和 RTX 等多種搶占式操作系統(其他如 Linux 等操作系統不適用於單片機)
操作系統和“時間片論法”,在任務執行方面來說,操作系統對每個任務的耗時沒有過多的要求,需要通過設置每個任務的優先順序,在高優先順序的任務就緒時,會搶占低優先順序的任務;操作系統相對複雜,因此這裡沒有詳細介紹了。
關於如何選擇合適的操作系統([RTOS]uCOS、FreeRTOS、RTThread、RTX等RTOS的對比之特點):
- UCOS:網上資料豐富,非常適合學習,但是在產品上使用則需要收費
- FreeRTOS:使用免費,因此很多產品都在用
- RT-Thread:國產物聯網操作系統,有著十分豐富的組件,也免費,資料:RT-Thread 文檔中心
- RTX:為ARM和Cortex-M設備設計的免版稅,確定性的實時操作系統
借網上一張對比圖:
總結
從上述的對比中可以看出,時間片輪詢法的優勢還是比較大的,它既有前後臺順序執行法的優點,也有操作系統的優點。結構清晰,簡單,非常容易理解,所以這種是比較常用的單片機設計框架。
本文來自博客園,作者:大橙子瘋,轉載請註明原文鏈接:https://www.cnblogs.com/const-zpc/p/16364444.html