S5PV210 | 裸機彙編LED流水燈實驗

大飛歌發表於2023-05-11

S5PV210 | 裸機彙編LED流水燈實驗


開發板:

x210bv3s

1.原理圖

上圖中,當按下POWER鍵後,VDD_5VVDD_IO會產生5V3.3V的電壓,其中D26無須GPIO控制,為常亮狀態,即我們所說的電源指示燈,D[22:25]對應的GPIO口如下:

LED指示燈 GPIO口 編號 動作
D22 GPJ_3 LED1 1:滅,0:亮
D23 GPJ0_4 LED2 1:滅,0:亮
D24 GPJ0_5 LED3 1:滅,0:亮
D25 GPD0_1 LED4 1:滅,0:亮

對應的GPIO口輸出低電平,點亮LED;反之,熄滅LED燈;

2.Datasheet相關

1.S5PV210 RISC微處理器使用者手冊:
	S5PV210_UM_REV1.1.pdf
	獲取方式:可在CSDN搜尋下載,也可以@大飛歌獲取

2.應用手冊(內部ROM啟動):
	S5PV210_iROM_ApplicationNote_Preliminary_20091126.pdf 
	獲取方式:可網路搜尋下載,也可以@大飛歌獲取
	中文文件地址:https://blog.csdn.net/I_feige/article/details/104848609
	
3.底板電路原理圖:
	x210bv3s.pdf
	下載連結:https://download.csdn.net/download/i_feige/11877902

控制LED GPIO的暫存器設定詳細參見以下章節(S5PV210_UM_REV1.1.pdf):

V210_ Book cover 
errata 
section 01_ overview 
section 02_ system 
section 03_ bus 
section 04_ interupt 
section 05_ memory 
section 06_ dma 
section 07_ timer 
section 08_ connectivity _ storage 
section 09_ mutimedia 
section 10_ audio _ etc 
section 11_ securty 
section 12_ etc

2.2.7 PORT GROUP GPD0 CONTROL REGISTER 
2.2.7.1 Port Group GPD0 Control Register ( GPD0CON , R / W , Address 0xE020_00A0)
2.2.7.2 Port Group GPD0 Control Register ( GPD0DAT , R / W , Address 0xE020_00A4)
2.2.7.3 Port Group GPD0 Control Register ( GPD0PUD , R / W , Address 0xE020_00A8)
2.2.7.4 Port Group GPD0 Control Register ( GPD0DRV , R / W , Address 0xE020_00AC)
2.2.7.5 Port Group GPD0 Control Register ( GPD0CONPDN , R / W , Address 0xE020_00B0)
2.2.7.6 Port Group GPD0 Control Register ( GPD0PUDPDN , R / W , Address 0xE020_00B4)

2.2.20 PORT GROUP GPJ0 CONTROL REGISTER 
2.2.20.1 Port Group GPJ0 Control Register ( GPJ0CON , R / W , Address 0xE020_0240)
2.2.20.2 Port Group GPJ0 Control Register ( GPJ0DAT , R / W , Address 0xE020_0244)
2.2.20.3 Port Group GPJ0 Control Register ( GPJ0PUD , R / W , Address 0xE020_0248)
2.2.20.4 Port Group GPJ0 Control Register ( GPJ0DRV , R / W , Address 0xE020_024C)
2.2.20.5 Port Group GPJ0 Control Register ( GPJ0CONPDN , R / W , Address 0xE020_0250)
2.2.20.6 Port Group GPJ0 Control Register ( GPJ0PUDPDN , R / W , Address 0xE020_0254)

GPD0控制暫存器組的相關資訊(部分摘取如下):

2.2.7 PORT GROUP GPD0 CONTROL REGISTER
有六個控制暫存器,分別是 GPD0CONGPD0DATGPD0PUDGPD0DRVGPD0CONPDN
埠組 GPD0 控制暫存器中的 GPD0PUDPDN
2.2.7.1 埠組 GPD0 控制暫存器 (GPD0CON, R/W, Address = 0xE020_00A0)

