8.1第1個ARM裸板程序及引申(上)
第001節(jié)_輔線1_硬件知識_LED原理圖
當(dāng)我們學(xué)習(xí)C語言的時候,我們會寫個Hello程序。那當(dāng)我們寫ARM程序,也該有一個簡單的程序引領(lǐng)我們?nèi)腴T,這個程序就是點亮LED。
我們怎樣去點亮一個LED呢? 分為三步:
看原理圖,確定控制LED的引腳;
看主芯片的芯片手冊,確定如何設(shè)置控制這個引腳;
寫程序;
先來講講怎么看原理圖:
LED樣子有很多種,像插腳的,貼片的。

它們長得完全不一樣,因此我們在原理圖中將它抽象出來。
點亮LED需要通電源,同時為了保護(hù)LED,加個電阻減小電流。
控制LED燈的亮滅,可以手動開關(guān)LED,但在電子系統(tǒng)中,不可能讓人來控制開關(guān),通過編程,利用芯片的引腳去控制開關(guān)。

LED的驅(qū)動方式,常見的有四種。
方式1:使用引腳輸出3.3V點亮LED,輸出0V熄滅LED。
方式2:使用引腳拉低到0V點亮LED,輸出3.3V熄滅LED。
有的芯片為了省電等原因,其引腳驅(qū)動能力不足,這時可以使用三極管驅(qū)動。
方式3:使用引腳輸出1.2V點亮LED,輸出0V熄滅LED。
方式4:使用引腳輸出0V點亮LED,輸出1.2V熄滅LED。

由此,主芯片引腳輸出高電平/低電平,即可改變LED狀態(tài),而無需關(guān)注GPIO引腳輸出的是3.3V還是1.2V。 所以簡稱輸出1或0:
邏輯1-->高電平
邏輯0-->低電平
第002節(jié)_輔線1_硬件知識_S3C2440啟動流程與GPIO操作
在原理圖中,同名的Net表示是連在一起的。
怎么樣GPF4怎么輸出1或0?
1. 配置為輸出引腳;
2. 設(shè)置狀態(tài);
因此,設(shè)置GPFCON[9:8]=0b01,即GPF4配置為輸出;
設(shè)置GPFDAT[4]=1或者0,即輸出高電平或低電平;?
S3C2440框架:

S3C2440啟動流程:
Nor啟動:
Nor Flash的基地址為0,片內(nèi)RAM地址為0x4000 0000;
CPU讀出Nor上第1個指令(前4字節(jié)),執(zhí)行;
CPU繼續(xù)讀出其它指令執(zhí)行。
Nand啟動:
片內(nèi)4k RAM基地址為0,Nor Flash不可訪問;
2440硬件把Nand前4K內(nèi)容復(fù)制到片內(nèi)的RAM,然后CPU從0地址取出第1條指令執(zhí)行。?
第003節(jié)_編寫第1個程序點亮LED
在開始寫第1個程序前,先了解一些概念。
2440是一個SOC,它里面的CPU有R1、R2、R3……等 寄存器;
它里面的GPIO控制器也有很多寄存器,如 GPFCON、GPFDAT。
這兩個寄存器是有差異的,在寫代碼的時候,CPU里面的寄存器可以直接訪問,其它的寄存器要以地址進(jìn)行訪問。
把GPF4配置為輸出,需要把0x100寫入GPFCON這個寄存器,即寫到0x5600 0050上;
把GPF4輸出1,需要把0x10寫到地址0x5600 0054上;
把GPF4輸出0,需要把0x00寫到地址0x5600 0054上;
這里的寫法會破壞寄存器的其它位,其它位是控制其它引腳的,為了讓第一個裸板程序盡可能的簡單,才簡單粗暴的這樣處理。
寫程序需要用到幾條匯編代碼:
①LDR ?(load):讀寄存器
舉例:LDR R0,[R1]
假設(shè)R1的值是x,讀取地址x上的數(shù)據(jù)(4字節(jié)),保存到R0中;
②STR ?(store):寫寄存器
舉例:STR R0,[R1]
假設(shè)R1的值是x,把R0的值寫到地址x(4字節(jié));
③B ?跳轉(zhuǎn)
④MOV ?(move)移動,賦值
舉例1:MOV R0,R1
把R1的值賦值給R0;
舉例2:MOV R0,#0x100
把0x100賦值給R0,即R0=0x100;
⑤LDR
舉例:LDR R0,=0x12345678
這是一條偽指令,即實際中并不存在這個指令,他會被拆分成幾個真正的ARM指令,實現(xiàn)一樣的效果。
最后結(jié)果是R0=0x12345678。
為什么會引入偽指令?
在ARM的32位指令中,有些字節(jié)表示指令,有些字節(jié)表示數(shù)據(jù),因此表示數(shù)據(jù)的沒有32位,不能表示一個32位的任意值,只能表示一個較小的簡單值,這個簡單值稱為立即數(shù)。引入偽指令后,利用LDR可以為R0賦任意大小值,編譯器會自動拆分成真正的的指令,實現(xiàn)目的。
有了前面5個匯編指令的基礎(chǔ),我們就可以寫代碼了。
第一個程序只能是匯編,以前你們可能寫過單片機(jī)程序,一上來就寫main()函數(shù),那是編譯器幫你封裝好了。
第一個LED程序代碼如下:?


