以下是魚鷹當(dāng)初完成公司第一個項目時寫的總結(jié),大家可以看看能否得到一些啟發(fā)(或許會比較枯燥,畢竟這是魚鷹自己的經(jīng)驗總結(jié))。
這個步進電機說起來挺簡單的,就是用戶輸入指令,讓兩個電機上下運動或者停止。并且運動范圍通過紅外對管限制,并且有步進電機的硬件驅(qū)動,我只要控制 IO 并且輸出 PWM 脈沖就行。驅(qū)動每收到一個脈沖走動一步。
這是公司的第一個項目,因為使用對象是人,所以必須對一些輸入進行參數(shù)檢查。實際上該項目完成的時候,雖然有一些檢查,但是卻沒有全部檢查,所以還是會有可能出現(xiàn) bug 現(xiàn)象。本來在第三次更新該驅(qū)動程序的時候,也準(zhǔn)備將其改寫的,但是我發(fā)現(xiàn)目前的水平實在有限,即使再改寫,對于參數(shù)檢查還是沒有更好的方法去優(yōu)化算法。所以在所有函數(shù)都重寫的情況下唯有這部分代碼保留下來了。
這個程序一共進行了三次更新,第一次花了一個星期將基本功能實現(xiàn)了。
當(dāng)時采用計時的方式進行運行距離的控制。1 ms 中斷,每次變量自加 1,這樣通過時間乘以速度的方式就可以得到電機的運行距離了,而整個指令組的運行控制在主函數(shù)執(zhí)行。
想法很正確,當(dāng)時確實也運行起來了,但是后來測試人員告訴我,當(dāng)設(shè)定運行時間很短暫的時候,電機不運行,想想也是,時間太短,并且指令運行的控制是在主函數(shù)的,必然有比較大的誤差。
雖然第一次寫這個程序的時候也想過通過對脈沖計數(shù)的方式來實現(xiàn)對電機的控制,這樣只要知道我輸出了多少個脈沖,就能確定運行了多大的距離。但是當(dāng)時卻不知道該如何獲取脈沖數(shù)。因為之前只知道輸出 PWM 波,根本沒想過如何獲取 PWM 的數(shù)量。
能想到的也就是改硬件了,就是再用一個定時器作為脈沖計數(shù)器,這樣兩個電機就需要兩個定時器,包括輸出 PWM 的一個定時器,共用三個……所以這個項目當(dāng)時對于我來說有心無力了,只能將就,只能等以后了。
而且這個程序還有兩個缺陷,就是兩個電機速度不能單獨控制,因為電機速度是通過單位時間內(nèi)的脈沖數(shù)決定的,而輸出 PWM 的兩個引腳使用了同一個定時器。
而控制 PWM 周期的方式有兩種,一種是修改定時器的基本時鐘,另一個就是修改計數(shù)周期,但這兩種方法實際上在一個定時器上都只有一個寄存器。
另一個問題就是當(dāng)時需要暫停功能,我沒辦法實現(xiàn),因為我根本不知道程序運行到哪里了,又如何從原來的地方開始運行呢?
后來在看一個有五年工作經(jīng)驗的高手寫的代碼發(fā)現(xiàn),我其實可以開啟定時器的更新中斷的,每輸出一個脈沖,必然是要進入更新中斷的,只要在更新中斷完成計數(shù)即可。
還有高級定時器有一個寄存器是可以控制輸出脈沖數(shù)的,這個另說。
因為這個問題一直在腦海,所以在看一篇文章的時候,雖然它不是說如何獲取脈沖數(shù),但是也給了我一個靈感,那就是可以同步開啟一個定時器的,知道輸出 PWM 頻率,只要在輸出 PWM 的同時開始另一個定時器,就能在控制時間的情況下精確的控制輸出 PWM 數(shù),雖然和之前的定時 1ms 計數(shù)類似,但精度更高,畢竟對于人來說,1ms 的誤差精度雖然高,但是對于 1ms 輸出幾千個脈沖來說,誤差不是一般的大。
這樣一來,馬上就開始了程序的更新,所以花了大概三天的時間完成了程序的修改,并且因為已經(jīng)能對輸出的脈沖數(shù)進行計數(shù),對于之前要求的在到達紅外對管的下限之后再運行電機的功能也就能實現(xiàn)了。
但是實際運行之后,設(shè)置向下向上的脈沖數(shù)相同的情況下,它是不能回到原點的位置。因為這個程序是在第一個程序上寫的,第一次寫的時候需求不明,考慮的不夠完善,所以整個程序的結(jié)構(gòu)比較混亂,對電機運行方向的判斷上可能會有誤差,所以在更新完成之后,就已經(jīng)打算有機會再更新一次,這一次更新將推翻之前的程序結(jié)構(gòu),要考慮的更完善才行,這是自我提高。
所以從家里回來之后,我就開始考慮更新這個程序了,只是沒想到花了這么久時間才完成。我也沒想過在公司去完善這個程序,公司有公司的事,而且這個程序問題的主因還是自己能力不夠,而且我需要更輕松的狀態(tài)去慢慢考慮清楚,并嘗試使用新方法去重新設(shè)計程序的結(jié)構(gòu)。
有了前兩個程序的基礎(chǔ),所以知道程序設(shè)計的時候該考慮哪些問題。
變量設(shè)計考慮:首先就是反映電機狀態(tài)的變量必須保證緊隨實際的狀態(tài),并且改變這個狀態(tài)的位置只能是一個地方,而不能這個函數(shù)改一下,那個程序改一下,這樣肯定會導(dǎo)致整個程序的混亂。
還有就是變量的作用要明確且單一,不要一個變量給它附加多種用途,這樣也可能會程序混亂。
除此之外,還要考慮這個變量是否必須一直存在,還是說多個變量其實歸納為一個變量。
一個例子就是,輸入用戶指令后,有多個參數(shù)變量,并且要指示這條指令是否完成,這條指令的當(dāng)前輸出脈沖數(shù)等等。
其實對于一個執(zhí)行器來說,只要將最基本的用戶指令保存即可,根本不需要附加其他的,即使有附加,也是在執(zhí)行的過程存在,也就是說這個命令組的其它附加信息其實只要共用一個變量即可,因為當(dāng)前運行的只能是一條指令,只要在運行下一條指令時將上一條指令信息清除(可以清除一個標(biāo)志,作為整條指令信息清除的標(biāo)志,比如將下行指令標(biāo)志改為無指令狀態(tài)即可)并重新初始化就可以繼續(xù)為下一條指令服務(wù)了。
時間同步:就是每次開始處理數(shù)據(jù)的時候都統(tǒng)一在定時器更新中斷執(zhí)行(前提是這個數(shù)據(jù)處理必須能在中斷時間內(nèi)處理完成),這樣每處理一次,都是在定時器重新計數(shù)的情況下,而不會出現(xiàn)這里剛處理完數(shù)據(jù),那里就中斷數(shù)據(jù)處理產(chǎn)生一個脈沖的情況,這樣必然可能出現(xiàn)脈沖計數(shù)不準(zhǔn)的情況。
而且在進行數(shù)據(jù)處理的時候,對關(guān)鍵數(shù)據(jù)的處理可以采用關(guān)閉全局中斷和停止定時器的方式來確保數(shù)據(jù)處理的完整性。
啟動操作、結(jié)束操作:就是一個完整動作的執(zhí)行其實只有啟動和停止而已,在將所有數(shù)據(jù)處理完之后就可以進行啟動操作,在該動作完成之后只要處理身后事即可,這個身后事包括停止上一個動作的所有影響,以防出現(xiàn)下一個動作還未啟動而上一個動作繼續(xù)執(zhí)行的情況。
當(dāng)然還有一種情況就是在啟動該動作之后,有可能出現(xiàn)意外情況,而需要將該動作停止,所以需要定時監(jiān)視。但是定時監(jiān)視有一個缺陷,就是你不能及時查看異常。所以如果硬件允許的話,可以采用硬件中斷來監(jiān)視。
走一步看一步:在硬件條件不允許的情況下可以采用走一步看一步的方法。就是每輸出一個脈沖(之后停止定時器),然后看有沒有到限制位置,如果沒到,就繼續(xù)走,如果到了,就停止該動作。這樣看起來效率挺低的,可實際上,單片機的速度很快,對于我們?nèi)藖碚f,我們根本感覺不到它是邊走邊看的,你看到的只有一個完整動作執(zhí)行的現(xiàn)象。
暫停動作:之前提到如何暫停動作,并且重新執(zhí)行問題,之前想的是從程序本身考慮(離開代碼執(zhí)行,然后重新返回該代碼位置重新執(zhí)行,有點像操作系統(tǒng)切換任務(wù)的情況)。
其實我只要停止這個動作的執(zhí)行并且不破壞這個動作的相關(guān)的變量即可,我根本不需要知道它執(zhí)行到了哪個 PC 地址。比如電機的執(zhí)行靠 PWM 輸出,相關(guān)變量的變化是在中斷執(zhí)行的,我只要停止了定時器,就一了百了了。既停止了 PWM 輸出(停止動作),又不會讓它繼續(xù)執(zhí)行相關(guān)變量的操作,這樣一旦重新啟動定時器,變量繼續(xù)變化,PWM 繼續(xù)輸出,變量條件達到即可像沒事一樣繼續(xù)執(zhí)行停止操作并完成身后事。
所有動作暫停一下:這里所謂的動作暫停不是外部現(xiàn)象的暫停,而是內(nèi)部的。
并且這個動作應(yīng)該是內(nèi)部控制情況下才可以暫停,否則如果是外部控制的情況下暫停內(nèi)部運行,必然會因為響應(yīng)不及時丟失重要東西。這里因為電機運行動作全靠自身輸出 PWM 來控制的,所以暫停幾十毫秒在外界看來沒什么影響,也看不出來。
那為什么要暫停呢?比如你要在中斷串口輸出一個信息,如果采用查詢的方式輸出的話,必然需要十幾毫秒才能輸出完成,而這個時間遠(yuǎn)大于你的中斷時間,所以必然會錯過下一次中斷的執(zhí)行,導(dǎo)致外部已經(jīng)輸出了一個 PWM,而相關(guān)變量計數(shù)器沒有計數(shù)的情況。所以這個時候就可以通過停止定時器的方式來停止動作的執(zhí)行,這樣定時器的寄存器計數(shù)器就不會繼續(xù)累加了,也不會產(chǎn)生脈沖。當(dāng)輸出完之后就可以繼續(xù)開啟定時器。
那為什么不采用關(guān)閉全局中斷的方式呢?你要知道的是,你關(guān)閉全局中斷只是屏蔽了中斷,而不能阻止中斷的發(fā)生,只是說,中斷發(fā)生后,因為你的屏蔽,導(dǎo)致該中斷處理程序的延后執(zhí)行而已。
之前說過兩個電機不能獨立設(shè)置速度的問題,主要就是因為兩個引腳共用同一個定時器,后來在偶然情況下想到其實引腳有一個復(fù)用功能的,有可能一個引腳是其他定時器的復(fù)用輸出引腳呢?或許是對于問題的執(zhí)著,我幸運的發(fā)現(xiàn)真的有一個引腳是高級定時器的一個復(fù)用 PWM 輸出引腳,這樣我就不用采用模擬的方式輸出 PWM 波了。
到此,當(dāng)初所有的功能需求完成了。越努力越幸運或許真是如此!