GPD0CON Bit Description Initial State
GPD0CON[3] [15:12] 0000 = Input 0001 = Output 0010 = TOUT_3 0011 ~ 1110 = Reserved 1111 = GPD0_INT[3] 0000
GPD0CON[2] [11:8] 0000 = Input 0001 = Output 0010 = TOUT_2 0011 ~ 1110 = Reserved 1111 = GPD0_INT[2] 0000
GPD0CON[1] [7:4] 0000 = Input 0001 = Output 0010 = TOUT_1 0011 ~ 1110 = Reserved 1111 = GPD0_INT[1] 0000
GPD0CON[0] [3:0] 0000 = Input 0001 = Output 0010 = TOUT_0 0011 ~ 1110 = Reserved 1111 = GPD0_INT[0] 0000

2.2.7.2 埠組 GPD0 資料對映暫存器 (GPD0DAT, R/W, Address = 0xE020_00A4)

GPD0DAT Bit Description Initial State
GPD0DAT[3:0] [3:0] 當埠被配置為輸入埠時,對應的位是引腳狀態。 當埠配置為輸出埠時,引腳狀態與對應位相同。 當埠被配置為功能引腳時,將讀取未定義的值。 0000

2.2.7.3 埠組 GPD0 上、下拉配置暫存器 (GPD0PUD, R/W, Address = 0xE020_00A8)

GPD0PUD Bit Description Initial State
GPD0PUD[n] [2n+1:2n] n=0~3 00 = 上拉/下拉禁用 01 = 下拉啟用 10 = 上拉啟用 11 = 保留 0x0055

2.2.7.4 埠組 GPD0 驅動強度配置暫存器 (GPD0DRV, R/W, Address = 0xE020_00AC)

GPD0DRV Bit Description Initial State
GPD0DRV[n] [2n+1:2n] n=0~3 00 = 1x 10 = 2x 01 = 3x 11 = 4x 0x0000

2.2.7.5 埠組 GPD0 低功耗模式配置暫存器 (GPD0CONPDN, R/W, Address = 0xE020_00B0)

GPD0CONPDN Bit Description Initial State
GPD0[n] [2n+1:2n] n=0~3 00 = Output 0 01 = Output 1 10 = Input 11 = Previous state 0x00

2.2.7.6 埠組 GPD0 低功耗模式上拉/下拉暫存器 (GPD0PUDPDN, R/W, Address = 0xE020_00B4)

GPD0PUDPDN Bit Description Initial State
GPD0[n] [2n+1:2n] n=0~3 00 = 上拉/下拉禁用 01 = 下拉啟用 10 = 上拉啟用 11 = 保留 0x00

例如設定GPD0_1 IO口為輸出模式,拉高或者拉低(組合語言實現):

#define GPD0CON         0xE02000A0
#define GPD0DAT         0xE02000A4

	/* 初始化GPIO口(配置為輸出模式),下面是比較規範的一種寫法,也可參考程式碼實現(流水燈)相關部分 */    
ldr r0,=GPD0CON    		//r0=0xE02000A0
ldr r1,[r0]            	//將r0地址處的資料讀出,儲存到r1中(零偏移)
orr r1,r1,#0x0010      	//設定r1的第4位(置1),其他位保持不變[7:4]->0001=Output
str r1,[r0]            	//將r1中的內容傳輸到r0中數指定的地址記憶體中去
	
	/* 點亮LED4,GPIO口輸出低電平 */
ldr r0,=GPD0DAT			//r0=0xE02000A4
ldr r1,[r0]           	//將r0地址處的資料讀出,儲存到r1中(零偏移)
bic r1,r1,#0x0002		//清除r1的第1位(置0),其他位保持不變[1]
str r1,[r0]			  	//將r1中的內容傳輸到r0中數指定的地址記憶體中去

	/* 熄滅LED4,GPIO口輸出高電平 */
ldr r0,=GPD0DAT			//r0=0xE02000A4
ldr r1,[r0]          	//將r0地址處的資料讀出,儲存到r1中(零偏移)
orr r1,r1,#0x0002		//設定r1的第1位(置1),其他位保持不變[1]
str r1,[r0]			 	//將r1中的內容傳輸到r0中數指定的地址記憶體中去

3.程式碼

