電子產(chǎn)業(yè)一站式賦能平臺

PCB聯(lián)盟網(wǎng)

搜索
查看: 33|回復: 0
收起左側(cè)

從屎山代碼到RTOS,老工程師的架構(gòu)進化史

[復制鏈接]

263

主題

263

帖子

1312

積分

三級會員

Rank: 3Rank: 3

積分
1312
跳轉(zhuǎn)到指定樓層
樓主
發(fā)表于 2024-8-28 20:20:00 | 只看該作者 |只看大圖 回帖獎勵 |倒序瀏覽 |閱讀模式
你點擊藍字關注,回復“入門資料”獲取單片機入門到高級開掛教程
開發(fā)板帶你入門,我們帶你飛

文 | 無際(微信:2777492857)
全文約5867字,閱讀大約需要 10-20 分鐘
很多老鐵和我反饋,說很喜歡看我寫的內(nèi)容,不管是朋友圈還是文章,能在字里行間中,受益匪淺。
想想也很久沒時間沒寫長文了,既然大家喜歡看,我盡量抽時間多寫。
長文預警,全文5800多字,寫了16個小時,如果你想真正深入了解哪些項目需要上RTOS?RTOS有哪些優(yōu)勢?具體能解決產(chǎn)品哪些痛點?請花十分鐘,耐心看完,相信這篇文章比你看一套教程更一針見血,如果你趕時間,建議先點贊+收藏防止找不到。。
前段時間,本來想錄新項目的教程,因為從去年研發(fā)出來,教程就一直爛尾了,沒想到還要繼續(xù)爛尾...   

后面我們評估了一下,想著把我們項目6跑個RTOS,給支持我們的老鐵,再安排一波彩蛋,會不會投產(chǎn)比更高一點?
答案是一定的,從幾個維度分析吧
1.如果學完我們項目現(xiàn)有的架構(gòu),比如任務管理、隊列、鏈表。再去學RTOS,簡直就是so esay! 很多思維,代碼和RTOS都是類似的,老鐵們需要花費的時間不多,但簡歷又能添金不少。
2.項目6這種級別的,上個RTOS,站在產(chǎn)品的角度,也合情合理,合理意味著更有說服力。
評估下來,大家都不用投入太多,但是產(chǎn)出高,說干就干,于是我們花了2個月左右,先是把freertos移植到項目6,然后又錄了教程。   

但今天不是來吹我們教程的,而是來解決很多小伙伴的一個疑問,就是RTOS能做什么項目?或者什么項目要上RTOS?

一、為什么要用RTOS?
想解決這個問題,就要追溯到RTOS能給項目帶來的好處,或者便利,大白話講就是RTOS有哪些功能優(yōu)勢?能具體解決產(chǎn)品哪些痛點?
1.解決項目架構(gòu)問題
RTOS是"Real-time Operating System"單詞的縮寫,對應的中文稱為"實時操作系統(tǒng)",目前市面的RTOS系統(tǒng)種類比較多,我們這里以FreeRTOS為例。
雖然我們推出了FreeRTOS的教程,但不意味著這是求職者的剛需技能,我和徐工都在一線干研發(fā)有10年以上,碰到上RTOS的項目,可以說不多。
一般復雜點的項目,用我們自己寫的那個架構(gòu)就能勝任,而且移植起來沒那么繁瑣,代碼也沒那么臃腫,該有的功能也有,比如任務管理,定時器矩陣,隊列。   





這個架構(gòu),下到51,上到STM32的項目,都能用,不過51單片機資源匱乏的,就別用了,省著點吧。   
而且這個架構(gòu)經(jīng)過了很多批量產(chǎn)品的驗證,擴展性、移植性都穩(wěn)的一批。
可以說,如果你自己能寫出這種架構(gòu),企業(yè)對你的認可度,比你只會用RTOS高多了,因為這背后意味著你的編程功底很扎實。
也非常榮幸受到了很多老鐵認可,隨機截了幾張圖。   

