筆者目前在開發TI的藍牙晶元,CC2640,在開發一個與手機藍牙通訊以實現單片機系統聯結廣域網功能的設備。本來想把自己開發的過程遇到的問題與困難分享出來,但是想到與我一樣開發這樣設備的朋友太少了,寫在知乎不大好,於是還是打算將開發的過程分享到CSDN上。

https://blog.csdn.net/Clarence_happy/article/details/88838008?

blog.csdn.net

在開發CC2640前,筆者先是開發了CC2541,,CC2541是TI推出的藍牙晶元,功能強大是沒說了。這裡為什麼要提他,因為筆者使用官方提供的SDK開發套件代碼的時候,看到了很多優秀的代碼編輯方法,CC2541使用了類似TI-RTOS的實時操作系統,但是為什麼說是類似的,因為我分明看到主函數的運行內容是C語言清晰可見的邏輯,並沒有牽扯到底層堆棧、上下文信息保存、、等等這些操作系統必有的深奧東西,我想到CC2541上運行的也許並不是一個真正的實時操作系統。

這裡筆者粗淺的說一下自己對真正操作系統的理解,希望各位朋友能斧正。

在51單片機中,筆者認為只有兩個線程,一個是主線程,一個是中斷線程。主線程運行在main中,是按順序執行的代碼序列,中斷線程運行在特定硬體事件發生後,有對應的服務函數。這兩個線程最大的特點是,主線程運行後,如果此時中斷出現,系統會保護當前的各種重要數據,例如通用寄存器R0-R12和PC寄存器等等,這些信息都保存好,然後重新載入一系列中斷服務函數需要的寄存器值,從而實現從主線程跳轉到中斷線程。

筆者認為,一個中斷服務函數相當於一個中斷線程,多個中斷服務函數的話,會出現搶佔的問題,但是優秀的處理器會處理好這些搶佔的問題,例如NVIC不就是管理這些事情的嗎?反正筆者認為線程的切換,換湯不換藥,就是先保護現場,然後跳轉到特定的程序段,覆蓋現場,執行完以後回到之前的現場。

這讓筆者聯想到交換兩變數數值的演算法

//交換a 和 b 裡面的數值

void swap(uint8_t * a , uint8_t *b){ uint8_t temp; temp = *a ; //保護現場,因為*a馬上就要被覆蓋了 *a = *b ; //覆蓋的事情發生了,這樣做以後,程序會跳轉 *b = temp;//回到最初的狀態繼續運行主函數}

筆者認為,如果RTOS中TASK的概念是建立在上述這樣的線程之上的,是真RTOS,因為它是真正的實時搶佔的,好比51裡面主函數正運行某一個函數,這時中斷線程來了,主函數沒有執行完,但是不給你執行完的時間了,立刻切換。TASK也是,如果有兩個TASK,其中一個TASK正在運行,此時優先順序更高的(筆者認為51單片機的主線程是低優先順序TASK,任意中斷服務函數是高於主線程的TASK)TASK出現,優先順序低的TASK立刻保護現場,轉而執行到另一個TASK中來!

這裡的真RTOS,筆者認為晦澀,因為和底層晶元級的操作密切相關,微機原理,編譯原理,這些太困難,也只有像TI等等這些晶元公司或者FREERTOS這樣的開源組織能夠提供技術方案。移植真RTOS的時候會發現與底層寄存器相關程度太高了,如果沒有前人相應的經驗,移植起來實在困難。(這裡筆者說的是把FREERTOS的源碼移植進自己的工程,而不是使用晶元廠商的一系列開發工具鏈)

相對應的,筆者想分享的是假RTOS,類似CC2541提供的操作系統一般,使用時間片輪詢法實現一個假的實時操作系統。

這個假RTOS,筆者在大學的時候和實驗室的朋友討論過,朋友說,有一定的可能WINDOWS就是用的這個假實時的方法。因為電腦的主控速度太快了,你看不出是假實時,又可能是因為人類的輸入方式太慢了,人的反應速度太慢了,你根本感受不出來這是假實時。這無從考證,畢竟不開源。隨口一提,不必當真!

筆者認為,如果TASK的概念是基於上下文順序的,一個任務的執行必須等待上一個正在執行任務結束才能執行的,是偽RTOS,如果主頻夠快,你也能感受到這兩個任務是同時運行一般。

舉個例子吧,按鍵和LED燈,兩個TASK我希望他能通同時運行,並且採用偽操作系統的思維。

這裡寫的簡單一點,只是想分享方法,偽RTOS需要時鐘sysTick來實現延時,在單片機裏可以用任意定時器中斷來實現它,這裡筆者闡明 SYS_TICK會在1ms的時候置1一次,實現方法:在1ms定時器中斷函數中將其置1,具體代碼不寫。

void toggle_led(void); //執行此函數一次,LED閃爍一次

bool get_key(void);//執行此函數,如果按下返回false 否則返回true//上文闡明SYS_TICK變數會在1ms後置1一次extern bool SYS_TICK;

//創建兩個任務,實現檢測到按鍵事件,LED閃爍

void main(){ bool task_button_start = false; bool task_led_start =false; bool button_pressed_message =false; uint32_t button_tick=0; //一系列硬體初始化 while(1){ if(SYS_TICK){ //此函數類似時間片調度器

SYS_TICK = false;

//當查詢到SYS_TICK置1,認為1ms已經過去,這裡做計時用 button_tick ++; if(button_tick > 100){ button_tick = 0; task_button_start = true; //100ms開啟一次key監測任務 } if(button_pressed_message ){ //發現了按鍵消息,如果沒有收到對應的數據,線程被掛起,這個if永遠是false button_pressed_message = false;

toggle_led();

} if(task_button_start){ task_button_start = false; //button task被調度 if(!get_key()) button_pressed_message = true; } } }

}

顯然,這裡創建了兩個TASK ,一個是LED TASK 這個TASK一般是掛起的,等待KEY被按下的事件觸發,BUTTON TASK是100ms執行一次的,由時間片調度器給到開始條件。

仔細看的話,LED TASK 和 BUTTON TASK的優先順序 很明顯是 LED TASK優先順序高,因為 LED TASK在上下文 BUTTON TASK之前,每次while(1)都先運行LED TASK 然後再運行BUTTON TASK。 當然調度時間片的任務優先順序就不談了,他算不上TASK。

這裡簡單的分享了筆者對假RTOS的認識,當然內容還有很多,比如操作系統裏的任務管理,TASK之間的消息傳遞,implement這些介面之後,咱偽RTOS都得一個個實現。

筆者接下來想寫一點關於CC2541裏操作系統的實現方法,或許TI的天才工程師創建工程的方法,能給我們開發單片機設備有很多的啟發。

筆者願分享這些給諸君。如有錯誤,請諸君提出,我好學習改正!

今天是主日,願諸君平安!

推薦閱讀:

相關文章