4

S5PV210 | 裸机汇编LED流水灯实验 - 大飞歌

 1 year ago
source link: https://www.cnblogs.com/feige1314/p/17392057.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

S5PV210 | 裸机汇编LED流水灯实验


x210bv3s

1.原理图

1069917-20230511195343818-798803893.png

上图中,当按下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-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

SD卡启动

1.OM5开关打到开发板靠下侧(选择启动方式):

1069917-20230511195343842-313884865.png
1069917-20230511195343830-312741425.png

2.将BIN文件下载到SD

2-1.Windos下使用x210_Fusing_Tool.exe下载(注意使用管理员模式打开)

1069917-20230511195343815-911185614.png

清理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,会有打印调试信息输出;

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK