從8/16位向32位圖形MCU升級(jí)的各種技巧
就在我的有關(guān)如何對(duì)16位PIC24微控制器進(jìn)行編程一書剛剛出版不久,我聽說Microchip公司一款新的32位PIC32微控制器已經(jīng)出爐。該產(chǎn)品使用的是MIPS內(nèi)核,同時(shí)還聲稱與16位的引腳以及PIC24系列產(chǎn)品外圍兼容。對(duì)我來說這簡(jiǎn)直是太重要了!我立即起身去要到一個(gè)樣片并刷新基于GNU的MPLAB C32 C編譯器的beta拷貝。
我只是必須看一下這款新產(chǎn)品像什么。它仍然像是PIC MCU?它能不能在同樣的演示版上工作?畢竟,我已經(jīng)用C語言為PIC24寫完了15章頗有價(jià)值的16位代碼和例程。長(zhǎng)話短說,在隨后的不到一個(gè)月的時(shí)間里,我不僅完成了代碼移植,而且已經(jīng)開始利用所掌握的PIC32的經(jīng)驗(yàn)書寫一本新書了!
下面就是對(duì)一個(gè)月里所發(fā)生的事情的簡(jiǎn)要敘述。我喜歡從我遵守最好的設(shè)計(jì)準(zhǔn)則并從閱讀數(shù)據(jù)頁開始講述,如果說我從頭到尾全面閱讀了數(shù)據(jù)頁,那是撒謊!實(shí)際上我所做的與你做的完全一樣。打開裝有以前PIC24項(xiàng)目的MPLAB集成開發(fā)環(huán)境,點(diǎn)擊F10鍵立即創(chuàng)建。
二進(jìn)制數(shù)字
一長(zhǎng)串的錯(cuò)誤列表出現(xiàn)在輸出窗口中。令我驚奇的是,所報(bào)道的所有錯(cuò)誤都明顯地只與我的二進(jìn)制注釋(0b00000000),即C語言的一個(gè)非標(biāo)準(zhǔn)擴(kuò)展有關(guān)。我試圖編譯我關(guān)于16位控制器一書前三章中的第一個(gè)代碼例程。這是一段非常簡(jiǎn)單的代碼,用C來說明I/O,精確定時(shí)以及流控制(用于循環(huán))命令。我立刻決定將所有二進(jìn)制文字轉(zhuǎn)換成標(biāo)準(zhǔn)十六進(jìn)制的注釋(0x00)并觀看結(jié)果,瞧!編譯器和鏈接器馬上解析出代碼沒有任何錯(cuò)誤。
感覺很幸運(yùn),我決定繼續(xù)“前進(jìn)”,并在一些實(shí)際的硬件——即Explorer 16演示板上運(yùn)行代碼。我獲取了一個(gè)PIC32插件式模塊(PIM),并取代我的16位器件一書中普遍所用的PIC24 PIM。加電后我抱著非常懷疑的態(tài)度觀察了幾秒鐘,竟然沒有“冒煙”!然后我取了一塊MPLAB Real ICE調(diào)試器和編程器連接到板子上。MPLAB IDE竟然很快識(shí)別出該工具并報(bào)告已發(fā)現(xiàn)PIC32連接到板子上。
在快速和自動(dòng)的固件升級(jí)后,我便立即按下編程鍵,隨后便是運(yùn)行命令….但卻不工作!
我意識(shí)到板子上明顯有什么問題,但一點(diǎn)也不像我所期望的那樣。這里需要解釋一下。在我的16位器件一書的前三章中,我利用C語言向讀者提供了如何生成“Hello World”這類例子。其中,我講到傳統(tǒng)的實(shí)現(xiàn)方法是,向終端發(fā)送一個(gè)字符串,但這在嵌入式控制應(yīng)用中是不現(xiàn)實(shí)或者不合適的。而是采用一個(gè)“有趣的”方案,即制作一排8個(gè)LED,當(dāng)把板子拿在手上并揮動(dòng)時(shí),燈將有節(jié)奏地閃爍。它將顯示出有用的信息,這要?dú)w功于眼睛的自然成像持久性。實(shí)際上對(duì)此進(jìn)行編碼要比描述更容易。
不同時(shí)鐘
事實(shí)上是,PIC32得到的I/O引腳和定時(shí)全部都是錯(cuò)誤的。
對(duì)于這一點(diǎn),通常我只需要噼里啪啦地翻開數(shù)據(jù)頁并按我自己的工作方式來查找問題的根源。出現(xiàn)的問題是,PIC32時(shí)鐘產(chǎn)生模塊比16位器件一書中所用的PIC24F要更加復(fù)雜一些。實(shí)際上,PIC32模塊更像16位MCU系列中最新的PIC24F上的振蕩器模塊。同樣,在PIC32結(jié)構(gòu)中,絕大多數(shù)外設(shè)模塊被連接到工作頻率不同的彼此分離的外設(shè)總線上,這些頻率低于系統(tǒng)時(shí)鐘,這有助于功率管理,當(dāng)然也有助于解決EMI問題。
我耐心找出如何使外設(shè)總線工作在與同一項(xiàng)目(16MHz外設(shè)總線)中PIC24F所用相同的頻率。我還找出了可以執(zhí)行的相同指令數(shù),而執(zhí)行頻率僅為PIC24F所要求系統(tǒng)頻率的一半,這是因?yàn)镻IC32內(nèi)核每個(gè)時(shí)鐘周期上可以執(zhí)行一個(gè)指令。
JTAG默認(rèn)值設(shè)置為on
在解決了時(shí)鐘問題之后,我快速地瀏覽了一下時(shí)鐘模塊。有5個(gè)時(shí)鐘模塊。看上去絕對(duì)與PIC24F完全一樣,進(jìn)一步回溯PIC MCU的歷史,一直回溯到PIC16C74(大約1994)都是兼容的。我繼續(xù)驗(yàn)證I/O端口:同樣的結(jié)構(gòu),同樣的引腳數(shù),同樣反映“歷史”的寄存器名稱,發(fā)現(xiàn)一個(gè)兼容型的軌跡也許可以一直延伸到最初的PIC16C54(大約1991年)。
最后我對(duì)A/D轉(zhuǎn)換模塊進(jìn)行了一次快速檢查,對(duì)于絕大多數(shù)PIC MCU初學(xué)者來說這是一個(gè)最難理解的外設(shè)。其輸入連接到I/O口的上端(絕大多數(shù)16位PIC器件的PORTB),并且先加電,故除非你的配置正確,否則它不會(huì)使你的數(shù)字輸入工作。顯然它與PIC24兼容,因此我仍然無法解釋LED行為異常的原因。
更靠近看,我發(fā)現(xiàn)有4個(gè)LED,要么從來不亮,要么就恒亮。于是,我又再一次翻開數(shù)據(jù)頁來檢查引腳圖,最后終于發(fā)現(xiàn)了“元兇”:JTAG端口。
四線(E)JTAG接口被稱為在線串行編程接口,是一個(gè)非正式的行業(yè)標(biāo)準(zhǔn),它不僅允許邊界掃描,而且還支持器件完全編程和調(diào)試控制。當(dāng)然,這在引腳數(shù)很多的32位芯片中是所期望的,PIC32在加電時(shí)通過默認(rèn)的方式將這兩個(gè)接口都激活了。如果為了利用一些PORTA I/O而不需要這些JTAG接口,則依賴應(yīng)用程序來將其關(guān)閉。
自從我注意了JTAG接口后,我的第一個(gè)PIC32項(xiàng)目開始按期望工作,并發(fā)送出它的首個(gè)“Hello”,如圖1所示。
圖1:用PIC32產(chǎn)生字符串。
至此所學(xué)到的簡(jiǎn)單經(jīng)驗(yàn)(振蕩器配置和JTAG接口)迅速地證明了它們與我16位器件一書中前面各章節(jié)中絕大多數(shù)項(xiàng)目兼容性的關(guān)鍵,在隨后幾天的開發(fā)中移植都比較順利。我利用UART與PC通信,用SPI接口與串行EEPROM通信,而利用Parallel Master Port與LCD模塊通信。我利用A/D先讀取電位器,然后讀取溫度傳感器,演示了PIC32如何與模擬應(yīng)用接口。除了模塊的一些擴(kuò)展功能以外,所有這些模塊的工作都與我所預(yù)期的完全一致。我發(fā)現(xiàn)我的16位代碼完全可以照用,幾乎不需要任何的改變。
測(cè)量性能
在早期的一些日子里,我的好奇心促使我想知道PIC32究竟帶來什么好性能。在我的16位器件一書的第四章中,即“Numb3rs”,我對(duì)執(zhí)行基本的算術(shù)運(yùn)算所需的指令周期數(shù)進(jìn)行了統(tǒng)計(jì),并將它們與各類整數(shù)和浮點(diǎn)類型進(jìn)行了比較。這在時(shí)鐘周期與所執(zhí)行的指令密切相關(guān)的場(chǎng)合,如PIC24和dsPIC DSC內(nèi)核中那樣,這種方法是合理的。但在PIC32內(nèi)核中,由于采用了MIPS的傳統(tǒng),為“比賽”增加了“難度”。每個(gè)時(shí)鐘周期所執(zhí)行的指令是可變的,因?yàn)楫?dāng)執(zhí)行代碼快于閃存額定速度(每30MHz只插入一個(gè)時(shí)鐘周期)時(shí)可以插入等待狀態(tài),或者可以無關(guān),這要?dú)w功于預(yù)取狀態(tài)機(jī)(能夠一次預(yù)取四條指令)。最后,激活一個(gè)高速緩存,進(jìn)一步改進(jìn)了高速性能。
PIC32的高速緩存使得周期數(shù)有點(diǎn)不可預(yù)測(cè),也許變得沒有意義。我好像覺得我從貨物推車一下子升級(jí)到了一級(jí)方程式賽車一樣!于是,我決定需要在32位器件一書中增加一章關(guān)于PIC32的性能調(diào)整內(nèi)容。為了給PIC32加上重載荷,我找到我在大學(xué)讀書時(shí)學(xué)習(xí)基本數(shù)字信號(hào)處理的一個(gè)老代碼程序:即快速付里葉變換。我采用的是標(biāo)準(zhǔn)浮點(diǎn)結(jié)構(gòu),沒有手工和編譯器優(yōu)化。另外還用了一個(gè)32位定時(shí)器,讓PIC32自己計(jì)時(shí),隨后我逐步地開始選用一些新的程序選項(xiàng)。
開始時(shí),我激活指令預(yù)取,然后我找到高速緩存,再隨后我通過人工方式調(diào)整等待狀態(tài)。一開始性能改善極大,并且隨著之后對(duì)配置進(jìn)行進(jìn)一步的細(xì)調(diào),性能改善更多。最終,我意識(shí)到最佳的配置必須隨應(yīng)用定制,但必須由標(biāo)準(zhǔn)器件庫中的SYSTEMConfigPerformance()提供一個(gè)好的起點(diǎn)。
學(xué)習(xí)外設(shè)庫
這是我第一次使用“標(biāo)準(zhǔn)”外設(shè)庫,也是這種愛/恨關(guān)系的開始。由于我在非常小型的8位器件上使用匯編進(jìn)行代碼開發(fā)已經(jīng)許多年了,且通常都是需要采用手工優(yōu)化客戶代碼,我基本上都是自己親自工作,最終我開發(fā)出了一些自己的器件庫。
這一次,在投放PIC32產(chǎn)品之前一年多的時(shí)間,我不僅移植了16位器件的庫,還對(duì)它們進(jìn)行了擴(kuò)展來支持一系列新功能。我沒有更多的理由-唯一理由就是我自己必須掌握并學(xué)會(huì)如何使用它們。參見用于一個(gè)使用該外設(shè)庫的程序代碼段的Listings1和2,見圖2。
圖2:代碼移植時(shí)用于一個(gè)使用該外設(shè)庫的程序代碼段的Listings1和2。
通過利用這個(gè)新庫,16位和32位應(yīng)用之間的代碼兼容“絕對(duì)”沒有問題。即便是外設(shè)寄存器上的極小差別也可以通過應(yīng)用代碼完全消除。實(shí)際上,這使得一個(gè)應(yīng)用在16位器件和32位器件上都可以運(yùn)行,從而開發(fā)人員面向兩種架構(gòu),卻維護(hù)統(tǒng)一的代碼基。
不過,雖然在器件數(shù)據(jù)頁中對(duì)硬件控制寄存器名稱已有注明(甚至每一位都很詳細(xì)),但卻沒有所有的功能/宏名及其參數(shù)。很多時(shí)候,我發(fā)現(xiàn)必須將單個(gè)的包含文件與器件數(shù)據(jù)頁進(jìn)行比較,嘗試著去猜測(cè)究竟有哪些控制位與一個(gè)特定的庫參數(shù)相關(guān)。當(dāng)利用最簡(jiǎn)單的庫(比如I/O端口操作)時(shí),這是一件特別麻煩的事情,對(duì)我來說,在這里,庫抽象層的優(yōu)點(diǎn)更值得質(zhì)疑。
最終,我發(fā)現(xiàn)可以采取一個(gè)平衡折中。即可以采用傳統(tǒng)的方法訪問絕大部分的基本外設(shè)(例如I/O端口和計(jì)時(shí)器),而在使用更復(fù)雜/新外設(shè)時(shí)才使用庫。于是,我迅速通過了有關(guān)代碼的幾個(gè)章節(jié),實(shí)際上什么都沒有改。這些章節(jié)包括:SD/MMC接口,F(xiàn)AT16文件I/O甚至包括WAV音樂文件重放。
當(dāng)我決定再深入地研究中斷時(shí),以及后來開始使用PIC32的新DMA模塊時(shí),這些庫的好處就變得很明顯了。
中斷和決策
PIC32提供兩種中斷選擇:一種是非常類似于PIC16/18 8位架構(gòu)操作方式的單矢量模式(順便指出,與RTOS也更加友好),另一種是更類似于16位PIC24 MCU和dsPIC DSC工作模式的多矢量模式。利用interrupt.h庫來設(shè)置參數(shù)是輕而易舉的事情。
是我開始嘗試移植第12章中代碼的時(shí)候了:“黑屏”確實(shí)是一件有趣的事情。用PIC24,我能夠演示SPI端口是如何的簡(jiǎn)單,只需要三只電阻器,幾個(gè)中斷,以及一些創(chuàng)新就可以產(chǎn)生一個(gè)復(fù)合視頻信號(hào),特別是可以將任意的電視機(jī)轉(zhuǎn)換成單色顯示器。要產(chǎn)生一個(gè)視頻信號(hào),需要中斷代碼與外設(shè)之間的精密協(xié)調(diào)。實(shí)際上,因?yàn)榧幢闶窃谳敵龆〞r(shí)上只差一個(gè)時(shí)鐘周期,在顯示器屏幕的左側(cè)就會(huì)產(chǎn)生可見的抖動(dòng)(所有的豎直線都變得像鋸齒一樣),故訓(xùn)練結(jié)果將是用于中斷“決策”的一個(gè)理想放大鏡,這是PIC架構(gòu)傳統(tǒng)上一直出眾的特性。然而不幸的是,根據(jù)定義,指令預(yù)取和存儲(chǔ)器緩存機(jī)制都是非確定的。
經(jīng)過一段苦思冥想,我最終明白了。我在試圖做不應(yīng)該做的事情!32位內(nèi)核是為了提高性能而設(shè)計(jì)的。其使命是C代碼的運(yùn)行盡可能地快,而把實(shí)時(shí)嚴(yán)格的工作留給外設(shè)。特別是,DMA外設(shè)是一個(gè)非常好的工具。
最終,我想出了如何利用定時(shí)器直接產(chǎn)生復(fù)合視頻信號(hào),并將DMA數(shù)據(jù)傳輸同步到SPI端口的方法。這種新方案提供了確定性的定時(shí),而且還將CPU的開銷減少了大約25%到5%。幾個(gè)小時(shí)工作后,我完成了2D和3D視頻演示并運(yùn)行,還加上了動(dòng)畫,從高清顯示一直到單顯VGA(實(shí)例見圖3和圖4)。
圖3:用PIC32所產(chǎn)生的3D圖形。
圖4:用PIC32所產(chǎn)生的不規(guī)則幾何圖形。
開發(fā)PIC32很快變成一件令人上癮的事情,結(jié)果是很好的回報(bào),那就是讓我完成了一本有關(guān)32位器件新書的寫作之旅!PIC32與早先的16-bit PIC24微控制器的兼容性是那樣的無縫。這款新的MIPS內(nèi)核的速度和性能給我留下了深刻的印象,大大擴(kuò)展了任何先前的PIC MCU都無法實(shí)現(xiàn)的應(yīng)用范圍。
表1:PIC24F AD1CON寄存器的并排比較。
評(píng)論
查看更多