將代碼上傳到服務(wù)器,
先編譯:?

再鏈接:?

生成bin文件:?

以上的命令,要是我們每次都輸入會容易輸錯,因此我們把他們寫到一個文件里,這個文件就叫Makefile. 關(guān)于Makefile以后會講。本次所需的Makefile如下:?

以后只需要 使用 make 命令進(jìn)行編譯, make clean 命令進(jìn)行清理。
最后燒寫到開發(fā)板上,即可看到只有一個LED亮,符合我們預(yù)期。
第004節(jié)_匯編與機(jī)器碼
前面介紹過偽指令,偽指令是實際不存在的ARM命令,編譯器在編譯時轉(zhuǎn)換成存在的ARM指令。我們代碼中的ldr r1, =0x56000050
這條偽指令的真實指令時什么呢?
我們可以通過反匯編來查看。
在前面的Makefile中加上:?

上傳服務(wù)器,編譯。
生成的led_on.dis就是反匯編文件。led_on.dis如下:?

第一列是地址,第二列是機(jī)器碼,第三列是匯編;
在反匯編文件里可以看到,ldr r1, =0x56000050
被轉(zhuǎn)換成ldr r1, [pc, #20]
,pc+20
地址的值為0x56000050,通過這種方式為r1賦值。
對于立即數(shù)0x100而言,ldr r0,=0x100
即是轉(zhuǎn)換成了mov r0,#256
;
在2440這個SOC里面,R0-R15都在CPU里面,其中:

為什么 PC=當(dāng)前指令+8?
ARM指令采用流水線機(jī)制,當(dāng)前執(zhí)行地址A的指令,已經(jīng)在對地址A+4的指令進(jìn)行譯碼,已經(jīng)在讀取地址A+8的指令,其中A+8就是PC的值。
C/匯編(給人類方便使用的語言)———編譯器———>bin,含有機(jī)器碼(給CPU使用)?
第005節(jié)_編程知識_進(jìn)制
17個蘋果,有4種表示方式,它們表示同一個數(shù)值:
計算驗證:
十進(jìn)制:17=1x10^1 + 7x10^0;?
二進(jìn)制:17=1x2^4 + 0x2^3 + 0x2^2 + 0x2^1 + 1x2^0;?
八進(jìn)制:17=2x8^1 + 1x8^0;?
十六進(jìn)制:17=1x16^1 + 1x16^0;
為何引入二進(jìn)制?
在硬件角度看,晶體管只有兩個狀態(tài):on是1,off是0; 數(shù)據(jù)使用多個晶體管進(jìn)行表示,用二進(jìn)制描述,吻合硬件狀態(tài)。?
為何引入八進(jìn)制?
將二進(jìn)制的三位作為一組,把這一組作為一位進(jìn)行表示,就是八進(jìn)制。?
為何引入十六進(jìn)制?
將二進(jìn)制的四位作為一組,把這一組作為一位進(jìn)行表示,就是十六進(jìn)制。八進(jìn)制和十六進(jìn)制方便我們描述,簡化了長度。
如何快速的轉(zhuǎn)換2/8/16進(jìn)制: 首先記住8 4 2 1 ——>二進(jìn)制權(quán)重?
舉例1:
將二進(jìn)制0b01101110101轉(zhuǎn)換成八進(jìn)制: 將二進(jìn)制從右到左,每三個分成一組:

結(jié)果就是1565;
舉例2:
將二進(jìn)制0b01101110101轉(zhuǎn)換成十六進(jìn)制:
將二進(jìn)制從右到左,每四個分成一組:

結(jié)果就是375;
舉例3:
將十六進(jìn)制0xABC1轉(zhuǎn)換成二進(jìn)制: 將十六進(jìn)制從右到左,每個分成四位:

結(jié)果就是1010 1011 1100 0001;
在C語言中怎么表示這些進(jìn)制呢?
十進(jìn)制: int a = 96;?
八進(jìn)制: int a = 0140;//0開頭?
十六進(jìn)制: int a = 0x60;//0x開頭
用0b開頭表示二進(jìn)制,約定俗成的規(guī)定。?
視頻教程??
