1、查阅原理图、数据手册
1.1、因为Tiny 210的LED在核心板上,所以通过通过查阅核心板,获得控制LED的GPIO引脚
得知控制LED的主要引脚是GPJ2_(0~3)
1.2、查阅s5pv210数据手册
(1)首先要明白这里控制LED的寄存器主要有两个:
GPJ2CON:设置引脚的状态
GPJ2DAT:该寄存器中的内容与GPJ2引脚高低电平状态同步,换句话说就是可以通过在该寄存器中写入内容来控制GPJ2引脚的高低电平,1为高电平,0为低电平。
(2)主要寄存器地址
GPJ2CON 0xE020_0280
GPJ2DAT 0xE020_0284
2、编程
2.1、start.s
//首先通过查阅原理图,得到控制4颗LED灯的引脚
//GPJ2_(0~3)对应LED(1~4)
//通过查阅数据手册与用户手册得到
//与控制LED有关的寄存器为GPJ2CON、GPJ2DAT
//GPJ2CON的地址为0xE020_0280、0xE020_0280的地址为0xE020_0284
//需要将GPJ2CON的bit(0~15)设置为0x1111
//如果要LED亮需要将GPJ2DAT的bit(0~3)设置为0x0
//如果要LED灭需要将GPJ2DAT的bit(0~3)设置为0xf
.global _start
//设置GPJ2CON的bit(0~15),配置GPJ2_0/1/2/3引脚为输出模式
_start:
ldr r0, =0x1111
ldr r1, =0xE0200280
str r0, [r1]
//此处赋给r2一个随机数0x1000,目的是为后面的循环是设定一个时间
mov r2, #0x1000
led_blink:
//LED亮
ldr r0, =0x0
ldr r1, =0xE0200284
str r0, [r1]
//延时
bl delay
//LED灭
ldr r0, =0xf
ldr r1, =0xE0200284
str r0, [r1]
//延时
bl delay
//下面为循环函数
sub r2, r2, #1
cmp r2, #0
beq led_blink
//此时mov r2, #0x1000和后面的循环函数
//两者中间为循环体一般仿佛声明了
//在汇编函数结尾需要一个死循环
half:
b half
//前面bl delay调用了函数delay
//需要在这里写出delay函数
delay:
mov r0, #0x100000
delay_loop:
sub r0, r0, #1
cmp r0, #0
beq delay_loop
//调用完函数需要返回,所以mov pc, lr起到返回作用
mov pc, lr
2.2、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
arm-linux-ld(链接)、arm-linux-gcc(编译)、arm-linux-objcopy(生成可以烧录的bin文件)、 arm-linux-objdump(反编译工具)均为交叉编译工具 。
mkv210_image.c为将led.bin转换为210.bin的c代码
%.o : %.S、%.o : %.c就是将.c、.s文件编译为.o文件
2.3、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[])
{
FILE *fp;
char *Buf, *a;
int BufLen;
int nbytes, fileLen;
unsigned int checksum, count;
int i;
// 1. 3个参数
if (argc != 3)
{
printf("Usage: mkbl1 <source file> <destination file>\n");
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
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;
*( (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;
}
注:这个代码是我搬砖过来的
代码主要内容:
第一步 分配 16k 的 buffer;
第二步 将 led.bin 读到buffer 的第 16byte 开始的地方;
第三步 计算校验和,并将校验和保存在 buffer 第 8~11byte 中;
第四步 将 16k 的 buffer 拷贝到 210.bin 中
2.4、write2 sd
#!/bin/sh
sudo dd iflag=dsync oflag=dsync if=210.bin of=/dev/sdb seek=1
dd 是一个读写命令, if 是输入, of 是输出, seek 表示从扇区 1 开始读写。sd 启动时, IROM 里的固化代码是从扇区1 开始拷贝代码的。sdb 为本文档编写时 sd 卡的设备节点。
3、生成可烧录镜像文件
3.1、将sd卡插入PC ,在ubuntu(我用的ubuntu,也可以是其他的)终端 执行如下命令 :
首先你要cd 到你放代码的文件夹(例如我放在1.led_s下)
cd 1.led_s
make
chmod 777 write2sd
./write2sd
就可以把生成的210.bin文件烧录到sd卡的1扇区。