arm啓動

從開始搞ARM到現在將近半年多了,第一個項目搞得有些眉目了,終於感覺像是入門了,半年來,有開始的新鮮,中間的苦悶,到最後的欣喜。其中過程可謂曲折離奇,遇到了很到前人沒有遇到過的疑難雜症,當然很多時候是因爲我的粗心釀成的。曾經也有過放棄的念頭,那個焦慮,像得了狂躁症一樣。後來下定決心即使績效沒了,工作丟了也要搞完它。其實在這個過程中,看到跟我一樣的很多新人在論壇上發帖求助,可是很多時候回者寥寥無幾,可能問題太幼稚,也可能問題描述的不清楚。我發過很多帖子,甚至直接騷擾了網上很多的牛人,他們都給了我很大的幫助,但是我當時的想法太簡單了,總想着某個牛人能夠解決掉這個問題,現在想想,即使是牛人,沒有看到具體的問題也很難給你一個解決方法,遇到困難不能把希望完全寄託在別人身上,要挖掘自身潛力,一遍遍仔細看手冊,反覆試驗,不斷思考,問題肯定能解決掉,只是時間問題而已。再次要感謝公司對我的容忍,一個這麼簡單的東東允許我搞了這麼久。

   
其實,在前面的過程中,一直有寫點什麼的衝動,但是當時困難重重、前途未卜,也沒了這個心情。現在可以坐下來仔細總結下前面的問題,有現在都沒搞明白的,特向大家請教了;有解決掉的,那就說說經驗教訓,給其他人一些參考。首先聲明本人腦瓜笨,邏輯思維差,點一個燈點了一個多月,最後還發現沒點對。所以提到的問題可能很幼稚,說話也好像前言不搭後語,有興趣看的那就受累了哈。

   
先說說我們的這個塊板子,裸奔的at91sam9260,外擴NorflashSRAM,實現程序既可以跑在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要用到的軟件以及工具吧,這段時間我接連使用了IARKEILh-jtagJLink,都會用但是都不精通。最後是在KEIL+Jlink下完成的。

開發工具的選擇:
1
、編譯環境:IARKEILGCC……

   
開始我想也沒想就選擇了IAR,原因很簡單:ATMEL的例程很都是基於IAR的,而KEIL安裝目錄下的例程很少。匆忙安裝了IAR當時的最新版IAR5.20,完全沒有考慮可用的資源和交流的方便。然後才發現IAR5.X4.X有很大的不同,主要就是其中的鏈接器從XLINK換成了ILINK,所以配置文件也由XCL文件換成了ICF文件,初看後者好像比前者更簡單更易懂了,但是由於是新版,使用的人還不多,網上上的參考資料大部分還是基於4.X的,對於新手哪幾條語句還是很費解。

   
不過一根筋的我還是硬着頭皮堅持使用IAR5.20,直到遇到IAR的殺招:系統從慢時鐘想快時鐘切換時就跑飛了,都是死在lowlevelinit()中的這一句上:AT91C_BASE_PMC->PMC_MCKR |= AT91C_PMC_CSS_PLLA_CLK;然後將這一句放在主程序中,照死不誤。使用過程中,總彈出一個警告,大致意思就是說:IAR安裝目錄下,bin文件夾下的armlibsupport.dllmay be missing or corrupt.重新裝後還是老樣子。在LED閃爍程序上折騰一個多月未果後,才痛下決心轉向KEIL

   
用上KEIL才發現這玩意不是傳說中的弱智,相反特別適合我等菜鳥,感覺主要有三個方面特別好:啓動代碼的圖形化配置;在Flash跑不需要什麼配置文件,直接在option中添兩個地址值就可以了;只需點擊鼠標就可以實現將代碼從Flash自動拷貝到RAM中執行,不需要再寫什麼代碼拷貝程序了。
GCC
,沒用過,不懂。
2
、調試工具:JLINKH-JTAGULINK……
   
開始使用h-jtag,配合Wiggler使用,小巧且便宜,不得不佩服Twentyone前輩,在RAM中調試很方便,也可以通過H-Flasher將程序下載到Flash跑,使用h-flasher時要一個初始化文件,主要是初始化Flash相關寄存器。
   