3-1.程式碼實現(流水燈,僅作演示)

/*******************************************************
 *   > File Name: start.S
 *   > Author: fly
 *   > Create Time: 2020年07月17日 星期五 07時56分19秒
 ******************************************************/
/*=====================================================
 * 彙編點亮led燈:對應GPIO口輸出低電平,點亮LED
 * D22->GPJ0_3
 * D23->GPJ0_4
 * D24->GPJ0_5
 * D25->PWMOUT1/GPD0_1
 *====================================================*/
#define GPD0CON         0xE02000A0
#define GPD0DAT         0xE02000A4
#define GPD0PUD         0xE02000A8

#define GPJ0CON         0xE0200240
#define GPJ0DAT         0xE0200244
#define GPJ0PUD         0xE0200248

#define PS_HOLD_CONTORL 0xE010E81C
#define WTCON           0xE2700000
#define SVC_STACK       0xD0037D80

//#define CONFIG_SYS_ICACHE_OFF   1

.global _start
_start:
    //給5v電源置鎖
    //LDR指令:從記憶體中將1個32位的字讀取到目標暫存器中
    //STR指令:將1個32位的字資料寫入到指令中指定的記憶體單元中
    //ORR指令:邏輯或操作指令
    //BIC指令:位清除指令
    //MOV指令:資料傳送
    ldr r0,=PS_HOLD_CONTORL     //r0=0xE010E81C
    ldr r1,[r0]                 //將r0地址處的資料讀出,儲存到r1中(零偏移)
    orr r1,r1,#0x300            //設定r1的第8、9位,其他位保持不變
    orr r1,r1,#0x1              //設定r1的第1位,其他位保持不變
    str r1,[r0]                 //將r1中的內容傳輸到r0中數指定的地址記憶體中去

    //關看門狗
    ldr r0, =WTCON
    mov r1, #0				   //將立即數0傳輸到r1處
    str r1, [r0]

    //開/關iCache
    // MRC指令:從協處理器暫存器傳資料到ARM暫存器
    // MCR指令:從ARM暫存器傳資料到協處理器暫存器
    mrc p15, 0, r0, c1, c0, 0
    #ifdef CONFIG_SYS_ICACHE_OFF
    bic r0, r0, #0x00001000     @ clear bit 12 (I) I-Cache
    #else
    orr r0, r0, #0x00001000     @ set bit 12 (I) I-Cache
    #endif
    mcr p15, 0, r0, c1, c0, 0

    //設定棧,以便呼叫c函式
    ldr sp, =SVC_STACK

led_init:
    /* LED初始化 */
    //把GPIO設定輸出模式
    ldr r0,=0x11111111
    ldr r1,=GPJ0CON
    str r0, [r1]                //把GPJ0所有的IO設定為輸出模式

    ldr r0,=0x00000010
    ldr r1,=GPD0CON
    str r0,[r1]                 //把GPD0_1設定為輸出模式

led_run:
    /* LED流水燈 */
    // 第1步:點亮LED1,其他熄滅
    ldr r0, =~(1<<3)            //r0=0xFFFFFFF7
    ldr r1, =GPJ0DAT            //r1=0xE0200244
    str r0, [r1]
    //熄滅LED4
    ldr r0, =~(0<<1)            //r0=0xFFFFFFFF
    ldr r1, = GPD0DAT
    str r0, [r1]
    bl delay

    // 第2步:點亮LED2,其他熄滅
    ldr r0, =~(1<<4)            //r0=0xFFFFFFEF
    ldr r1, =GPJ0DAT
    str r0, [r1]
    bl delay

    // 第3步:點亮LED3,其他熄滅
    ldr r0, =~(1<<5)            //r0=0xFFFFFFDF
    ldr r1, =GPJ0DAT
    str r0, [r1]
    bl delay

    //熄滅LED3/4/5,點亮LED4
    ldr r0, = ((1<<3)|(1<<4)|(1<<5))
    ldr r1, =GPJ0DAT
    str r0, [r1]
    ldr r0, =~(1<<1)            //r0=0xFFFFFFFD
    ldr r1, = GPD0DAT
    str r0, [r1]
    bl delay

    bl led_run

half:
    b half

    /* 延時函式:delay*/
delay:
    ldr r2,=9000000
    ldr r3,=0x0
delay_loop:
    //SUB指令:從暫存器Rn中減去shifter_operand表示的數值,
    //並將結果儲存在目標暫存器Rd中,並根據指令的執行結果
    //設定CPSR中的相應標誌位
    //SUB {<cond>} {s} <Rd>,<Rn>,<shifter_operand>
    sub r2,r2,#1                //r2 = r2 - 1
    //CMP指令:使用暫存器Rn的值減去shifter_operand的值,
    //根據操作的結果更新CPSR中相應的條件標誌位,以便後面
    //的指令根據相應的條件標誌位來判斷是否執行
    //CMP {<cond>} <Rn>,<shifter_operand>
    cmp r2, r3
    bne delay_loop
    mov pc,lr

配套編譯Makefile檔案:

# 將所有的.o檔案連結成.elf檔案,“-Ttext 0x0”
# 表示程式的執行地址是0x0,由於目前編寫的是位置
# 無關碼,可以在任一地址執行
# 將elf檔案抽取為可在開發板上執行的bin檔案
# 將elf檔案反彙編儲存在dis檔案中,除錯程式會用
# 新增檔案頭
# 編譯器版本:arm-2009q3
.PHONY: all clean tools

CROSS		?= arm-linux-
NAME		:= LED

LD			:= $(CROSS)ld
OC			:= $(CROSS)objcopy
OD			:= $(CROSS)objdump
CC			:= $(CROSS)gcc
MK			:= ../../tools/mk_image/mkv210_image

all:$(NAME).bin

$(NAME).bin : start.o
	$(LD) -Ttext 0x0 -o $(NAME).elf $^
	$(OC) -O binary $(NAME).elf $(NAME).bin
	$(OD) -D $(NAME).elf > $(NAME)_elf.dis
	$(MK) $(NAME).bin

# 將當前目錄下存在的彙編檔案及C檔案編譯成.o檔案
%.o : %.S
	$(CC) -o $@ $< -c
%.o : %.c
	$(CC) -o $@ $< -c

clean:
	$(RM) *.o *.elf *.bin *.dis *.sd

tools:
	make -C ../../tools/mk_image/

arm-linux-ld:一個連結程式工具,其作用主要是將彙編過的多個二進位制檔案進行連結,成為一個可執行的二進位制檔案,這個命令的選項有好多,具體用到的時候大家可以使用--help 選項來檢視具體的選項用法。

arm-linux-ld -Ttext 0x0 -o led.elf $^:這句話是將所有的依賴檔案連線成ELF格式檔案,在連線的過程中,-Ttext 0x0這個選項告訴聯結器我的這段程式需要被載入到RAM0x00000000地址處執行。所以在連線的時候第一條語句的連線地址就是0x00000000,第二條語句就是跟在其後面。有很多人都議論連線地址和執行地址這個怎麼說的都有。執行地址可以等於連線地址,還可以認為執行地址是pc指標指向的地址,就是正在執行指令的地址。只要理解了這個概念就可以了。

arm-linux-objcopy:被用來複制一個目標檔案的內容到另一個檔案中.此選項可以進行格式的轉換.在實際程式設計的,用的最多的就是

ELF格式的可執行檔案轉換為二進位制檔案

arm-linux-objdump:常用來顯示二進位制檔案資訊,常用來檢視反彙編程式碼


編譯:

fly@fly-vm:01-led_s$ make clean
rm -f *.o *.elf *.bin *.dis *.sd *.BIN
fly@fly-vm:01-led_s$ ls
Makefile  start.S
fly@fly-vm:01-led_s$ make
arm-linux-gcc -o start.o start.S -c
arm-linux-ld -Ttext 0x0 -o LED.elf start.o
arm-linux-objcopy -O binary LED.elf LED.bin
arm-linux-objdump -D LED.elf > LED_elf.dis
../../tools/mk_image/mkv210_image LED.bin
the checksum 0x000060EB for 228bytes, output: LED.bin.SD.BIN
fly@fly-vm:01-led_s$ ls
LED.bin  LED.bin.SD.BIN  LED.elf  LED_elf.dis  Makefile  start.o  start.S

