很多從單片機(jī)或者從CM3轉(zhuǎn)到ARM9/ARM11的時(shí)候,一開始就講得是Uboot,Linux,然后就是什么QT等高級(jí)玩意,不像單片機(jī)有什么裸機(jī)開發(fā)的概念,的確,這些高級(jí)ARM本來就是用來跑系統(tǒng),誰用來它做裸機(jī)開發(fā)呢。但是如果我們要更好了解ARM的工作原理,,尤其是想進(jìn)階嵌入式中最難的一層,驅(qū)動(dòng)層的話,甚至想把其它的非Linux系統(tǒng)移植到ARM9/ARM11上。我們就無法避免要跟ARM跟底層的寄存器打交道,要了解ARM的內(nèi)核,時(shí)鐘,GPIO,SPI,等等外設(shè)的工作原理和配置。通過裸機(jī)的一些實(shí)驗(yàn),我們就可以像用STM32這樣來寫ARM9/ARM11代碼,讓我們更深入了解ARM的原理。
我之前因?yàn)樵谧霭袽F這個(gè)系統(tǒng)移植到S3C2416上,基本上都靠MDK來完成調(diào)試,對(duì)ARM9/ARM11的認(rèn)識(shí)有了進(jìn)一步的認(rèn)識(shí)。這里我就以S3C2416為例來講怎么建立開發(fā)環(huán)境和點(diǎn)亮LED燈。
1、裸機(jī)開發(fā)必要條件:玩過ARM9之上同學(xué)都知道,ARM9之類的ARM是沒有內(nèi)部Flash的話,我們不能直接用開發(fā)軟件來對(duì)燒寫代碼來調(diào)試硬件,這點(diǎn)跟我們開發(fā)單片機(jī)是最大的區(qū)別,也是導(dǎo)致很多人不知道怎么來進(jìn)行ARM9的裸機(jī)開發(fā)。有同學(xué)就說,沒有Flash,還有RAM,沒錯(cuò)S3C2416內(nèi)部是有RAM,但是很小,S3C2416的內(nèi)部RAM在用NAND啟動(dòng)時(shí)候,才8K而已,S3C2440的才4K,我們要在這8K的RAM既放存數(shù)據(jù)還要放代碼,那我們寫不了幾行代碼。所以最好的方法就是用Uboot,先把芯片的時(shí)鐘,SDRAM都初始化好,那我們就可以用SDRAM來調(diào)試代碼,一般都有64M的SDRAM,對(duì)我們來完全足夠用了。這里我只在開發(fā)上燒寫了Uboot,內(nèi)核跟文件系統(tǒng)都沒有燒,因?yàn)槿绻鸘boot引導(dǎo)了Linux內(nèi)核后,Linux內(nèi)核接到CPU的控制權(quán),Debug會(huì)被關(guān)掉,我們就無法再使用Jlink對(duì)開發(fā)進(jìn)行調(diào)試。你可以進(jìn)去Uboot上用nand erase來把內(nèi)核擦掉,不然你每次要調(diào)試的時(shí)候要注意不要讓Uboot引導(dǎo)內(nèi)核。
2、用MDK建立工程:新建工程大家都會(huì),我這里只說要點(diǎn)。選擇器件的話,選擇我們對(duì)應(yīng)ARM開發(fā)板上芯片,注意如果是MDK5.0以上,默認(rèn)是沒有安裝有ARM7/ARM9有器件庫的,我有發(fā)帖子共享給大家,如果用是用MDK5.0以上的同學(xué)翻翻帖子去下載。
點(diǎn)確定后,然后是打開工程設(shè)置
其中IROM就是代碼區(qū)的地址,前面說了我們沒法用NAND,只能通過SDRAM來調(diào)試。我的S3C2416的開發(fā)是64M的DDR,地址就是從0X30000000開始到0x34000000,這里我一半用做ROM,從0x32000000開始,另一半用做RAM。
Debug設(shè)置,一樣我們?cè)O(shè)置成J-Link。點(diǎn)OK就可以完成工程的建立。
3、點(diǎn)亮LED燈:用過STM32的同學(xué)都知道,新工程后,要增加啟動(dòng)代碼,然后才是C代碼來寫Main函數(shù)。這里我們沒有啟動(dòng)代碼,也不知道怎么找到Main函數(shù)。沒關(guān)系,我們自己來寫個(gè)最簡(jiǎn)單的啟動(dòng)代碼,讓它來轉(zhuǎn)到我們的C代碼。新建一個(gè)startup.s的匯編代碼文件,然后加入到工程上來,代碼如下
IMPORT Main ;//c語言入口函數(shù)
PRESERVE8 ;//字節(jié)對(duì)齊
CODE32 ;//ARM代碼
AREA RESET,CODE,READONLY ;//reset名,代碼段,只讀
ENTRY ;//程序入口
bl Main
END
IMPORT Main這里就引入我們C文件里的Main函數(shù),ENTRY 表示的這里代碼的開始,編譯器就從這里開發(fā)編譯代碼。 bl Main這里就是跳轉(zhuǎn)到我們的Main函數(shù)。END就是說這里代碼的結(jié)尾。相關(guān)的匯編指令大家可以去找《DUI0204HC_rvct_assembler_guide 》這本書,里面就講到MDK所有的匯編指令。這里我們就寫出來最簡(jiǎn)單的啟動(dòng)代碼。接著就是寫我們的C代碼了,增加Main.c文件到工程中,我的代碼如下:
#define GPBCON *(volatile unsigned int*)0x56000010
#define GPBDAT *(volatile unsigned int*)0x56000014
#define GPBUDP *(volatile unsigned int*)0x56000018
int Main(void)
{
unsigned int i;
GPBCON = 1 << 2;//設(shè)置PB1為輸出
GPBUDP = 2 << 2;//設(shè)置PB1為上拉
GPBDAT &= ~(1 << 1);//置PB1的輸出為0
while(1)
{
}
}
點(diǎn)亮LED的原理大家都知道,就不用我再說了,我的開發(fā)的LED的控制腳對(duì)應(yīng)的是PB1,這里我就要對(duì)GPIOB第一個(gè)引腳進(jìn)去配置。代碼的開頭我定義了GPIOB口相關(guān)的寄存器。具體的自然也就是翻對(duì)應(yīng)ARM的參考手冊(cè),下面是S3C2416手冊(cè)相關(guān)GPIOB的寄存器
這里就以GPBCON的寄存器來說,折騰寄存器的同學(xué)一看明白怎么做了。GPBCON是來控制GPIO的輸入還是輸出的,我們這里把GPB1設(shè)置成輸出,然后設(shè)置GPBDAT寄存器讓GPB1輸出低電平,LED就會(huì)被點(diǎn)亮。
接下就是編譯,代碼沒寫錯(cuò)就會(huì)編譯成功,點(diǎn)調(diào)試之前要注意開發(fā)不要進(jìn)去Linux,如果你沒有擦除內(nèi)核,就要讓開發(fā)板進(jìn)入U(xiǎn)boot,如下:
點(diǎn)調(diào)試,彈出下面對(duì)話框,提示Jlink不支持S3C2416,沒關(guān)系,點(diǎn)Yes
然后我們選一個(gè)最接近S3C2416的S3C2410A。如果是用S3C2440的話應(yīng)該就不會(huì)有這個(gè)問題。
然后點(diǎn)OK進(jìn)去調(diào)試。進(jìn)去調(diào)試后注意不要點(diǎn)運(yùn)行。因?yàn)檫@時(shí)候的PC指針地址是0x0,而我們的代碼并不是在0x0,而是在0x32000000。我們手工修改PC指針為0x32000000
然后我們單步執(zhí)行,可以看代碼跑起來了,代碼執(zhí)行于While(1)的時(shí)候,就可以看到LED燈亮起來了。