一、背景
前些年,為了給學(xué)習(xí)單片機編程的學(xué)生提供一個方便使用的控制素材,我設(shè)計了一個輪式驅(qū)動單元,其最大特點就是將電機驅(qū)動和碼盤反饋集成到一起,用幾個TTL電平線就可以驅(qū)動,和舵機的驅(qū)動類似,這樣使用時可以根據(jù)喜好、需要隨意選擇核心板驅(qū)動
可以用它構(gòu)建不同驅(qū)動方式的小車底盤,最簡單的一種驅(qū)動方式就是使用一個輪式驅(qū)動單元加一個舵機實現(xiàn)的“單輪驅(qū)動舵機轉(zhuǎn)向小車”,它的運動方式和現(xiàn)實世界中的電動叉車類似
因這種驅(qū)動方式小車的轉(zhuǎn)向和行走兩個主要控制元素相互獨立,從學(xué)習(xí)編程角度考慮,相對于兩輪差分驅(qū)動方式更為簡單、容易。
當時目標是降低學(xué)習(xí)門檻,故選了Arduino作為控制器。為便于接線,選擇了市場上的一種 Nano 擴展板,其外形和UNO一樣,只是將每個IO都配了一個地線和電源端,用杜邦線連接十分方便
后來覺得Arduino Nano控制器資源太少,考慮改用主流的 STM32F103C8T6核心板,但Nano擴展板的構(gòu)思不錯,邊參考它自己設(shè)計了一塊 STM32F103C8核心板的擴展板
由于大環(huán)境影響,國產(chǎn)OS被重視,在RTOS領(lǐng)域,RT-Thread無疑是國產(chǎn)系統(tǒng)中的佼佼者。故萌生了在小車平臺上嘗試一下的念頭,既然有可以方便使用的STM32F103C8擴展板,又有和STM32F103C8核心板兼容的STM32F411CE核心板,小車的控制也應(yīng)該提升一下,跑跑我們自己的RTOS。
下面將完整地記錄實施過程,期望能對想選用 RT-Thread 開發(fā)產(chǎn)品的朋友有幫助,因為小車控制相對實時性要求較高,有真實的多任務(wù)需求,不同于HMI(人機界面)類應(yīng)用,用事件驅(qū)動即可,沒有那么強的實時性、并發(fā)性要求。
二、目標
按實時多任務(wù)的思路,基于 RT-Thread,完成小車驅(qū)動,并可以通過串口(藍牙透傳)操縱小車運動。
因為RT-Thread 的特色是有豐富的組件和軟件包可以擴展,但要享用這些,必須是標準版;如果用Nano版,和FreeRTOS區(qū)別不大。故選擇RT-Thread標準版。
三、實施過程
3.1 總體構(gòu)思及任務(wù)設(shè)計
我做項目通常是先構(gòu)思程序框架,將所需完成的功能合理拆解,根據(jù)功能確定框架。
選用成熟的RTOS,只是有了多任務(wù)實現(xiàn)的手段和工具,程序框架還是需要自己設(shè)計。
對于基于RTOS的程序而言,首先要設(shè)計的就是任務(wù)(在RT-Thread中為線程),根據(jù)RTOS所提供的工具,結(jié)合自己想要實現(xiàn)的功能,合理劃分任務(wù),創(chuàng)建相應(yīng)的線程,并確定線程之間的交互方式及內(nèi)容,從而完成一個基于RTOS的多任務(wù)控制程序。
任務(wù)劃分的首要目標是相對獨立,即所構(gòu)建的任務(wù)是完整的,有清晰的輸入消息和輸出結(jié)果,其工作只是對輸入進行相應(yīng)處理,給出輸出,中間過程不受其它因素所牽制。
其次是任務(wù)可一次完成,中間沒有長時間的等待操作,任務(wù)通常是處在等待輸入的狀態(tài),收到輸入后,即刻完成對輸入的處理,輸出結(jié)果,之后再次回到等待輸入狀態(tài)。
這樣設(shè)計主要是因為:所謂多任務(wù)操作,表面上看每個任務(wù)都在連續(xù)執(zhí)行,實質(zhì)上MCU還是分時處理各個任務(wù),只是通過RTOS在后臺切換。
RTOS調(diào)度的方式?jīng)Q定了各個任務(wù)的分配時間和響應(yīng)速度,各家RTOS的調(diào)度方式雖有不同,但都遵循一個原則:即處于等待狀態(tài)的任務(wù)不分配運行時間,這樣才能使程序達到最佳效率。
使用RTOS的等候消息函數(shù),就是告知OS,我目前“沒事”,在等新的消息。
基于這種方式構(gòu)建任務(wù)除了保證程序的執(zhí)行效率和實時性外,還便于調(diào)試??扇藶樽⑷胂⒂|發(fā)任務(wù)執(zhí)行,觀察輸出結(jié)果是否符合設(shè)計即可。而且可以用多模塊方式,一個任務(wù)一個模塊,由多人分別編寫、合作完成。
基于RTOS編程,考慮到其多任務(wù)的特點,通常是先做一個基礎(chǔ)框架,再根據(jù)不同的場景設(shè)計相應(yīng)的任務(wù)。
所謂“基礎(chǔ)框架”,就是結(jié)合單片機應(yīng)用的特點,將一些通用需求納入,這樣在每次做新的項目時以此為基礎(chǔ),增加新的功能即可。
基礎(chǔ)框架包含:
1)串口命令接收:作為人機交互通道,代替以往通過按鍵實現(xiàn)的程序操控手段。操作命令的產(chǎn)生可以用PC、手機之類有豐富交互手段的設(shè)備,比實體按鍵更為靈活、直觀、豐富。
2)串口數(shù)據(jù)發(fā)送:作為人機交互通道,代替以往通過顯示器實現(xiàn)的信息輸出,同樣可以用PC、手機之類的設(shè)備接收后顯示,界面設(shè)計遠比顯示屏靈活、豐富、隨心所欲。之所以將收、發(fā)分開,是考慮到輸出信息需要服務(wù)于所有任務(wù),以便功能設(shè)計更為合理。
3)調(diào)試信息輸出任務(wù):在沒有或不能使用IDE調(diào)試手段時,需要在程序中輸出相應(yīng)的調(diào)試信息,以實現(xiàn) Debug ;不過,RT-Thread 內(nèi)置Finsh 十分完善,故選用RT-Thread則不再需要設(shè)計此任務(wù),這是 RT-Thread 優(yōu)點之一。
4)看護任務(wù):利用RTOS的信息交互機制,周期性地與各任務(wù)交換信息,當出現(xiàn)不應(yīng)答時,說明對應(yīng)的任務(wù)工作異常,可以做相應(yīng)的操作。這樣處理無需復(fù)位,導(dǎo)致其它任務(wù)被非正常中斷,增加了程序的可靠性。即便不處理,也能利用調(diào)試信息輸出及時發(fā)現(xiàn)是哪個任務(wù)異常,以便消除隱患。
5)主應(yīng)用任務(wù):前面幾個任務(wù)屬于框架的基礎(chǔ)任務(wù),未涉及程序需要執(zhí)行的實際功能,主應(yīng)用任務(wù)就是實現(xiàn)具體功能的核心。設(shè)計主應(yīng)用任務(wù)是考慮到一般來說,程序均有一個核心的部分,用于管理、協(xié)調(diào)一些子功能,使得程序運行有序;它相當于一個管理者。這個在抽象的框架設(shè)計時只完成了信息交互,在具體到特定的需求時再完善設(shè)計。
6)其它任務(wù):相當于執(zhí)行者,完成特定的功能。同上,這個任務(wù)在框架設(shè)計時只是完成和主應(yīng)用任務(wù)的信息交互,具體實施時再詳細設(shè)計。
在本項目實施中,需要完成的功能如下:
1、接受串口操作命令,解析并執(zhí)行。具體而言就是2個命令:按指定速度前進或后退指定距離、轉(zhuǎn)向角度。
2、電機驅(qū)動,實現(xiàn)調(diào)速和走指定距離。
3、舵機驅(qū)動,完成指定轉(zhuǎn)向角度。
據(jù)此,考慮設(shè)置兩個執(zhí)行任務(wù):電機驅(qū)動、舵機驅(qū)動,以及一個主應(yīng)用任務(wù),負責(zé)接受、解析操作命令,將相應(yīng)操作信息發(fā)送給相應(yīng)的執(zhí)行任務(wù)。
舵機的操作方式按道理無需單設(shè)為一個任務(wù),因為它自身閉環(huán),無需程序去處理反饋和修正。但考慮到邏輯上的獨立性,以及未來用多個舵機和輪式驅(qū)動單元構(gòu)成的全向小車驅(qū)動,舵機轉(zhuǎn)向構(gòu)建為一個獨立的任務(wù)有助于程序的可維護性和可擴展性。
構(gòu)建如下應(yīng)用任務(wù):
1)主應(yīng)用任務(wù):解析所設(shè)計的操作命令,將命令參數(shù)提取后發(fā)送給相應(yīng)的執(zhí)行任務(wù),并應(yīng)答。
2)電機驅(qū)動:接收執(zhí)行參數(shù)實現(xiàn)PWM電機驅(qū)動,并根據(jù)碼盤反饋實現(xiàn)調(diào)速和行走距離控制,反饋當前運行狀態(tài)給主應(yīng)用任務(wù)。
3)舵機驅(qū)動:接收執(zhí)行參數(shù),執(zhí)行舵機操作;基于舵機特性,反饋舵機當前狀態(tài)(正在運行、已到位)。
3.2 任務(wù)(線程)間交互設(shè)計
一個任務(wù)常常需要接收多個消息,而且是來自不同的任務(wù);為了實現(xiàn)將這些消息通過一個等待函數(shù)獲取,RTOS提供了一個方便的手段:事件組;即將若干消息匯總在一起,一個消息對事件的一位;而且等待的方式也很靈活,可以是“與”的關(guān)系,即所關(guān)注的各個消息都出現(xiàn)才觸發(fā);也可以是“或”的關(guān)系,即出現(xiàn)任意一個消息就觸發(fā)。
事件組雖然解決了等待多個消息的問題,但所傳遞的內(nèi)容往往不夠,一個消息有時包含許多參數(shù),如通訊命令的內(nèi)容。
為傳遞消息內(nèi)容,選擇了RTOS的另一個工具:郵箱。
郵箱只傳送一個字(4字節(jié)),很多時候也不夠,我習(xí)慣于用郵箱傳遞存放數(shù)據(jù)的指針,將要傳送的消息定義為一個數(shù)據(jù)結(jié)構(gòu),通過指針傳送,這樣比較靈活。
曾經(jīng)用過隊列傳輸,但需要預(yù)先確定隊列項長度,很難一次規(guī)劃到位,后期修改比較麻煩,故改用郵箱。
通過事件組和郵箱的配合,基本上可以實現(xiàn)任務(wù)間的消息交互。
3.3 任務(wù)(線程)詳細設(shè)計
3.3.1 串口接收任務(wù)
串口是作為單片機的操作輸入通道,取代傳統(tǒng)模式下的按鍵操作。通過串口命令操作單片機遠比設(shè)計實體按鍵靈活方便,而且硬件資源占用也少。
此任務(wù)要實現(xiàn)對串口命令的接收和初步解析,將命令內(nèi)容轉(zhuǎn)發(fā)給主應(yīng)用任務(wù)處理。
核心是能可靠的監(jiān)測和接收符合通訊協(xié)議的命令幀。
通訊協(xié)議的定義也是串口命令的重點,要考慮的簡潔性和可擴展性的平衡,同時要兼顧應(yīng)用場景的需求。
目前協(xié)議是參考 ROS (機器人操作系統(tǒng))中的ROS Serial 協(xié)議設(shè)計的。因為小車有無線通訊的需求,故在協(xié)議中有相應(yīng)的通訊地址,以便在一個通道上實現(xiàn)多機通訊。
串口通訊協(xié)議如下:
字符格式: 115200 8 N 1
幀格式:(借鑒 ROS 的 ROS Serial 協(xié)議)
0xFF 0xFE(2字節(jié)幀同步字) 幀長L 幀長H 幀長校驗和 目標地址 源地址 幀數(shù)據(jù)區(qū) 幀校驗和
其中:
幀同步字 —— 2字節(jié)特征字,暫定為0xFF 0xFE,借鑒的ROS Serial協(xié)議。
幀長 —— 幀數(shù)據(jù)區(qū)數(shù)據(jù)字節(jié)數(shù),不含幀校驗和、目標地址和源地址;先低后高,最大支持65535字節(jié),實際不一定需要,但有可能超過256字節(jié),所以用2字節(jié)。
幀長校驗和 —— 2字節(jié)幀長算數(shù)和取反,取最低字節(jié)。借鑒ROS Serial協(xié)議
目標地址 —— 接收方的通訊地址, 1字節(jié)。
源地址 —— 發(fā)送此幀的通訊地址,1字節(jié),應(yīng)答時使用。
幀數(shù)據(jù)區(qū) —— 通訊數(shù)據(jù),字節(jié)數(shù)為幀長
幀校驗和 —— 數(shù)據(jù)區(qū)所有字節(jié)的算數(shù)和取反,取最低字節(jié)。如果數(shù)據(jù)長度為0,則CS為0xFF。
按此設(shè)計,最短的幀為 8字節(jié),數(shù)據(jù)區(qū)無數(shù)據(jù),可作為心跳幀或命令應(yīng)答幀。
幀的傳輸方向由兩個地址確定,無需再設(shè)計上、下行(應(yīng)答幀)標志。
幀數(shù)據(jù)區(qū)定義如下:
Key: 操作命令,1字節(jié)
Len: 數(shù)據(jù)長度,2字節(jié),先低后高,單位 - 字節(jié)
Val[Len] :N 字節(jié)數(shù)據(jù)
至于 Val的數(shù)據(jù)如何定義,取決于 Key,可以定義為結(jié)構(gòu)、數(shù)據(jù)、或者更復(fù)雜的數(shù)據(jù),也可以簡單的定義為字節(jié)、字、整形。
串口數(shù)據(jù)幀的可靠接收源于可靠的幀提取方式,因為有可能使用無線通訊(串口接無線透傳模塊即可),就存在串口接收到的數(shù)據(jù)并非都是有效的、應(yīng)該收的,需要從接收的數(shù)據(jù)流中檢出發(fā)給自己的數(shù)據(jù)幀,不能根據(jù)數(shù)據(jù)絕對位置提取。
對于從連續(xù)數(shù)據(jù)流中檢出一段符合要求的數(shù)據(jù),使用滑窗比較方式較為可靠。
由于上述協(xié)議定義的是變長幀,無法對整個數(shù)據(jù)幀進行滑窗比較,只能基于協(xié)議,找到幀頭的特征后,再對整個數(shù)據(jù)幀接收,之后再通過校驗判斷此幀是否正確。
此處所用的幀頭特征為:同步字、幀長格式(2字節(jié)+校驗)、目標地址。
除數(shù)據(jù)幀的可靠接收外,在串口命令接收任務(wù)中,還設(shè)計了兩個操作命令:
讀內(nèi)存、寫內(nèi)存
目的是為了在小車運行過程中檢測特定變量的值,從而發(fā)現(xiàn)程序出現(xiàn)的問題,類似于在IDE環(huán)境下設(shè)置斷點,停下程序后檢查內(nèi)存變量值。
這種方式可以在程序運行中實現(xiàn),不影響程序運行,更為真實。
將需要監(jiān)測的變量設(shè)計為全局變量或靜態(tài)變量,在編譯產(chǎn)生的 map 文件中可以查到相應(yīng)的地址,通過讀內(nèi)存操作可隨時觀察變量的變化。
由于STM32的內(nèi)存是線性的,其RAM、ROM、硬件工作寄存器均在一個地址空間,因此還可以通過讀內(nèi)存功能監(jiān)測MCU相應(yīng)硬件的工作狀態(tài)(讀取相應(yīng)寄存器值),以確定初始化是否正常,運行是否正確。
這個功能作為調(diào)試信息輸出的補充,可以使調(diào)試手段更加豐富。調(diào)試信息輸出需要預(yù)先嵌入一段代碼,而讀內(nèi)存操作可以隨時使用,只要對象不是動態(tài)變量。
寫內(nèi)存功能也是作為調(diào)試手段的補充,可以通過串口命令修改程序中的相應(yīng)變量,從而激勵程序執(zhí)行所需的操作。
讀、寫命令定義如下:
命令1(0x01):讀內(nèi)存操作
0x01 0x05 0x00 數(shù)據(jù)地址(4byte,L-H)讀字節(jié)數(shù)(1byte)
讀取從數(shù)據(jù)地址開始的N字節(jié)數(shù)據(jù),用于調(diào)試及故障遠程診斷。
應(yīng)答內(nèi)容:
0x01 lenL lenH 數(shù)據(jù)地址(4byte,L-H)讀字節(jié)數(shù)(1byte)數(shù)據(jù)(N字節(jié))
數(shù)據(jù)長度len為 讀字節(jié)數(shù)+5
命令2(0x02):寫內(nèi)存數(shù)據(jù)
0x02 lenL lenH 數(shù)據(jù)地址(4byte,L-H)寫字節(jié)數(shù)(1byte)數(shù)據(jù)(N字節(jié))
從數(shù)據(jù)地址開始寫 N 字節(jié)數(shù)據(jù),用于調(diào)試,及臨時性的參數(shù)設(shè)置,需要保護,以免引起程序崩潰。
應(yīng)答內(nèi)容:
0x02 0x05 0x00 數(shù)據(jù)地址(4byte,L-H)實際寫字節(jié)數(shù)(1byte)
如果寫失?。ㄋ鶎懙刂凡辉谠试S范圍內(nèi)容,或長度超過設(shè)定),則實際寫字節(jié)數(shù)為 0。
3.3.2串口發(fā)送任務(wù)
在傳統(tǒng)的單片機系統(tǒng)中,通常會設(shè)計顯示屏、至少是LED數(shù)碼管作為信息輸出手段;但目前多數(shù)單片機系統(tǒng)已不需要這樣設(shè)計,通過串口輸出信息,使用PC、手機這類顯示功能完善的設(shè)備作為單片機系統(tǒng)信息輸出的呈現(xiàn)手段,比LED數(shù)碼管、LCD屏更為直觀、靈活、美觀,而且占用硬件資源極少。
因程序框架是多任務(wù)方式,理論上各個任務(wù)都有輸出信息的需求,故將串口發(fā)送功能獨立設(shè)計為一個任務(wù),可以服務(wù)于所有任務(wù)。
為減少內(nèi)存消耗,發(fā)送數(shù)據(jù)傳遞消息只傳輸存放指針,發(fā)送任務(wù)根據(jù)數(shù)據(jù)結(jié)構(gòu)定義,取出要發(fā)送的數(shù)據(jù)。串口發(fā)送速度和內(nèi)存操作相比慢很多,要發(fā)送的數(shù)據(jù)放置在各自任務(wù)中,在每次需要發(fā)送前,需要確定上次數(shù)據(jù)是否取走,以避免數(shù)據(jù)覆蓋,導(dǎo)致發(fā)送數(shù)據(jù)錯誤。
3.3.3看護任務(wù)
看護任務(wù)的設(shè)計目前只是示意性的,沒有實質(zhì)的恢復(fù)處理。因為恢復(fù)處理需要根據(jù)具體功能確定,沒有統(tǒng)一的方法。
但編好一個處理框架,后續(xù)如果需要增加相應(yīng)的處理會方便一些。
作為看護任務(wù),除了通過和各任務(wù)交互,以確定任務(wù)是否在正常運行外,還順帶完成了運行指示功能。
多數(shù)單片機系統(tǒng)雖無需顯示器,但工作狀態(tài)指示通常都有,可直觀的反映系統(tǒng)是否在正常運行,一般是通過LED的閃爍變化呈現(xiàn)。
此處參考FreeRTOS的異常指示方式設(shè)計了LED顯示功能,正常時,LED等間隔閃爍,當發(fā)現(xiàn)某個任務(wù)異常時,按任務(wù)順序會出現(xiàn)間斷閃爍。具體方式為:
將一個完整的顯示周期定為10次閃爍,正常時一個周期閃爍10次。
如果是1號任務(wù)異常,則一個周期閃爍1次,其余9次對應(yīng)暗狀態(tài);如果是2號,一個周期閃2次、暗8次,以此類推,最多可以支持9個任務(wù)的異常指示。
3.3.4 主應(yīng)用任務(wù)
此處主應(yīng)用任務(wù)(后面簡稱:主任務(wù))完成:
1)解析串口接收任務(wù)發(fā)來的操作命令,根據(jù)命令將參數(shù)發(fā)給電機驅(qū)動和舵機驅(qū)動任務(wù)。
2)定時讀取電機和舵機工作狀態(tài),以便反饋給操作者。
根據(jù)前述設(shè)計目標,定義操作命令如下:
A) 命令3:讀工作狀態(tài)命令(Key = 3,Len = 0)
0x03 0x00 0x00
應(yīng)答內(nèi)容:
0x03 0x09 0x00 電機工作參數(shù)(2字節(jié))電機運行狀態(tài)(2字節(jié))電機供電電壓(1字節(jié))電機供電電流(2字節(jié))舵機操作角度(1字節(jié))舵機當前狀態(tài)(1字節(jié))
電機工作參數(shù):PWM值或速度值
電機運行狀態(tài):剩余運行時間或距離
舵機操作角度:-90 ~ +90
舵機當前狀態(tài):運行、到位兩個狀態(tài)
B) 命令4:PWM方式定時運行(Key = 4,Len = 5,Val:2字節(jié)電機 PWM,2字節(jié)運行時間,1字節(jié)舵機操作角度)
0x04 0x05 0x00 電機PWM(2字節(jié)) 2字節(jié)運行時間 1字節(jié)舵機角度
電機PWM:為 2 字節(jié)有符號數(shù),-100% ~ 100%,正數(shù)前進,負數(shù)倒退,0 - 惰行,127 - 剎車,-128 - 無效PWM,不操作
運行時間:單位:秒
舵機角度:1字節(jié)有符號數(shù)。-90 ~ +90
應(yīng)答內(nèi)容:(同讀狀態(tài)命令)
C)命令5:PWM方式定距離運行(Key = 5,Len = 5,Val:2字節(jié)電機 PWM, 2字節(jié)運行距離,1字節(jié)舵機角度)
0x05 0x05 0x00 電機PWM(2字節(jié)) 運行距離(2字節(jié)) 舵機角度(1字節(jié))
電機PWM: (同上)
運行距離:2字節(jié)無符號數(shù),單位mm
舵機角度:同上
應(yīng)答內(nèi)容:(同讀狀態(tài)命令)
D)命令6:速度方式定時運行(Key = 6,Len = 5,Val:2字節(jié)運行速度, 2字節(jié)運行時間,1字節(jié)舵機角度)
0x06 0x05 0x00 運行速度(2字節(jié)) 運行時間(2字節(jié)) 舵機角度(1字節(jié))
電機速度: 2 字節(jié)有符號數(shù),單位:mm/s,正數(shù)前進,負數(shù)倒退,0 - 惰行,32767 - 剎車,-32768 - 無效速度,不操作
運行時間:2字節(jié)無符號數(shù),單位 秒
舵機角度:同前
應(yīng)答內(nèi)容:(同讀狀態(tài)命令)
E)命令7:速度方式定距離運行(Key = 6,Len = 5,Val:2字節(jié)運行速度, 2字節(jié)運行距離,1字節(jié)舵機角度)
0x06 0x05 0x00 運行速度(2字節(jié)) 運行距離(2字節(jié)) 舵機角度(1字節(jié))
電機速度:(同上)
運行距離:2字節(jié)無符號數(shù),單位 mm
舵機角度:同前
應(yīng)答內(nèi)容:(同讀狀態(tài)命令)
主任務(wù)從串口接收任務(wù)獲取上述操作命令,解析后,將相應(yīng)的工作參數(shù)轉(zhuǎn)發(fā)給電機驅(qū)動和舵機驅(qū)動任務(wù)。
讀狀態(tài)命令則由主任務(wù)完成,為提高響應(yīng)速度,兩個驅(qū)動任務(wù)定時將自身的工作狀態(tài)反饋給主任務(wù),主任務(wù)接收并保存,以便隨時響應(yīng)讀狀態(tài)命令。
主任務(wù)等待的消息為:串口任務(wù)發(fā)來的命令、電機驅(qū)動及舵機驅(qū)動反饋的狀態(tài)、看護任務(wù)發(fā)來的詢問。
輸出的信息為:發(fā)給電機驅(qū)動及舵機驅(qū)動的解析后命令參數(shù),發(fā)給串口發(fā)送任務(wù)的應(yīng)答信息。
3.3.5 電機驅(qū)動任務(wù)
任務(wù)需要完成:
1)PWM方式驅(qū)動電機
2)通過碼盤反饋實現(xiàn)測速
3)通過PID算法實現(xiàn)調(diào)速
4)通過碼盤反饋實現(xiàn)行走距離控制
5)通過計時器實現(xiàn)定時運行控制
輪式驅(qū)動單元的電機驅(qū)動設(shè)計了4種工作狀態(tài):
前進、后退、惰行、剎車。
前進、后退不難理解,惰行和剎車需要說明一下:
惰行 —— 是指在停止PWM信號后,電機驅(qū)動H橋使電機線圈處于開路狀態(tài),電機轉(zhuǎn)子會由于慣性,繼續(xù)轉(zhuǎn)動到機械阻力使其停止。
剎車 —— 是指在停止PWM信號后,電機驅(qū)動H橋使電機線圈處于短路狀態(tài),電機轉(zhuǎn)子由于慣性轉(zhuǎn)動產(chǎn)生的感應(yīng)電勢形成電流,產(chǎn)生阻力,和機械阻力共同作用使其停止。
在精確控制行走距離時,應(yīng)該運用剎車功能,降低慣性產(chǎn)生的誤差。
平時待命狀態(tài),使其處于惰行狀態(tài),可以輕松的用手轉(zhuǎn)動輪子。
為了對應(yīng)這兩種狀態(tài),在PWM命令參數(shù)中增加了剎車、惰行。
硬件上電機由3根信號線控制,2根控制線實現(xiàn)電機的四個狀態(tài),一根PWM線控制電機功率。需要啟用RTT的PIN組件及PWM組件。
輪式驅(qū)動單元設(shè)計是作為編程學(xué)習(xí)素材,故刻意降低成本,碼盤是用輪轂上的齒實現(xiàn),分辨率較低,為達到測速所需的精度,在算法上做了優(yōu)化(增加了編程的挑戰(zhàn))。
基于正常轉(zhuǎn)動的特征:相鄰兩個脈沖的周期不會突變(排除干擾造成的脈沖丟失、抖動等異常狀況)。
擬用計數(shù)和測周期兩種方式組合,實現(xiàn)倍頻,以提高分辨率。處理方式為:
在測速周期內(nèi),一方面對脈沖計數(shù),得到完整脈沖的計數(shù)值。同時測量每個脈沖的周期,保存上一個脈沖的周期值(也可以保留前幾個脈沖的平均值,以提高可靠性),為計算非完整脈沖做準備。
當測速周期到時,讀取當前脈沖周期測量“已計量的時間“,除保存的“前一脈沖周期值”,即可得到非完整脈沖值 0. XX ,從而提高分辨率。
圖示如下:
通過這種方式,基本滿足了測速的需要。
測速和行走距離控制都需要啟用IO中斷。
因為測速和PID計算需要定時,但計時要求比不高,故直接使用OS的tick計時。
電機驅(qū)動任務(wù)等待的消息:主任務(wù)發(fā)來的參數(shù)、Tick定時喚醒信號。通過Tick定時喚醒實現(xiàn)周期性處理,如測速、PID調(diào)速、定時運行。
輸出為:電機的物理運轉(zhuǎn)、工作狀態(tài)反饋
3.3.6 舵機驅(qū)動任務(wù)
舵機驅(qū)動比較簡單,輸出周期為20ms的脈沖信號,脈沖寬度從1.5 ~ 2.5ms可調(diào)。
基于PWM組件即可實現(xiàn)。
舵機自身有閉環(huán)控制。但為了能反饋舵機當前狀態(tài),主要是為了告知舵機是否轉(zhuǎn)到指定角度,需要做一些處理。
因舵機自身無反饋信號,只能根據(jù)舵機參數(shù)(響應(yīng)速度),通過計算當前角度到指定角度轉(zhuǎn)動需要多少時間,適當放寬后計時處理,計時完成則說明轉(zhuǎn)到;為連續(xù)控制提供方便。
舵機任務(wù)等待的消息為:主任務(wù)發(fā)來的參數(shù)、Tick喚醒消息。
因本身舵機完成計時精度不高,用Tick計時即可。
舵機任務(wù)輸出為:舵機物理運動、工作狀態(tài)反饋
3.4 基于RT-Thread 實現(xiàn)過程
3.4.1 工程創(chuàng)建
選擇STM32F411CE 芯片創(chuàng)建標準 RT-Thread 項目:
系統(tǒng)版本4.1.1;芯片支持包版本0.2.3;工具鏈(編譯器)版本 10.2.1。
對生成的main函數(shù)略作修改,輸出7次信息后停止輸出。
編譯通過,可以下載執(zhí)行:
說明RTT的編程基礎(chǔ)已有,軟、硬件環(huán)境已打通,可以著手根據(jù)上述設(shè)計編寫程序。
因輪式驅(qū)動單元當初設(shè)計是模塊化構(gòu)思,即可以用一個或多個構(gòu)成不同驅(qū)動方式的小車底盤,為后續(xù)用多個輪子和舵機做其它驅(qū)動方式的小車方便,故此處用C++模式,以便用類的方式簡化后續(xù)的編程,增加程序的可維護性、可擴展性。
在 RT-Thread Studio 環(huán)境下,用C++編程,除了在RT-Thread Setting中選擇C++外,如果用多文件編程,需將應(yīng)用的C程序后綴改為cpp,否則編譯出錯。
之前在兩輪差分小車驅(qū)動上嘗試過使用 RT-Thread,這次以那個程序為基礎(chǔ)修改。
3.4.2 硬件資源分配
串口命令端口:
USART1(PA9、PA10)默認給 Finsh使用。
將USART2(PA2、PA3)作為串口命令及反饋端口。
電機驅(qū)動:
PWM控制端(CT1):PB6,使用 PWM4(Timer4)通道1(T4CH1)
工作狀態(tài)控制端(CT2、CT3):PB5(CT2)、PB4(CT3)
脈沖反饋輸入端:PA12,中斷方式處理
電機電壓采集端:PA0,使用ADC1,通道0
電機電流采集端:PA1,使用ADC1,通道1
原來程序是驅(qū)動差分小車的,控制兩個電機,分別對應(yīng)左、右兩側(cè)。為了便于日后將程序擴展應(yīng)用到多輪驅(qū)動小車,將電機變量標識從左右側(cè)改為1、2……。目前只有一個電機,故定義為“1”。
為了實現(xiàn)倍頻測速,啟用Timer3,作為硬件計時器,獲取ns級計時。
舵機驅(qū)動:
因為舵機的控制脈沖周期為20ms,和電機不同,故需要另用一個定時器實現(xiàn)。
基于STM32F411CE的芯片引腳分配,考慮到后期可能需要驅(qū)動三輪全向小車,選擇Timer2作為舵機驅(qū)動用定時器,仍然使用PWM模式。
目前驅(qū)動一個舵機,使用PA15(T2CH1),即PWM2的通道1。
參考電機驅(qū)動方式,構(gòu)建舵機驅(qū)動類,完成:
1)將角度轉(zhuǎn)換為脈沖寬度
2)進行角度非線性修正,彌補舵機的偏差
3)根據(jù)舵機操作的運行角度,用延時方式指示舵機工作狀態(tài)。
3.4.3 編程
之前首次嘗試 RT-Thread 是做了一個兩輪差分驅(qū)動的小車程序,在上面完成了電機驅(qū)動、測速、調(diào)速,以及一直想實現(xiàn)的PID自整定;控制效果不錯,基本達到了預(yù)期。
這次以兩輪差分驅(qū)動的程序為基礎(chǔ),修改為舵機轉(zhuǎn)向單輪驅(qū)動小車的驅(qū)動程序,電機驅(qū)動部分基本照搬,增加了舵機驅(qū)動部分。
因為后面還想嘗試使用 RT-Thread 驅(qū)動3輪全向小車,所以保留了電機驅(qū)動用類的方式,同時將舵機驅(qū)動也做成類,這樣后面做三輪小車就很方便了。
同樣,為方便測試,基于以往的程序,修改為此處所需的PC端測試程序(基于Processing編寫):
-
控制器
+關(guān)注
關(guān)注
112文章
16111瀏覽量
177083 -
電機驅(qū)動
+關(guān)注
關(guān)注
60文章
1195瀏覽量
86556 -
TTL電平
+關(guān)注
關(guān)注
1文章
99瀏覽量
11965 -
STM32F103C8
+關(guān)注
關(guān)注
1文章
23瀏覽量
8068 -
RTThread
+關(guān)注
關(guān)注
7文章
132瀏覽量
40776
發(fā)布評論請先 登錄
相關(guān)推薦
評論