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