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