後來看到DBJLINK都白菜價了,就從淘寶買了一個JLINK,主要是不用再用並口了,現在百元以內的JLINK大把,用JLINK是個不錯的選擇,但是調試片外的flash好像還不能無限斷點。
ULINK
,不太清楚,好像只支持KEIL,不過沒用過。

這次主要說說第一次搞ARM特別是從51直接跳到ARM的必須面對的幾個概念:REMAPBootloaderFlashloader

1
REMAP
   
提到REMAP。首先應想到什麼是MAP,英語不好,開始就斷章取義,MAP就是地圖嘛,Memory Map就是存儲器地圖,不過這個地圖的參考座標不是經緯度,而是地址,進而叫做存儲器映射。由於要適應不同存儲器容量要求的用途,ARM處理器本身的RAMROM並不是足夠大,所以很多時候要外擴一些存儲器,NorflashNANDFlashSDRAMSRAM……而對於ARM來說怎麼識別這些不同的存儲器呢,只能給每個分配一個獨立的地址,就相當於每個人有不同的名字。片內存儲器的地址一般出廠就固化好的,片外的話就根據每個存儲器所連接的外部總線片選而具有不同的地址。

   
所以REMAP,顧名思義就是存儲器的重新映射,即某些存儲器的地址又發生了變化。我就很不理解了,這地址本來就不好記,還變來變去的,麻不麻煩呀,學51的時候咋就沒這玩意呢?後來查了些資料,有些明白了,518位機,更重要的是51的主頻不高,8位的ROMFlash足夠匹配51的主頻,不用插入等待指令,所以程序直接在ROMFlash中跑影響不到系統的速度。而ARM就不同了,ARM32位機,但是Flash一般是8位或16位,32位的也有吧,好像價格很高。而且ARM的製品很高,動輒上百M,所以Flash的工藝達不到這個速度。如果程序跑在Flash中就要插入過多的等待指令,所以會影響ARM的性能。而RAM一般存取速度比較快,很容易構成32位,可以與高速的ARM匹配。更重要的是ARM上電後必須從0x0地址處取得指令,因此上電後必須將ROMFlash映射位0X0地址處,當時還產生了一個弱智的想法,既然RAM這麼好,爲啥還要ROMFlash,直接將程序下載到RAM中不就得了,後來才猛地想到RAM是易失型存儲器,掉電後啥也沒了,再上電後0X0處啥都沒有。而且還有一條,ARM的中斷向量表,既存放各個中斷入口地址的地方,一般放在0x0處,即ROMFlash中,爲了加快中斷響應速度,也應該將0X0映射到RAM中去。因此,ARM一般從ROMFlash啓動完成初始化,然後將應用程序拷貝到RAM,然後跳到RAM執行。

   
剛纔說的是,爲啥要REMAP,接下來說怎麼REMAP。開始的時候我就不清楚,都說REMAP,那怎麼才能完成REMAP呢?都是手冊看得少呀,其實上面說的已經很清楚了,我們用的at91sam9260更是簡單,有專門的寄存器可以配置,MATRIX_MRCR—Master Remap ControlRegister,向這個寄存器相應位寫1就可以了。網上還看到Samsung的某些ARM可以通過編程相應Bank寄存器改變其起始地址,來實現REMAP

   
下面以我們的at91sam9260的板子爲例詳細說說我對at91sam9260 REMAP的理解,開始Flash沒有任何程序,當然也沒有REMAP,此時將BMS接高,然後上電,此時的0X0地址處位於片內的ROM,由於ROM內好像固化了引導程序,所以此時串口會輸出“Rom Boot…>”字樣。而內部的SRAM0的起始地址還是在0x20 0000處,而片外Norflash起始地址是0x1000 0000處。然後我們利用h-flasherJ-Flash將生成的Bin文件下載到Norflash內,即起始地址爲0x1000 0000處。然後將BMS接低,此時Norflash被映射在0X0地址處,即此時Norflash的起始地址爲0X0(你可能要問那ROM的地址現在在哪兒呢?我也不知道,因爲Norflash的地址範圍是0X0~0X1F FFFF,而ROM的起始地址默認是0X10 0000,恰好在Norflash的範圍內,所以此時ROM哪兒去了?)此時上電,因爲0X0地址處即Norflash起始地址有八個合法的中斷向量,程序會從Norflash啓動,接着執行啓動代碼,初始化SMCPMC,然後Copy中斷向量表到內部SRAM0,然後,將MATRIX_MRCR寄存器相應位置1,實現REMAP,此時,Norflash的起始地址又變回0X1000 0000,而內部的SRAM0的起始地址又變回0x0了,系統如果發生異常,將從地址0X0處即內部SRAM0取中斷向量,而內部SRAM的訪問速度顯然高於外部的Norflash,所以提高程序性能。這是我對at91sam9260REMAP的理解,歡迎討論指教。

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下的啓動代碼的理解。 

