(二)S5pv210的GPIO使用_part1

本節內容:

        首先我們會給出LED燈閃爍的彙編代碼,然後把上節遺留下來的內容(即SD卡啓動時對映像文件的要求)進行補充,然後給出映像文件生成的源代碼。最後我們會介紹Makefile的基本書寫格式以及介紹gcc、objcopy、objdump的基本用法,對於彙編指令我不做詳細介紹,如果想學一點彙編的同學們還是買一本arm的書認真學一下彙編指令,會彙編指令也就那麼幾頁的內容(其實挺多的QAQ)。

start.S:

        在start.S代碼中主要實現了4個LED燈的閃爍,這只是一個示例的彙編代碼,其實用匯編實現一個複雜的功能是一個費力不討好的事,因爲彙編指令不值得過於糾結,什麼都用匯編寫那還需要C語言幹嘛?實在想糾結匯編的可以通過C語言寫出對應的代碼,然後一個gcc -S選項即可生成彙編代碼。我的硬件情況是這樣的,我們的4個LED分別接的是GPJ2[3:0],只要給0即可點亮LED,因此我們這裏要做的就是把GPJ2CON配置爲輸出模式,然後設置對應位爲0點亮LED,設置對應爲爲0則關閉LED,我們現在看看start.S的具體代碼:

.global _start


_start:
<span style="white-space:pre">	</span>//GPJ2CON[3:0] set output mode
<span style="white-space:pre">	</span>ldr r1, =0xE0200280
<span style="white-space:pre">	</span>ldr r0, =0x00001111
<span style="white-space:pre">	</span>str r0, [r1]
<span style="white-space:pre">	</span>
led_blink:
<span style="white-space:pre">	</span>//set led on => GPJ2DAT[3:0] is low
<span style="white-space:pre">	</span>ldr r1, =0xE0200284
<span style="white-space:pre">	</span>mov r0, #0
<span style="white-space:pre">	</span>str r0, [r1]
<span style="white-space:pre">	</span>bl delay
<span style="white-space:pre">	</span>
<span style="white-space:pre">	</span>//set led off => GPJ2DAT[3:0] is off
<span style="white-space:pre">	</span>ldr r1, =0xE0200284
<span style="white-space:pre">	</span>mov r0, #0xF
<span style="white-space:pre">	</span>str r0, [r1]
<span style="white-space:pre">	</span>bl delay


<span style="white-space:pre">	</span>b led_blink


halt:
<span style="white-space:pre">	</span>b halt


delay:
<span style="white-space:pre">	</span>mov r0, #0xD00000
delay_loop:
<span style="white-space:pre">	</span>cmp r0, #0
<span style="white-space:pre">	</span>sub r0, r0, #1
<span style="white-space:pre">	</span>bne delay_loop
<span style="white-space:pre">	</span>mov pc, lr
.end
         上面的代碼其實就是做了配置GPIO的工作模式(輸出),設置全部燈關閉(GPJ2DAT=0xFF),延時,設置全部燈打開(GPJ2DAT=0x00)。

SD卡啓動要求:

         前面講過,S5pv210上電時候根據OM[5:0]引腳識別是1st boot是哪個設備,然後把設備的前16K代碼拷貝到片內iram中,如果代碼有效則執行片內代碼。那麼問題來了,片內代碼怎麼樣纔算有效?芯片規定,程序的前16字節用於一些特別的信息(整個代碼的字節大小,代碼的校驗信息),因此我們生成的映像文件要求是:16字節的頭部 + 實際代碼,然後把映像文件通過dd命令拷貝到SD卡中,S5pv210的BL0通過校驗信息知道我們的程序是否是有誤(最怕的就是SD卡沒程序,卻選擇SD卡啓動,這時通過校驗信息知道SD卡內部數據無效就不會執行SD卡內的“代碼”了)。首先我給出程序頭的格式:


       映像文件的第一個字(4字節)保存的是整個映像文件的大小(我們設置成16K即可),第3個字是用於設置校驗信息,校驗信息是有生成規則的,S5pv210規定代碼中按照4個字節對齊,設置uint32_t cheksum=0,每次把4個字節取出來(假設保存在tmp中),執行cheksum+=tmp,直到所有字節都取出來計算過爲止,這時checksum中的值就是我們程序的校驗信息,把它保存在映像文件的第三個字的地方即可,最後把start.S編譯生成的代碼拷貝到整個映像文件的後面即完成鏡像文件的製作,這些工作都是由源文件mkv210_image.c完成的期待嗎如下所示:

#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  "


int main (int argc, char *argv[])
{
<span style="white-space:pre">	</span>FILE<span style="white-space:pre">		</span>*fp;
<span style="white-space:pre">	</span>char<span style="white-space:pre">		</span>*Buf, *a;
<span style="white-space:pre">	</span>int<span style="white-space:pre">		</span>BufLen;
<span style="white-space:pre">	</span>int<span style="white-space:pre">		</span>nbytes, fileLen;
<span style="white-space:pre">	</span>unsigned int<span style="white-space:pre">	</span>checksum, count;
<span style="white-space:pre">	</span>int<span style="white-space:pre">		</span>i;
<span style="white-space:pre">	</span>
<span style="white-space:pre">	</span>if (argc != 3)//使用格式:mkv210_image 編譯後的文件名  生成的映像文件的名字
<span style="white-space:pre">	</span>{
<span style="white-space:pre">		</span>printf("Usage: mkbl1 <source file> <destination file>\n");
<span style="white-space:pre">		</span>return -1;
<span style="white-space:pre">	</span>}

<span style="white-space:pre">	</span>BufLen = BUFSIZE;
<span style="white-space:pre">	</span>Buf = (char *)malloc(BufLen);//分配16K的內存,前16字節保存校驗信息
<span style="white-space:pre">	</span>if (!Buf)
<span style="white-space:pre">	</span>{
<span style="white-space:pre">		</span>printf("Alloc buffer failed!\n");
<span style="white-space:pre">		</span>return -1;
<span style="white-space:pre">	</span>}

<span style="white-space:pre">	</span>memset(Buf, 0x00, BufLen);//全部字節清零

<span style="white-space:pre">	</span>fp = fopen(argv[1], "rb");
<span style="white-space:pre">	</span>if( fp == NULL)
<span style="white-space:pre">	</span>{
<span style="white-space:pre">		</span>printf("source file open error\n");
<span style="white-space:pre">		</span>free(Buf);
<span style="white-space:pre">		</span>return -1;
<span style="white-space:pre">	</span>}
        //filelen就是16K,爲了代碼的通用性寫代碼的人裝逼了→_→
<span style="white-space:pre">	</span>fseek(fp, 0L, SEEK_END);
<span style="white-space:pre">	</span>fileLen = ftell(fp);
<span style="white-space:pre">	</span>fseek(fp, 0L, SEEK_SET);

<span style="white-space:pre">	</span>count = (fileLen < (IMG_SIZE - SPL_HEADER_SIZE))
<span style="white-space:pre">		</span>? fileLen : (IMG_SIZE - SPL_HEADER_SIZE);
        //設置頭部信息
<span style="white-space:pre">	</span>memcpy(&Buf[0], SPL_HEADER, SPL_HEADER_SIZE);
        //把程序拷貝到進去
<span style="white-space:pre">	</span>nbytes = fread(Buf + SPL_HEADER_SIZE, 1, count, fp);
<span style="white-space:pre">	</span>if ( nbytes != count )
<span style="white-space:pre">	</span>{
<span style="white-space:pre">		</span>printf("source file read error\n");
<span style="white-space:pre">		</span>free(Buf);
<span style="white-space:pre">		</span>fclose(fp);
<span style="white-space:pre">		</span>return -1;
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>fclose(fp);



<span style="white-space:pre">	</span>a = Buf + SPL_HEADER_SIZE;
<span style="white-space:pre">	</span>for(i = 0, checksum = 0; i < IMG_SIZE - SPL_HEADER_SIZE; i++)
<span style="white-space:pre">		</span>checksum += (0x000000FF) & *a++;

<span style="white-space:pre">	</span>a = Buf + 8;
<span style="white-space:pre">	</span>*( (unsigned int *)a ) = checksum;


<span style="white-space:pre">	</span>fp = fopen(argv[2], "wb");
<span style="white-space:pre">	</span>if (fp == NULL)
<span style="white-space:pre">	</span>{
<span style="white-space:pre">		</span>printf("destination file open error\n");
<span style="white-space:pre">		</span>free(Buf);
<span style="white-space:pre">		</span>return -1;
<span style="white-space:pre">	</span>}

<span style="white-space:pre">	</span>a = Buf;
<span style="white-space:pre">	</span>nbytes<span style="white-space:pre">	</span>= fwrite( a, 1, BufLen, fp);
<span style="white-space:pre">	</span>if ( nbytes != BufLen )
<span style="white-space:pre">	</span>{
<span style="white-space:pre">		</span>printf("destination file write error\n");
<span style="white-space:pre">		</span>free(Buf);
<span style="white-space:pre">		</span>fclose(fp);
<span style="white-space:pre">		</span>return -1;
<span style="white-space:pre">	</span>}


<span style="white-space:pre">	</span>free(Buf);
<span style="white-space:pre">	</span>fclose(fp);


<span style="white-space:pre">	</span>return 0;
}

Makefile文件:

        gcc 用於編譯文件,ld工具用於將多個目標文件連接成一個可執行文件(elf格式),objcoy將elf文件中的可執行代碼拿出來保存在一個文件裏頭(elf格式的文件含有很多調試信息,符號表等,我們要把它幹掉),objdump用於將elf文件生成對應的調試文件(其實就是以彙編的形式列出整個文件的代碼),makefile文件如下:

led.bin: start.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 mkmini210
	./mkmini210 led.bin 210.bin
	
%.o : %.S
	arm-linux-gcc -o $@ $< -c

%.o : %.c
	arm-linux-gcc -o $@ $< -c 

clean:
	rm *.o *.elf *.bin *.dis mkmini210 -f
         對於ld工具,-T指定連接的信息,例如  -Tlink.lds表示指定鏈接文件link.lds,-Ttext 0x0就是說可執行文件的鏈接地址是0地址處。objcopy的-O表示指定輸出文件格式,我們舍值爲binary(二進制輸出)其中led.elf爲輸入文件,生成的文件名爲led.bin。objdump的-D說的是debug,通過led.elf生成調試文件led_ellf.dis(重定向輸出)。



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