這個架構(gòu)教程也是開放的,可以找我安排,也可以自己去看。   

   
哈哈,不吹水了,這不是一篇GG文。。!
回到RTOS,我個人認為,RTOS本質(zhì)上,就是一個高實時性的程序架構(gòu),解決你項目程序"地基"的痛點,很多工程師沒能力自己設計穩(wěn)定的架構(gòu),就用這種現(xiàn)成的。
但是有能力的,都是習慣用自己寫的架構(gòu),簡單輕便,而且程序100%可控,像實時性這個問題,寫程序時規(guī)避好,實時性基本和RTOS沒啥區(qū)別,至少產(chǎn)品功能沒影響,畢竟RTOS用不熟,也會埋雷的。
架構(gòu)穩(wěn)了,你在此基礎上"搭積木",才不會造成系統(tǒng)性的程序崩塌風險,這對于復雜的項目特別重要,可以說沒好的架構(gòu),項目基本上很難做穩(wěn)定,哪怕能做穩(wěn)定,后期維護成本也巨高,比如改個功能,移植到更便宜的芯片啥的,砸鍵盤的心都有。

2.項目代碼的模塊化管理
一個成熟的架構(gòu),除了支持不同的內(nèi)核和開發(fā)平臺,像模塊化這些,都是基本操作,我們根據(jù)不同的應用和功能創(chuàng)建不同的任務,這樣就相當于你的項目增加了一個老板,可以很輕松地指揮哪個任務執(zhí)行,什么時候執(zhí)行,什么頻率執(zhí)行等等....
在單片機程序開發(fā)時,大多數(shù)工程師都是從while(1)死循環(huán)開始,將一堆功能代碼都對接到while循環(huán)里面運行,包括我自己也是這樣過來的。
以下是我10年前的代碼截圖,這種方式的缺點是不好控制每個任務的執(zhí)行/不執(zhí)行,以什么頻率執(zhí)行。

專業(yè)術語來說,這種就是屎山代碼。
后面隨著開發(fā)經(jīng)驗豐富起來,不斷優(yōu)化架構(gòu)以后,現(xiàn)在做項目,變成了如下代碼:

咋一看,是不是以為加了RTOS系統(tǒng)?     
其實沒有,只是借鑒了系統(tǒng)的編程思路,把傳統(tǒng)的while(1)結(jié)構(gòu)進行了代碼封裝,本質(zhì)還是輪詢的機制。
輪詢機制的弊端是,無法做到任務搶占式,就是任務插隊,這樣的話,對于沒有經(jīng)驗的工程師,可能會因為某些任務代碼時間過長,而影響整個系統(tǒng)的實時性。
如果有豐富經(jīng)驗,能規(guī)避掉延時高的代碼,說出來你可能不信,這個架構(gòu)比RTOS好用不是億點點...
以下是我們無際單片機項目6增加了FreeRTOS后的代碼:

下面以這個項目為例,給大家一一講解下FreeRTOS功能的實際應用場景。
比如任務的搶占式調(diào)度,消息隊列,內(nèi)存動態(tài)分配,軟件定時器....等功能。
這些功能,幾乎每個都能解決某些特定應用場景的痛點,而且這些功能,我們只需要通過調(diào)用現(xiàn)成的API函數(shù),就可以實現(xiàn),極大減輕了工程師的負擔。
         
二、RTOS提供的功能以及應用場景
1.任務的搶占式調(diào)度
FreeRtos的任務調(diào)度支持搶占式調(diào)度,這也是它的優(yōu)勢之一。
FreeRtos系統(tǒng)創(chuàng)建的每個任務都有優(yōu)先級參數(shù)的配置,搶占式調(diào)度就是基于優(yōu)先級來實現(xiàn)的,如下圖,以項目6創(chuàng)建的4個任務為例,其中硬件處理層任務的優(yōu)先級最高(優(yōu)先級為5),數(shù)字越大優(yōu)先級越高。

