編寫第一個裸機程序


一. 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

    
    
View Code

    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 $@ $^
View Code

    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;
}
View Code

    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
View Code

三. 編譯燒錄程序

  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# 
View Code

    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# 
View Code

 

 

 

 

      

 

   

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM