從開始搞ARM到現在將近半年多了,第一個項目搞得有些眉目了,終于感覺像是入門了,半年來,有開始的新鮮,中間的苦悶,到最后的欣喜。其中過程可謂曲折離奇,遇到了很到前人沒有遇到過的疑難雜癥,當然很多時候是因為我的粗心釀成的。曾經也有過放棄的念頭,那個焦慮,像得了狂躁癥一樣。后來下定決心即使績效沒了,工作丟了也要搞完它。其實在這個過程中,看到跟我一樣的很多新人在論壇上發帖求助,可是很多時候回者寥寥無幾,可能問題太幼稚,也可能問題描述的不清楚。我發過很多帖子,甚至直接騷擾了網上很多的牛人,他們都給了我很大的幫助,但是我當時的想法太簡單了,總想著某個牛人能夠解決掉這個問題,現在想想,即使是牛人,沒有看到具體的問題也很難給你一個解決方法,遇到困難不能把希望完全寄托在別人身上,要挖掘自身潛力,一遍遍仔細看手冊,反復試驗,不斷思考,問題肯定能解決掉,只是時間問題而已。再次要感謝公司對我的容忍,一個這么簡單的東東允許我搞了這么久。
其實,在前面的過程中,一直有寫點什么的沖動,但是當時困難重重、前途未卜,也沒了這個心情。現在可以坐下來仔細總結下前面的問題,有現在都沒搞明白的,特向大家請教了;有解決掉的,那就說說經驗教訓,給其他人一些參考。首先聲明本人腦瓜笨,邏輯思維差,點一個燈點了一個多月,最后還發現沒點對。所以提到的問題可能很幼稚,說話也好像前言不搭后語,有興趣看的那就受累了哈。
先說說我們的這個塊板子,裸奔的at91sam9260,外擴Norflash、SRAM,實現程序既可以跑在Norflash中,也可以拷貝到Sram中跑。任務就一個:控制一個片外AD,讀出數據然后通過串口發出。很簡單的吧,這我都搞了幾個月呢,你說菜不菜吧。以下我將回想整個的ARM學習過程,涉及到的知識都是很簡單的基礎知識,老鳥就不用看了,希望可以幫助到像我一樣的菜鳥。
前三個月主要是熟悉的過程,當時完全沒有想到后面程序的調試會如此的困難,想當然地認為又不上系統,無非是32位的單片機嘛。板子做回來一大段時間內,元器件都沒有湊齊,當時傻了呀,應該先熟悉熟悉編程環境,每天就為幾個破元件折騰,什么都沒干。
后來板子焊接好了,才發現只會用H-JTAG識別芯片,其他的什么都不懂。然后開始看例程,只玩過51的我,初次接觸32位的單片機,當時看到AT91C_BASE_PMC->PMC_PCER=(0x1<<3)這樣的語句,竟然不知道是什么意思,一是困惑->代表個啥?二是為啥寫成0x1<<3的樣子?后來不知過了多久才明白,AT91C_BASE_PMC是個基地址,PMC_PCER是相對這個基地址的偏移,移位賦值是為了給32位寄存器賦值的方便。
先說說開發ARM要用到的軟件以及工具吧,這段時間我接連使用了IAR、KEIL、h-jtag、JLink,都會用但是都不精通。最后是在KEIL+Jlink下完成的。
開發工具的選擇:
1、編譯環境:IAR、KEIL、GCC、……
開始我想也沒想就選擇了IAR,原因很簡單:ATMEL的例程很都是基于IAR的,而KEIL安裝目錄下的例程很少。匆忙安裝了IAR當時的最新版IAR5.20,完全沒有考慮可用的資源和交流的方便。然后才發現IAR5.X跟4.X有很大的不同,主要就是其中的鏈接器從XLINK換成了ILINK,所以配置文件也由XCL文件換成了ICF文件,初看后者好像比前者更簡單更易懂了,但是由于是新版,使用的人還不多,網上上的參考資料大部分還是基于4.X的,對于新手哪幾條語句還是很費解。
不過一根筋的我還是硬著頭皮堅持使用IAR5.20,直到遇到IAR的殺招:系統從慢時鐘想快時鐘切換時就跑飛了,都是死在lowlevelinit()中的這一句上:AT91C_BASE_PMC->PMC_MCKR |= AT91C_PMC_CSS_PLLA_CLK;然后將這一句放在主程序中,照死不誤。使用過程中,總彈出一個警告,大致意思就是說:IAR安裝目錄下,bin文件夾下的armlibsupport.dll may be missing or corrupt.重新裝后還是老樣子。在LED閃爍程序上折騰一個多月未果后,才痛下決心轉向KEIL。
用上KEIL才發現這玩意不是傳說中的弱智,相反特別適合我等菜鳥,感覺主要有三個方面特別好:啟動代碼的圖形化配置;在Flash跑不需要什么配置文件,直接在option中添兩個地址值就可以了;只需點擊鼠標就可以實現將代碼從Flash自動拷貝到RAM中執行,不需要再寫什么代碼拷貝程序了。
GCC,沒用過,不懂。
2、調試工具:JLINK、H-JTAG、ULINK、……
開始使用h-jtag,配合Wiggler使用,小巧且便宜,不得不佩服Twentyone前輩,在RAM中調試很方便,也可以通過H-Flasher將程序下載到Flash跑,使用h-flasher時要一個初始化文件,主要是初始化Flash相關寄存器。
后來看到DB的JLINK都白菜價了,就從淘寶買了一個JLINK,主要是不用再用并口了,現在百元以內的JLINK大把,用JLINK是個不錯的選擇,但是調試片外的flash好像還不能無限斷點。
ULINK,不太清楚,好像只支持KEIL,不過沒用過。
今天先寫到這里,不好意思,牢騷多于知識了。接下來準備說說我對REMAP、Bootloader等概念的理解。
第一次搞ARM特別是從51直接跳到ARM的必須面對的幾個概念:REMAP、Bootloader、Flashloader。
1、REMAP:
提到REMAP。首先應想到什么是MAP,英語不好,開始就斷章取義,MAP就是地圖嘛,Memory Map就是存儲器地圖,不過這個地圖的參考坐標不是經緯度,而是地址,進而叫做存儲器映射。由于要適應不同存儲器容量要求的用途,ARM處理器本身的RAM、ROM并不是足夠大,所以很多時候要外擴一些存儲器,Norflash、NANDFlash、SDRAM、SRAM……而對于ARM來說怎么識別這些不同的存儲器呢,只能給每個分配一個獨立的地址,就相當于每個人有不同的名字。片內存儲器的地址一般出廠就固化好的,片外的話就根據每個存儲器所連接的外部總線片選而具有不同的地址。
所以REMAP,顧名思義就是存儲器的重新映射,即某些存儲器的地址又發生了變化。我就很不理解了,這地址本來就不好記,還變來變去的,麻不麻煩呀,學51的時候咋就沒這玩意呢?后來查了些資料,有些明白了,51是8位機,更重要的是51的主頻不高,8位的ROM或Flash足夠匹配51的主頻,不用插入等待指令,所以程序直接在ROM或Flash中跑影響不到系統的速度。而ARM就不同了,ARM是32位機,但是Flash一般是8位或16位,32位的也有吧,好像價格很高。而且ARM的制品很高,動輒上百M,所以Flash的工藝達不到這個速度。如果程序跑在Flash中就要插入過多的等待指令,所以會影響ARM的性能。而RAM一般存取速度比較快,很容易構成32位,可以與高速的ARM匹配。更重要的是ARM上電后必須從0x0地址處取得指令,因此上電后必須將ROM或Flash映射位0X0地址處,當時還產生了一個弱智的想法,既然RAM這么好,為啥還要ROM或Flash,直接將程序下載到RAM中不就得了,后來才猛地想到RAM是易失型存儲器,掉電后啥也沒了,再上電后0X0處啥都沒有。而且還有一條,ARM的中斷向量表,既存放各個中斷入口地址的地方,一般放在0x0處,即ROM或Flash中,為了加快中斷響應速度,也應該將0X0映射到RAM中去。因此,ARM一般從ROM或Flash啟動完成初始化,然后將應用程序拷貝到RAM,然后跳到RAM執行。
剛才說的是,為啥要REMAP,接下來說怎么REMAP。開始的時候我就不清楚,都說REMAP,那怎么才能完成REMAP呢?都是手冊看得少呀,其實上面說的已經很清楚了,我們用的at91sam9260更是簡單,有專門的寄存器可以配置,MATRIX_MRCR—Master Remap Control Register,向這個寄存器相應位寫1就可以了。網上還看到Samsung的某些ARM可以通過編程相應Bank寄存器改變其起始地址,來實現REMAP。
下面以我們的at91sam9260的板子為例詳細說說我對at91sam9260 REMAP的理解,開始Flash沒有任何程序,當然也沒有REMAP,此時將BMS接高,然后上電,此時的0X0地址處位于片內的ROM,由于ROM內好像固化了引導程序,所以此時串口會輸出“Rom Boot…>”字樣。而內部的SRAM0的起始地址還是在0x20 0000處,而片外Norflash起始地址是0x1000 0000處。然后我們利用h-flasher或J-Flash將生成的Bin文件下載到Norflash內,即起始地址為0x1000 0000處。然后將BMS接低,此時Norflash被映射在0X0地址處,即此時Norflash的起始地址為0X0,(你可能要問那ROM的地址現在在哪兒呢?我也不知道,因為Norflash的地址范圍是0X0~0X1F FFFF,而ROM的起始地址默認是0X10 0000,恰好在Norflash的范圍內,所以此時ROM哪兒去了?)此時上電,因為0X0地址處即Norflash起始地址有八個合法的中斷向量,程序會從Norflash啟動,接著執行啟動代碼,初始化SMC、PMC,然后Copy中斷向量表到內部SRAM0,然后,將MATRIX_MRCR寄存器相應位置1,實現REMAP,此時,Norflash的起始地址又變回0X1000 0000,而內部的SRAM0的起始地址又變回0x0了,系統如果發生異常,將從地址0X0處即內部SRAM0取中斷向量,而內部SRAM的訪問速度顯然高于外部的Norflash,所以提高程序性能。這是我對at91sam9260 REMAP的理解,歡迎討論指教。
2、Bootloader:
說實話,這個概念到現在也不是很明白。可能對于裸奔的系統來說,Bootloader這個概念本身就比較模糊吧,望文生義的話,Boot,靴子,Load,穿上靴子走路才比較舒服(這個比喻好像比較爛喔),對于ARM來說,初始化好,并將向量表以及數據什么的拷貝到RAM,運行起來才順暢。就是傳說中的引導裝載。所以我理解的Bootloader就是完成ARM的初始化、建立中斷向量表并映射到RAM中、將數據段和必要的代碼段拷貝到RAM、完成REMAP、跳轉到Main,這一系列過程。說白了就是啟動代碼干的活。這個理解我自己都感覺很牽強,還請大家多多指點。
3、Flashloader:
這個概念更是模糊,總感覺跟Bootloader差不多,只不過Flashloader可以實現對Flash的讀寫、擦除等操作,并與調試軟件配合實現將程序下載到Flash中。IAR中有一個選項:Use Flashloader,不過好像一般都是針對片內Flash的,我們的板子是外擴的Norflash,好像就沒有用到這個東東。
接下來準備說說基于KEIL MDK下的啟動代碼的理解。
提起啟動代碼,我就嗷嗷郁悶,IAR下的程序都死在了這里,Keil中出現的問題很多都是通過對啟動代碼的修修補補才解決的,一句話:成也啟動代碼,敗也啟動代碼。
啟動代碼應該是剛接觸ARM的新手必須面對而又很頭痛的問題吧,剛開始我也很納悶,為什么搞個這玩意,學51的時候咋就沒見過呢。而且還都是匯編寫的,俺的匯編還停留在“MOV”階段,其他的不是很懂,沒辦法,誰讓匯編的效率高呢。提到啟動代碼還不得不老生常談一下其中要完成的任務:
1、建立異常中斷向量表,ARM從0X0開始給每個異常中斷分配4個字節的空間,一般存放一個跳轉指令(B)或PC的裝載指令(LDR PC,X_Vector),當發生異常時,ARM從此處取得相應異常中斷處理程序入口地址,再跳轉執行;
2、ARM都是高速處理器,而在高速下啟動很可能會不穩定,所以在啟動代碼從慢時鐘開始運行,在適當的位置,從32.768K切換到高速運行;
3、ARM一般帶有片外存儲器,Flash、SDRAM等,這些存儲器都需要初始化才能使用,這都是在啟動代碼中完成,但是Norflash的初始化要在時鐘初始化之前;
4、ARM有不同的模式,每種模式都需要相應的堆棧;
5、Copy異常中斷向量表到RAM,并實現REMAP,具體請參照上一節;
6、Copy可執行映像的數據段到RAM,并將ZI區清零。這個一般都是由編譯器完成的,IAR下是?main來實現,Keil中由__main實現。
現在啟動代碼可以看懂一些,不過自己寫啟動代碼還是很遙遠的事情。如果開始對啟動代碼很抵觸,可以考慮使用Keil,因為Keil由啟動代碼的圖形化配置,直接點擊鼠標操作就可以實現自己的啟動代碼。下面結合我們at91sam9260的板子,說說Keil中的啟動代碼。
打開Keil生成的SAM9260.S,點擊左下角的“Configuration Wizard”進入圖形化配置向導,根據你的需要選擇參數,全部選擇完畢后,再點擊"Text Editor",將會看到生成的啟動代碼。
我靠,不是吧,將近2000行,開始你可能會很泄氣,但仔細一看,前面不都是些宏定義嘛,跟圖形化配置向導一一對應的,只有從1200多行的這一句開始的才是真正的啟動代碼部分。
;----------------------- CODE --------------------------------------------------
PRESERVE8
開始是8個PC裝載指令,注意第六個向量,即地址0X14處,存放可執行映像的大小,||Image$$ER_ROM1$$RO$$Length||+||Image$$RW_RAM1$$RW$$Length||
。
接下來是SMC、PMC的初始化,我們的板子外擴了Norflash,如果在未初始化Norflash前,切換到快時鐘,系統起不來,所以應該先初始化SMC,再初始化PMC,而Keil自帶的啟動代碼中默認PMC在前,怎么辦,可以將前面PMC的宏定義部分和初始化部分剪切,然后分別粘貼在SMC宏定義部分和初始化部分的后面,然后再看“Configuration Wizard”中,PMC自動放到了SMC的后面了。
接下來是關閉看門狗(默認是打開的),拷貝異常中斷表到RAM中,然后REMAP,建立緩存,建立各個模式的堆棧指針。
最后進入__main進行數據段和代碼段的拷貝以及初始化C語言庫函數,然后跳轉到main執行。
Keil中有詳細的注釋,理解起來應該不是很難,具體的語句無需明白,知道個大致意思就行了。無非是將某個外設的基地址裝載(LDR)到一個寄存器R0,將要向這個外設的某個寄存器賦的值裝載到另一個寄存器R1,然后加載(STR)。一般的模式就是這樣:
LDR R0,=Periphral_BASE ;某外設的基地址
LDR R1,=0XFFFF0000 ;向寄存器要賦的值
STR R1,[R0,#Periphral_Register_OFS] ;向外設Peripheral基地址偏移OFS的寄存器Register賦值0xFFFF0000
Keil的啟動代碼部分有兩個注意的地方:
1、啟動代碼中有很多IF語句,如:IF :DEF:RAM_INTVEC。這就可以通過在Options/Asm對話框中的Define中填入RAM_INTVEC就可以實現中斷向量從Flash到RAM的拷貝。同理,還有IF :DEF:REMAP等等;
2、帶有Keil特色的MICROLIB,通過在Options/Target中選擇“Used MICROLIB”,比不使用微庫相比生成的代碼較小。不過除此之外,應該還有其他的關系,因為我們的程序如果選擇不使用微庫的話,就執行不成功。對于微庫只有這些很片面的理解,還請老手指教。
總而言之,Keil中的啟動代碼還是比較好理解的,而且借助圖形化配置向導,可以更快的上手,以實現自己的啟動代碼。
下面要說說Keil下,怎樣實現程序在片外Norflash運行、片外SRAM調試、片外SRAM運行。
|