一. ARM裸機之Makefile
1.1. Makefile 分析
led.bin: led.o arm-linux-ld -Ttext 0x0 -o led.elf $^ arm-linux-objcopy -O binary led.elf led.bin arm-linux-objdump -D led.elf > led_elf.dis gcc mkv210_image.c -o mkx210 ./mkx210 led.bin 210.bin %.o : %.S arm-linux-gcc -o $@ $< -c %.o : %.c arm-linux-gcc -o $@ $< -c clean: rm *.o *.elf *.bin *.dis mkx210 -f
1.1.1. arm-linux-ld -Ttext 0x0 -o led.elf $^
a. -Ttext 0x0將鏈接起始地址設定為0x00
b. -o led.elf表示鏈接后生成的elf文件
c. $^表示所有的依賴文件
PS: Makefile有三個非常有用的變量。分別是$@,$^,$<代表的意義分別是:
$@--目標文件,$^--所有的依賴文件,$<--第一個依賴文件。
示例:
main: main.o mytool1.o mytool2.o gcc -o main main.o mytool1.o mytool2.o #等效於 gcc -o $@ $^
1.1.2. arm-linux-objcopy -O binary led.elf led.bin
a. arm-linux-objcopy 被用來復制一個目標文件的內容到另一個文件中.此選項可以進行格式的轉換.在實際編程的,用的最多的就是將ELF格式的可執行文件轉換為二進制文件
b. 此命令是將elf文件轉為二進制bin文件
1.1.3. arm-linux-objdump -D led.elf > led_elf.dis
a. 此命令是將elf文件反編譯成匯編文件
1.1.4. gcc mkv210_image.c -o mkx210
a. 此語句是編譯mkv210_image.c文件
b. mkv210_image.c這個程序其實最終不是在開發板上執行的,而是在主機linux中執行的,因此編譯這個程序用gcc而不是用arm-linux-gcc。
1.1.5. ./mkx210 led.bin 210.bin
a. 此命令是在linux主機中運行mkx210程序。
b. led.bin 210.bin是運行此程序傳入的參數, argc = 3; argv[0] = "./mkx210" argv[1] = led.bin argv[2] = 210.bin
c. 目的是通過執行這個mkx210 程序而由led.bin得到210.bin。(210.bin(其實進入了16字節的頭)是通過SD卡啟動時的裸機鏡像,這個鏡像需要由led.bin來加工的到,加工的具體方法和原理要看mkv210_image.c)
d. 210啟動后先執行內部iROM中的BL0,BL0執行完后會根據OMpin的配置選擇一個外部設備來啟動(有很多,我們實際使用的有2個:usb啟動和SD卡啟動)。在usb啟動時內部BL0讀取到BL1后不做校驗,直接從BL1的實質內部0xd0020010開始執行,因此usb啟動的景象led.bin不需要頭信息,因此我們從usb啟動時直接將鏡像下載到0xd0020010去執行即可,不管頭信息了;從SD啟動時,BL0會首先讀取sd卡得到完整的鏡像(完整指的是led.bin和16字節的頭),然后BL0會自己根據你的實際鏡像(指led.bin)來計算一個校驗和checksum,然后和你完整鏡像的頭部中的checksum來比對。如果對應則執行BL1,如果不對應則啟動失敗(會轉入執行2st啟動,即SD2啟動。如果這里已經是2st啟動了,這里校驗通不過就死定了)。
/* * mkv210_image.c的主要作用就是由usb啟動時使用的led.bin制作得到由sd卡啟動的鏡像210.bin * * 本文件來自於友善之臂的裸機教程,據友善之臂的文檔中講述,本文件是一個熱心網友提供,在此表示感謝。 */ /* 在BL0階段,Irom內固化的代碼讀取nandflash或SD卡前16K的內容, * 並比對前16字節中的校驗和是否正確,正確則繼續,錯誤則停止。 */ #include <stdio.h> #include <string.h> #include <stdlib.h> #define BUFSIZE (16*1024) #define IMG_SIZE (16*1024) #define SPL_HEADER_SIZE 16 //#define SPL_HEADER "S5PC110 HEADER " #define SPL_HEADER "****************" int main (int argc, char *argv[]) { FILE *fp; char *Buf, *a; int BufLen; int nbytes, fileLen; unsigned int checksum, count; int i; // 1. 3個參數 if (argc != 3) { printf("Usage: %s <source file> <destination file>\n", argv[0]); return -1; } // 2. 分配16K的buffer BufLen = BUFSIZE; Buf = (char *)malloc(BufLen); if (!Buf) { printf("Alloc buffer failed!\n"); return -1; } memset(Buf, 0x00, BufLen); // 3. 讀源bin到buffer // 3.1 打開源bin fp = fopen(argv[1], "rb"); if( fp == NULL) { printf("source file open error\n"); free(Buf); return -1; } // 3.2 獲取源bin長度 fseek(fp, 0L, SEEK_END); // 定位到文件尾 fileLen = ftell(fp); // 得到文件長度 fseek(fp, 0L, SEEK_SET); // 再次定位到文件頭 // 3.3 源bin長度不得超過16K-16byte count = (fileLen < (IMG_SIZE - SPL_HEADER_SIZE)) ? fileLen : (IMG_SIZE - SPL_HEADER_SIZE); // 3.4 buffer[0~15]存放"S5PC110 HEADER " memcpy(&Buf[0], SPL_HEADER, SPL_HEADER_SIZE); // 3.5 讀源bin到buffer[16] nbytes = fread(Buf + SPL_HEADER_SIZE, 1, count, fp); if ( nbytes != count ) { printf("source file read error\n"); free(Buf); fclose(fp); return -1; } fclose(fp); // 4. 計算校驗和 // 4.1 從第16byte開始統計buffer中共有幾個1 // 4.1 從第16byte開始計算,把buffer中所有的字節數據加和起來得到的結果 a = Buf + SPL_HEADER_SIZE; for(i = 0, checksum = 0; i < IMG_SIZE - SPL_HEADER_SIZE; i++) checksum += (0x000000FF) & *a++; // 4.2 將校驗和保存在buffer[8~15] a = Buf + 8; // Buf是210.bin的起始地址,+8表示向后位移2個字,也就是說寫入到第3個字 *( (unsigned int *)a ) = checksum; // 5. 拷貝buffer中的內容到目的bin // 5.1 打開目的bin fp = fopen(argv[2], "wb"); if (fp == NULL) { printf("destination file open error\n"); free(Buf); return -1; } // 5.2 將16k的buffer拷貝到目的bin中 a = Buf; nbytes = fwrite( a, 1, BufLen, fp); if ( nbytes != BufLen ) { printf("destination file write error\n"); free(Buf); fclose(fp); return -1; } free(Buf); fclose(fp); return 0; }
1.1.6 目標文件:%.o :
a. 由依賴文件.S生成.o文件
%.o : %.S
arm-linux-gcc -o $@ $< -c
b. 由依賴文件.c生成.o文件
%.o : %.c
arm-linux-gcc -o $@ $< -c
1.1.7. 目標clean
a. 當make clean時便會執行rm命令!但並不用生成目標文件
clean:
rm *.o *.elf *.bin *.dis mkx210 -f
二. 其他工程文件
2.1. write2sd腳本文件
a. 該文件很簡單,就是將210.bin燒錄到sd卡中
#!/bin/sh sudo dd iflag=dsync oflag=dsync if=210.bin of=/dev/sdb seek=1
2.2. led.S文件
a. 此文件下才是真正的裸機程序
b. 用 b . 來實現死循環, bl比b多做一步,在跳轉前,bl會把當前位置保存在r14(即lr寄存器),當跳轉代碼結束后,用mov pc,lr指令跳回來,這實際上就是C語言執行函數的用法,
匯編里調子程序都用bl,執行完子函數后,可以用mov pc,lr跳回來
c. 用.global把_start鏈接屬性改為外部,匯編程序是從_start開始執行的
/* * file name :led.S * author: MUSK * description:test led code */ .global _start _start: ldr r1, =0xE0200240 @PORT GROUP GPJ0 CONTROL REGISTER address ldr r0, =0x00111000 @config corresponding gpio str r0, [r1] while: ldr r1, =0xE0200244 @Port Group GPJ0 Control Register address ldr r0, =~(0x01<< 3 ) @confid register data str r0,[r1] bl delay ldr r1, =0xE0200244 @Port Group GPJ0 Control Register address ldr r0, =~(0x01<< 4 ) @confid register data str r0,[r1] bl delay ldr r1, =0xE0200244 @Port Group GPJ0 Control Register address ldr r0, =~(0x01<< 5 ) @confid register data str r0,[r1] bl delay ldr r1, =0xE0200244 @Port Group GPJ0 Control Register address ldr r0, =~(0x01<< 4 ) @confid register data str r0,[r1] bl delay b while @the same with while(1); // delay function delay: ldr r2, = 9000000 ldr r3, = 0x00 delay_loop: sub r2, r2, #1 @r2-- cmp r2, r3 @compare r2 r3 bne delay_loop mov pc,lr
三. 編譯燒錄程序
3.1. 通過USB燒錄,使用window下dwn軟件燒錄
3.1.1 編譯生成目標文件
a. 使用make命令完成編譯鏈接以及生成目標文件
PS: 當make后面沒有帶目標時,默認是使用Makefile中第一個目標
root@ubuntu:/mnt/hgfs/windows_share/baseC/lesson1.4.13-LED# ls led.S Makefile mkv210_image.c write2sd 說明.txt root@ubuntu:/mnt/hgfs/windows_share/baseC/lesson1.4.13-LED# make arm-linux-gcc -o led.o led.S -c arm-linux-ld -Ttext 0x0 -o led.elf led.o arm-linux-objcopy -O binary led.elf led.bin arm-linux-objdump -D led.elf > led_elf.dis gcc mkv210_image.c -o mkx210 ./mkx210 led.bin 210.bin root@ubuntu:/mnt/hgfs/windows_share/baseC/lesson1.4.13-LED# ls 210.bin led.elf led.o Makefile mkx210 說明.txt led.bin led_elf.dis led.S mkv210_image.c write2sd root@ubuntu:/mnt/hgfs/windows_share/baseC/lesson1.4.13-LED#
3.1.2. 使用dnw工具燒錄
a. 燒錄地址:0xd0020010
b. 燒錄文件是led.bin
3.2. 通過SD卡燒錄,使用write2sd腳本
3.2.1. 編譯生成目標文件(同3.1.1.)
3.2.1. SD卡燒錄使用的文件是x210.bin,此文件帶16字節校驗頭
root@ubuntu:/mnt/hgfs/windows_share/baseC/lesson1.4.13-LED# ls /dev/sd* /dev/sda /dev/sda1 /dev/sda2 /dev/sda5 /dev/sdb /dev/sdb1 root@ubuntu:/mnt/hgfs/windows_share/baseC/lesson1.4.13-LED# ./write2sd 32+0 records in 32+0 records out 16384 bytes (16 kB) copied, 0.143708 s, 114 kB/s root@ubuntu:/mnt/hgfs/windows_share/baseC/lesson1.4.13-LED#