Norflash被映射在0X0地址處時,此時Norflash的起始地址爲0X0,那ROM的地址現在在哪兒呢?因爲Norflash的地址範圍是0X0~0X1F FFFF,而ROM的起始地址默認是0X10 0000,恰好在Norflash的範圍內,所以此時ROM哪兒去了?
 


該死的啓動代碼

提起啓動代碼,我就嗷嗷鬱悶,IAR下的程序都死在了這裏,Keil中出現的問題很多都是通過對啓動代碼的修修補補才解決的,一句話:成也啓動代碼,敗也啓動代碼。

啓動代碼應該是剛接觸ARM的新手必須面對而又很頭痛的問題吧,剛開始我也很納悶,爲什麼搞個這玩意,學51的時候咋就沒見過呢。而且還都是彙編寫的,俺的彙編還停留在“MOV”階段,其他的不是很懂,沒辦法,誰讓彙編的效率高呢。提到啓動代碼還不得不老生常談一下其中要完成的任務:

1
、建立異常中斷向量表,ARM0X0開始給每個異常中斷分配4個字節的空間,一般存放一個跳轉指令(B)PC的裝載指令(LDRPC,X_Vector),當發生異常時,ARM從此處取得相應異常中斷處理程序入口地址,再跳轉執行;
2
ARM都是高速處理器,而在高速下啓動很可能會不穩定,所以在啓動代碼從慢時鐘開始運行,在適當的位置,從32.768K切換到高速運行;
3
ARM一般帶有片外存儲器,FlashSDRAM等,這些存儲器都需要初始化才能使用,這都是在啓動代碼中完成,但是Norflash的初始化要在時鐘初始化之前;
4
ARM有不同的模式,每種模式都需要相應的堆棧;
5
Copy異常中斷向量表到RAM,並實現REMAP,具體請參照上一節;
6
Copy可執行映像的數據段到RAM,並將ZI區清零。這個一般都是由編譯器完成的,IAR下是?main來實現,Keil中由__main實現。
現在啓動代碼可以看懂一些,不過自己寫啓動代碼還是很遙遠的事情。如果開始對啓動代碼很抵觸,可以考慮使用Keil,因爲Keil由啓動代碼的圖形化配置,直接點擊鼠標操作就可以實現自己的啓動代碼。下面結合我們at91sam9260的板子,說說Keil中的啓動代碼。

打開Keil生成的SAM9260.S,點擊左下角的“Configuration Wizard”進入圖形化配置嚮導,根據你的需要選擇參數,全部選擇完畢後,再點擊"TextEditor",將會看到生成的啓動代碼。
我靠,不是吧,將近2000行,開始你可能會很泄氣,但仔細一看,前面不都是些宏定義嘛,跟圖形化配置嚮導一一對應的,只有從1200多行的這一句開始的纔是真正的啓動代碼部分。
;----------------------- CODE--------------------------------------------------

               PRESERVE8 
開始是8PC裝載指令,注意第六個向量,即地址0X14處,存放可執行映像的大小,||Image$$ER_ROM1$$RO$$Length||+||Image$$RW_RAM1$$RW$$Length||

接下來是SMCPMC的初始化,我們的板子外擴了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就可以實現中斷向量從FlashRAM的拷貝。同理,還有IF      :DEF:REMAP等等;
2
、帶有Keil特色的MICROLIB,通過在Options/Target中選擇“Used MICROLIB”,比不使用微庫相比生成的代碼較小。不過除此之外,應該還有其他的關係,因爲我們的程序如果選擇不使用微庫的話,就執行不成功。對於微庫只有這些很片面的理解,還請老手指教。

總而言之,Keil中的啓動代碼還是比較好理解的,而且藉助圖形化配置嚮導,可以更快的上手,以實現自己的啓動代碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章