单片机
返回首页

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

2024-12-05 来源:cnblogs

开发板:

x210bv3s

1.原理图

上图中,当按下POWER键后,VDD_5V和VDD_IO会产生5V和3.3V的电压,其中D26无须GPIO控制,为常亮状态,即我们所说的电源指示灯,D[22:25]对应的GPIO口如下:

LED指示灯GPIO口编号动作
D22GPJ_3LED11:灭,0:亮
D23GPJ0_4LED21:灭,0:亮
D24GPJ0_5LED31:灭,0:亮
D25GPD0_1LED41:灭,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
有六个控制寄存器,分别是 GPD0CON、GPD0DAT、GPD0PUD、GPD0DRV、GPD0CONPDN 和
端口组 GPD0 控制寄存器中的 GPD0PUDPDN。
2.2.7.1 端口组 GPD0 控制寄存器 (GPD0CON, R/W, Address = 0xE020_00A0)

GPD0CONBitDescriptionInitial 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)

GPD0DATBitDescriptionInitial State
GPD0DAT[3:0][3:0]当端口被配置为输入端口时,对应的位是引脚状态。 当端口配置为输出端口时,引脚状态与对应位相同。 当端口被配置为功能引脚时,将读取未定义的值。0000

2.2.7.3 端口组 GPD0 上、下拉配置寄存器 (GPD0PUD, R/W, Address = 0xE020_00A8)

GPD0PUDBitDescriptionInitial State
GPD0PUD[n][2n+1:2n] n=0~300 = 上拉/下拉禁用 01 = 下拉启用 10 = 上拉启用 11 = 保留0x0055

2.2.7.4 端口组 GPD0 驱动强度配置寄存器 (GPD0DRV, R/W, Address = 0xE020_00AC)

GPD0DRVBitDescriptionInitial State
GPD0DRV[n][2n+1:2n] n=0~300 = 1x 10 = 2x 01 = 3x 11 = 4x0x0000

2.2.7.5 端口组 GPD0 低功耗模式配置寄存器 (GPD0CONPDN, R/W, Address = 0xE020_00B0)

GPD0CONPDNBitDescriptionInitial State
GPD0[n][2n+1:2n] n=0~300 = Output 0 01 = Output 1 10 = Input 11 = Previous state0x00

2.2.7.6 端口组 GPD0 低功耗模式上拉/下拉寄存器 (GPD0PUDPDN, R/W, Address = 0xE020_00B4)

GPD0PUDPDNBitDescriptionInitial State
GPD0[n][2n+1:2n] n=0~300 = 上拉/下拉禁用 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 {} {s} ,,

    sub r2,r2,#1                //r2 = r2 - 1

    //CMP指令:使用寄存器Rn的值减去shifter_operand的值,

    //根据操作的结果更新CPSR中相应的条件标志位,以便后面

    //的指令根据相应的条件标志位来判断是否执行

    //CMP {} ,

    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这个选项告诉连接器我的这段程序需要被加载到RAM的0x00000000地址处执行。所以在连接的时候第一条语句的连接地址就是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

#include

#include

#include

 

#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 n', argv[0]);exit(EXIT_FAILURE);

    }

 

    /* 打开源BIN文件 */

    fps = fopen(argv[1], 'rb');

    if (fps == NULL){

        printf('fopen %s err: %sn', argv[1], ERR_STR);

        exit(EXIT_FAILURE);

    }

 

    /* 创建目标BIN文件 */

    fpd = fopen(mk_getCheckSumFile(argv[1]), 'w+b');

    if (fpd == NULL){

        printf('fopen %s err: %sn', 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: %sn', 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: %sn',

            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:UsersflyAppDataRoamingSDFusing,然后删除文件config.ini;


2-2.Linux下载BIN文件到SD卡脚本命令:


#!/bin/sh

#命令行参数检测

if [ -n '$1' ];then

    echo 'Source file: $1'

else

    echo 'Usage:$0 '

    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') ' '

    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


进入单片机查看更多内容>>
相关视频
  • 【TI MSPM0 应用实战】智能小车+工业角度编码器+血氧仪+烟雾探测器!硬核参考设计详解!

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

  • 直播回放: Microchip Timberwolf™ 音频处理器在线研讨会

  • 基于灵动MM32W0系列MCU的指夹血氧仪控制及OTA升级应用方案分享

精选电路图
  • 1瓦线性调频增强器

  • 家用电器遥控器

  • 12V 转 28V DC-DC 变换器(基于 LM2585)

  • 红外开关

  • DS1669数字电位器

  • HA1377 桥式放大器 BCL 电容 17W(汽车音频)

    相关电子头条文章