在項目6中,對實時性要求比較高的功能代碼,基本都在硬件數(shù)據(jù)采集這塊,例如Lora數(shù)據(jù)收發(fā),如果有l(wèi)ora門磁探測器觸發(fā)了,要第一時間收集數(shù)據(jù),然后觸發(fā)主機報警,這是比較緊急的任務,因為我們項目的核心功能就是防盜報警。
硬件層還有其它,比如串口數(shù)據(jù)接收,這個是連接wifi和4G,優(yōu)先級也需要高一些,當主機報警后能第一時間把數(shù)據(jù)上傳到我們云平臺,以及給用戶撥打警示電話。



剩下的就是PWM輸入定時器捕獲,PWM輸出等等了...     
所以,這個優(yōu)先級怎么去規(guī)劃,一般是按照項目的功能來的,需要緊急處理的功能邏輯,往往優(yōu)先級設置最高。  

2.消息隊列
消息隊列是什么?有什么用?
消息隊列主要是用來處理單片機大數(shù)據(jù)采集和處理的,比較經(jīng)典的使用場景就是串口數(shù)據(jù)接收和解析。
FreeRTOS隊列和我們自己架構(gòu)的隊列,原理都是大同小異的。
這里我們以項目6的Lora模塊無線數(shù)據(jù)的接收為例,來介紹FreeRTOS消息隊列的應用。
Lora模塊我們接的是單片機串口。
如下截圖,紅色標注的是用來接收Lora接收數(shù)據(jù)消息隊列的創(chuàng)建。

  首先要按照消息隊列的格式來定義消息隊列的名稱和大小,然后在Lora初始化程序中通過API函數(shù)創(chuàng)建消息隊列。
創(chuàng)建完成后,消息隊列的返回值要賦值給消息隊列的句柄(句柄也就是我們定義的消息隊列的名稱),數(shù)據(jù)的入列和出列就是通過句柄來操作的。
消息隊列創(chuàng)建完成之后,就是消息隊列的使用,包括消息隊列的入列和出列。
如下圖,對應的就是Lora接收數(shù)據(jù)消息隊列的入列和出列的過程。
把Lora接收到的數(shù)據(jù)入列處理截圖:

Lora接收到的數(shù)據(jù)出列處理代碼截圖:

如果是我們無際單片機項目特訓營的老鐵,可能會好奇,為什么入列,出列函數(shù)的名字,和我們自己架構(gòu)是一樣的?   
答案是為了兼容,我們對API函數(shù)名稱做了重定義,因為我們是在我們架構(gòu)的基礎上,改的RTOS,方便大家學完我們架構(gòu)無縫過渡RTOS。
上面的截圖只是說明了消息隊列的過程,關于Lora數(shù)據(jù)處理的過程遠比這個要復雜,文章篇幅有限,這里就不介紹了
除了Lora,還有我們OTA升級的功能,這種大數(shù)據(jù)流的,用隊列也能事半功倍。
隊列的本質(zhì),就是可以先把數(shù)據(jù)存起來(比如存在數(shù)組里),等用到了再取,理解這個本質(zhì),就好拋磚引玉了。
不過要想實現(xiàn)這個功能,就要把數(shù)據(jù)進行一系列的算法處理,才能實現(xiàn),比如說存數(shù)據(jù)和取數(shù)據(jù),可能每次位置都不一樣。   
這種應用場景很多,比如任務之間的數(shù)據(jù)傳遞,是不是也可以?既然需求多,是不是可以寫成一套模版程序呢?下次就不用重復造輪子了,所以有了入列、出列這類函數(shù)。


3.內(nèi)存動態(tài)分配,提升單片機RAM的使用率
在產(chǎn)品開發(fā)中,我們經(jīng)常會碰到一個問題,如果沒有動態(tài)內(nèi)存分配,那么定義的全局變量/數(shù)組只能用于有一個特定的功能,這樣會導致RAM資源的浪費。
舉例來說,在我們項目6網(wǎng)關的探測器列表界面下,要顯示這個網(wǎng)關所有配對好的探測器,探測器數(shù)量一般是動態(tài)的,可能用戶A配對了5個探測器,用戶B配對了10個,用戶C配對了20個.....
在具體的代碼中,我們程序定義了兩個靜態(tài)結(jié)構(gòu)體數(shù)組用來實現(xiàn)探測器列表的功能。

這兩個靜態(tài)的結(jié)構(gòu)體數(shù)組占用了比較大的RAM空間,假如客戶突然發(fā)神經(jīng),要求這臺主機要支持配對100個探測器,這個結(jié)構(gòu)體數(shù)組大小就是100,這得多嚇人,光這個結(jié)構(gòu)數(shù)組就把單片機的RAM用得差不多了。
我們不妨計算以下:   

這里以StuDTCtemp結(jié)構(gòu)體數(shù)組為例,結(jié)構(gòu)體數(shù)組的元素類型Stu_DTC,通過 sizeof(Stu_DTC)計算,占用52個字節(jié),假設最多支持20個探測器,再乘以數(shù)組元素的個數(shù)20,共1040個字節(jié);
這1040個字節(jié)RAM空間被顯示探測器列表界面長期占用,造成了ARM空間的浪費。
這里我們只計算了其中一個結(jié)構(gòu)體數(shù)組,如果上圖兩個結(jié)構(gòu)體數(shù)組占用的空間加起來更大。
而FreeRTOS提供的內(nèi)存動態(tài)分配就可以很好的解決這個問題,動態(tài)內(nèi)存分配是指在程序運行時,根據(jù)需要動態(tài)地分配和釋放內(nèi)存。這種方式可以更大限度的利用RAM空間。
應用內(nèi)存動態(tài)分配,優(yōu)化后的代碼如下;   

這樣就可以實現(xiàn)在需要顯示探測器列表的時候,申請已配對探測器需要用到的內(nèi)存大小,退出當前界面的時候,釋放內(nèi)存。
內(nèi)存動態(tài)分配可以提高RAM的使用率,但會如上圖,內(nèi)存動態(tài)分配需要和結(jié)構(gòu)體指針配合使用,這樣就會增加代碼的復雜度,如果使用不當,會造成內(nèi)存的泄漏、碎片化等問題。因此內(nèi)存動態(tài)分配,我們主要使用在占用RAM空間比較大的變量中。
如果沒有RTOS的話,就要自己手擼內(nèi)存管理代碼,我們就手擼過,相當痛苦,調(diào)了3天3夜。
下圖是我們原來寫的內(nèi)存管理代碼,解決了碎片化的問題,本來想在項目中加入,但是考慮到復雜程度過高,怕無際單片機項目特訓營老鐵學得嗷嗷叫,就放棄了。            
下面展示下源碼,內(nèi)存分配:

         
內(nèi)存釋放:

這個源碼,是有放到項目3資料包里的,特訓營需要的老鐵可以自己去研究。

4.時間管理和Delay延時處理
在嵌入式開發(fā)過程中,我們經(jīng)常會碰到一些喪心病狂的延時需求,舉個例子,LED燈每5秒快閃3次,按鍵消抖,單口通訊的時序控制等等....
用死循環(huán)延時函數(shù)這種方式肯定是不行的,會長時間占用CPU資源,導致無法及時響應其它任務。
為了解決這個問題,我們采取了很多方法,如使用定時器或狀態(tài)機等。
盡管這些方法都能解決延時問題,但它們在可擴展性和移植性,以及規(guī)范性方面都存在很多不足,就是換一個項目不通用。
所以,F(xiàn)reeRTOS提供的阻塞式延時函數(shù)vTaskDelay(); vTaskDelay()就解決了這些痛點。
函數(shù)是以系統(tǒng)滴答(SysTick)為單位實現(xiàn)延時的,通常是1ms,例如vTaskDelay(1000),就表示1000ms的延時。
什么是阻塞延時?
阻塞延時就是在延時計時過程中,單片機的CPU可以處理其他任務或代碼,當計時時間到了之后,F(xiàn)reeRtos會自動返回任務并執(zhí)行下一條指令,這種方式既節(jié)省了CPU資源,又提高了系統(tǒng)的響應性。
可以看以下示例,在FreeRtosTask1函數(shù)代碼中,使用vTaskDelay(1000)實現(xiàn)LED1間隔1秒翻轉(zhuǎn),在FreeRtos實現(xiàn)1秒的延時過程中,CPU能夠處理其他任務。   


5.軟件定時器功能
除了FreeRTOS提供的延時函數(shù)外,F(xiàn)reeRTOS也提供了軟件定時器功能,通過軟件定時器也可以實現(xiàn)上面的功能,這個跟我們自己架構(gòu)的定時器矩陣功能原理差不多。
FreeRTOS軟件定時器是一種純軟件實現(xiàn)的定時器,它利用系統(tǒng)的時基(通常是系統(tǒng)滴答中斷),來模擬定時器的功能,而不需要占用額外的硬件資源。
我們以項目6中通過軟件定時器實現(xiàn)LED7間隔500ms翻轉(zhuǎn),來簡單的了解FreeRTOS的軟件定時器功能。

當然,軟件定時器雖然不占用硬件定時器資源,但也有一些缺點,由于它是基于系統(tǒng)時基實現(xiàn)的,定時精度受限于系統(tǒng)時基的頻率,無法達到硬件定時器那樣的高精度。
還有軟件定時器的執(zhí)行需要消耗一定的CPU時間,如果定時器數(shù)量過多或者定時頻率過高,可能會對系統(tǒng)的實時性產(chǎn)生影響。
以上知識列舉了FreeRTOS的一些經(jīng)典功能,除了以上還有信號量,任務通知,事件標志組等功能,由于篇幅有限,這就就給大家不一一列舉了。

三、有哪些項目要用RTOS?
雖然RTOS功能非常強大,但并不是所有的單片機項目都適合,尤其是資源比較匱乏的51內(nèi)核單片機。
FreeRTOS系統(tǒng)內(nèi)核代碼大概需要占用7-10K的Flash空間,如果單片機的Flash空閑小于64K,就不推薦使用。   
其次就是RAM空間的消耗,如果要用到消息隊列,內(nèi)存分配,建議RAM空間在20K以上, RAM空間太小,無法發(fā)揮FreeRTOS系統(tǒng)的優(yōu)勢。
上面是從單片機硬件資源上的建議,站在項目的角度,F(xiàn)reeRTOS比較適合中大型項目,例如我們項目6這種,項目功能比較復雜,涉及的模塊比較多,這樣就可以充分的體現(xiàn)它的優(yōu)勢,包括不同模塊任務的管理,任務之間消息的傳遞,內(nèi)存的動態(tài)分配等。
在老工程師眼里,一般流傳一句話:"能不用RTOS,就不用",特別是跑量的產(chǎn)品,代碼越好理解,越簡單越好。
一方面是在使用FreeRTOS系統(tǒng)的時候,需要有比較扎實的編程基礎,包括回調(diào)函數(shù),函數(shù)指針,結(jié)構(gòu)體數(shù)組,鉤子函數(shù),臨界保護等。
如果沒有扎實的編程基礎的話,很難游刃有余地駕馭該系統(tǒng),如果開發(fā)中出現(xiàn)了奇怪的問題,就無法快速鎖定并解決問題。
因此建議大家剛開始學習單片機的時候,沒有扎實的編程基礎和經(jīng)驗,謹慎使用FreeRtos系統(tǒng)。

end


下面是更多無際原創(chuàng)的個人成長經(jīng)歷、行業(yè)經(jīng)驗、技術干貨。
1.電子工程師是怎樣的成長之路?10年5000字總結(jié)
2.如何快速看懂別人的代碼和思維
3.單片機開發(fā)項目全局變量太多怎么管理?
4.C語言開發(fā)單片機為什么大多數(shù)都采用全局變量的形式?
5.單片機怎么實現(xiàn)模塊化編程?實用程度讓人發(fā)指!
6.c語言回調(diào)函數(shù)的使用及實際作用詳解

7.手把手教你c語言隊列實現(xiàn)代碼,通俗易懂超詳細!

8.c語言指針用法詳解,通俗易懂超詳細!
回復

使用道具 舉報

發(fā)表回復

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則

關閉

站長推薦上一條 /1 下一條


聯(lián)系客服 關注微信 下載APP 返回頂部 返回列表