3-2.工具mkv210_image程式碼

/*******************************************************************
 *   > File Name: mkv210_image.c
 *   > Author: fly
 *   > Create Time: 2021-06-17  4/24  12:03:22 +0800
 *   > Note: 將USB啟動時使用的BIN檔案製作得到SD啟動的Image
 *          計算校驗和,新增16位元組檔案頭,校驗和寫入第8位元組處
 *================================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define ERR_STR                 strerror(errno)
#define SPL_HEADER_SIZE         (16)
#define SPL_HEADER              "@S5PV210$$$$****"
#define IMG_SIZE                (16*1204)
#define FILE_PATH_LEN_MAX       (256)

char *mk_getCheckSumFile(char *binName)
{
    static char checkSumFileName[FILE_PATH_LEN_MAX] = {0};

    //snprintf(checkSumFileName, FILE_PATH_LEN_MAX, "%s%s", "sd.", binName);
    snprintf(checkSumFileName, FILE_PATH_LEN_MAX, "%s%s",  binName, ".SD.BIN");
    return (char*)checkSumFileName;
}

long mk_getFileLen(FILE* fp)
{
    static long fileLen = 0;
    fseek(fp, 0L, SEEK_END);
    fileLen = ftell(fp);
    fseek(fp, 0L, SEEK_SET);
    return fileLen;
}

int main(int argc, char* argv[])
{
    FILE* fps, *fpd;
    long nbytes, fileLen;
    unsigned int checksum, count;
    char *BUF = NULL, *pBUF = NULL;
    int i;

    if(argc != 2){
        printf("Usage: %s <bin-file>\n", argv[0]);exit(EXIT_FAILURE);
    }

    /* 開啟源BIN檔案 */
    fps = fopen(argv[1], "rb");
    if (fps == NULL){
        printf("fopen %s err: %s\n", argv[1], ERR_STR);
        exit(EXIT_FAILURE);
    }

    /* 建立目標BIN檔案 */
    fpd = fopen(mk_getCheckSumFile(argv[1]), "w+b");
    if (fpd == NULL){
        printf("fopen %s err: %s\n", mk_getCheckSumFile(argv[1]), ERR_STR);
        fclose(fps);exit(EXIT_FAILURE);
    }

    /* 獲取原始檔大小 */
    fileLen = mk_getFileLen(fps);
    if(fileLen < (IMG_SIZE - SPL_HEADER_SIZE)){
        count = fileLen;
    }else{
        count = IMG_SIZE - SPL_HEADER_SIZE;
    }

    BUF = (char *)malloc(IMG_SIZE);/* malloc 16KB BUF */
    if (BUF == NULL){
        printf("malloc err: %s\n", ERR_STR);
        fclose(fps);fclose(fpd);
        exit(EXIT_FAILURE);
    }
    memcpy(&BUF[0], SPL_HEADER, SPL_HEADER_SIZE);
    nbytes = fread(BUF+SPL_HEADER_SIZE, 1, count, fps);

    /* 計算檔案檢驗和 */
    pBUF = BUF + SPL_HEADER_SIZE;
    for(i = 0, checksum = 0; i< IMG_SIZE - SPL_HEADER_SIZE; i++)
    {
        checksum += (0x000000FF) & *pBUF++;
    }
    pBUF = BUF + 8;
    *((unsigned int *)pBUF) = checksum;

    /* 將校驗和原始檔寫入目標檔案 */
    fwrite(BUF, 1, IMG_SIZE, fpd);

    printf("the checksum 0x%08X for %ldbytes, output: %s\n", \
            checksum, fileLen, mk_getCheckSumFile(argv[1]));

    free(BUF);
    fclose(fps);
    fclose(fpd);

    return 0;
}

在這裡插入圖片描述

配套Makefile

.PHONY: all clean

CC              = gcc
SRC             = ${wildcard *.c}
BIN             = ${patsubst %.c, %, $(SRC)}
CFLAGS  = -g -Wall
RM              = rm -rf
PRJ_PATH= $(shell pwd)

all:$(BIN)

$(BIN):%:%.c
        @echo [CC] $@
        @$(CC) -o $@ $^ $(CFALGS) -D_PRJ_PATH_='"$(PRJ_PATH)"'

clean:
        $(RM) a.out $(BIN) .*.*.sw? *.sd

test:
        @echo $(PRJ_PATH)

.PHONY: clean test

4.執行

SD卡啟動

1.OM5開關打到開發板靠下側(選擇啟動方式):

2.將BIN檔案下載到SD

2-1.Windos下使用x210_Fusing_Tool.exe下載(注意使用管理員模式開啟)

清理x210_Fusing_Tool.exe檔案列表,進入目錄:C:\Users\fly\AppData\Roaming\SDFusing,然後刪除檔案config.ini

2-2.Linux下載BIN檔案到SD卡指令碼命令:

#!/bin/sh

#命令列引數檢測
if [ -n "$1" ];then
    echo "Source file: $1"
else
    echo "Usage:$0 <source_file>"
    exit -1
fi

#使用超級使用者許可權把210.bin讀取進來,經過處理再輸出到裝置sdb上,
#跳過該裝置的第一個block(每個block的大小為512B)
sudo dd iflag=dsync oflag=dsync if=$1 of=/dev/sdb seek=1

另一種更具體的指令碼寫法:

###########################################################
  # File Name: s5pv210-irom-sd.sh
  # Author: fly
  # Created Time: 2021-06-27  0/25  14:51:59 +0800
###########################################################
#!/bin/bash

# s5pv210 irom sd boot fusing tool
# display usage message
USAGE()
{
    echo Usage: $(basename "$0") '<device> <bootloader>'
    echo '      device      = disk device name of for SD card.'
    echo '      bootloader  = /path/to/*.bin.sd'
    echo 'e.g. '$(basename "$0")' /dev/sdb boot.bin.sd'
}

[ `id -u` == 0 ] || { echo "you must be root user"; exit 1;}
[ -z "$1" -o -z "$2" ] && { USAGE; exit 1; }

dev="$1"
xboot="$2"

# validate parameters
[ -b "${dev}" ] || { echo "${dev} is not a valid block device"; exit 1; }
[ X"${dev}" = X"${dev%%[0-9]}" ] || { echo "${dev} is a partition, please use device, perhaps ${dev%%[0-9]}"; exit 1; }
[ -f ${xboot} ] || { echo "${xboot} is not a bootloader binary file."; exit 1; }

# copy the full bootloader image to block device
dd if="${xboot}" of="${dev}" bs=512 seek=1 conv=sync

sync;
sync;
sync;

echo "^_^ The image is fused successfully"

3.將SD卡插入SD2通道,上電即可檢視程式執行狀況

接通電源,長按POWER鍵;可使用串列埠工具連線UART2,會有列印除錯資訊輸出;

5.參考

1.書籍:ARM嵌入式體系結構與介面技術(Cortex-A8版)(ARM Embedded Architecture and Interface Technology)

2.書籍:嵌入式LinuxC語言程式設計基礎教程(C Language Programming of Embedded Linux)

3.使用的交叉編譯器1:https://sourcery.mentor.com/public/gnu_toolchain/arm-none-linux-gnueabi/arm-2009q3-67-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2

4.編譯器下載地址2(CSDN):https://download.csdn.net/download/qq_37363920/12333876?utm_medium=distribute.pc_relevant_t0.none-task-download-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.baidujs&depth_1-utm_source=distribute.pc_relevant_t0.none-task-download-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.baidujs

5.專案地址:https://gitee.com/x210bv3s/s5pv210-noos-dev

6.S5PV210_UM_REV1.1.pdf:https://download.csdn.net/download/han1202012/8342745?utm_source=iteye_new

7.S5PV210_iROM_ApplicationNote_Preliminary_20091126.pdf:https://download.csdn.net/download/q171884957/8561553

8.S5PV210_iROM_ApplicationNote_Preliminary_20091126.pdf(中文版本):https://blog.csdn.net/I_feige/article/details/104848609

9.x210_Fusing_Tool.exe下載地址:https://download.csdn.net/download/i_feige/11937635

相關文章