LED 基本上是学习每款芯片(驱动级别的)的第一步。好,我们看点灯实验
webee的电路图接法是:
可以看到控制led信号低时,LED灯亮。而控制这些管脚的是GPJ2_0-GPJ2_3。
看芯片手册,以GPA0为例
其实控制GPIO的功能有6个寄存器,表格中前4个是在正常模式下,而后2个是断电状态(并非CPU断电,而是处于某种低功耗状态)。
正常:
GPA0CON:控制输入输出及其他功能的
GPA0DAT:input时是pin管脚的状态,而输出时能控制pin管脚的输出。
GPA0PUD:上下拉控制,一般用于输入模式
GPA0DRV:控制电流驱动能力,控制倍数输出。
低功耗:
GPC0CONPDN:控制低功耗输入输出状态。
GPC0PUDPDN:控制上拉下拉等。
我们现在只讨论正常模式下。
在讨论正式代码之前,先了解一下210启动的细节(摘抄webee210):
210的内存映射:
对应的表格:
由上面二个图可知, S5PV210 含有一个大小为 64KB 的 IROM,起始地址为0xD0000000,结束地址为 0xD000FFFF;含有一个大小为 96KB 的 IRAM,起始地址为 0xD0020000,结束地址为 0xD0037FFF;内存起始地址为 0x20000000,有二个内存块,DRAM0 和 DRAM1 大小分别为 512MB、1024MB。
S5PV210 启动流程分析
根据三星公司的《S5PV210_UM_REV1.1》手册可知,S5PV210 启动过程
主要可分为 3 个阶段。
1 S5PV210 上电复位后将从 IROM 处执行已固化的启动代码 -------BL0
2 在 BL0 里初始化过程中对启动设备进行判断,并从启动设备拷贝 BL1(最大
16KB ) 到 IRAM 处 , 即 刚 才 所 说 的 0xD0020000 开 始 的 地 址 , 其 中
0xD0020000~0xD0020010 的 16 字节为 BL1 的校验信息和 BL1 尺寸,并对
BL1 进行校验,校验通过转入 BL1 进行执行,BL1 继续初始化,并拷贝 BL2
(最大 80KB)到 IRAM 中并对其校验,通过后转入 BL2。
3 BL2 完成一些比较复杂的初始化,包括 DRAM 的初始化,完成后将 OS 代码
拷贝到 DRAM 中,并跳到 OS 中执行并完成启动引导。
30图 3.4 启动过程示意图
BL0 固化代码主要完成以下初始化:
1 关闭看门狗;
2 初始化 icache;
3 初始化栈;
4 初始化堆;
5 初始化块设备拷贝功能;
6 设置系统时钟;
7 拷贝 BL1 到 iRAM;
8 检查 BL1 的校验和,如果失败则第二启动模式(安全启动模式),校验成功
则跳到 0xD0020000(IRAM)处执行。
其中 0xD0020000 ~ 0xD0020010 里的 16 字节头部信息是什么呢?这 16 字
节信息用户是不能随便设置的!!
在《S5PV210_iROM_ApplicationNote_Preliminary_20091126》文档中规定
16 字节的头部信息必须为:
地址 内容
0xD002_0000 BL1 的大小
0xD002_0004 必须写为 0
0xD002_0008 CheckSum
0xD002_000C 必须写为 0
校验和的计算方法:
for(count = 0;count < dataLength; count++)
{
buffer = (*(volatile u8*))(uBLAddr + count);
CheckSum = CheckSum + buffer;
}
count:是一个循环索引变量
dataLength :BL1 的大小,以字节为单位
buffer: 从 BL1 里读一个 1 字节的数据
CheckSum: BL1 的校验总和
如何生成 16byte 的头部信息?
在从启动设备(如: NAND FLASH / SD 卡)拷贝 BL1 的前 16K 数据到 IRAM
时,这 16K 数据中的前 16byte 中保存了校验和的值以及 BL1 的大小,在拷贝过
程中 CPU 会计算出当前 bin 文件中含’1’的个数,然后与校验和进行比较,如果
二者相等则继续运行程序,否则,不执行。
启动流程弄好之后没就是程序了:
start.s
.globl _start
_start: @因为这里的标号是程序的入口,一定要用 _start ,
@否则链接器将找不到程序的入口
ldr r1, =0xE0200280 @设置GPJ2CON的bit[0:15],让GPJ2_0、GPJ2_1、GPJ2_2、GPJ2_3
ldr r0, =0x00001111 @都作为输出引脚
str r0, [r1]
ldr r1, =0xE0200284 @设置GPJ2DAT的bit[0:3],让GPJ2_0、GPJ2_1、GPJ2_2、GPJ2_3
mov r0, #0 @引脚输出低电平,LED亮
str r0, [r1]
halt: @让CPU一直执行这条指令,防止PC指针跑飞
b halt
%.o : %.S
arm-linux-gcc -o $@ $< -c
%.o : %.c
arm-linux-gcc -o $@ $< -c
led.bin: start.o
arm-linux-ld -Ttext 0 -o led.elf start.o
arm-linux-objcopy -O binary led.elf led.bin
arm-linux-objdump -D led.elf > led.dis
gcc -o mktools mktools.c
./mktools led.bin new_led.bin
clean:
rm *.o *.elf *.bin *.dis mktools
load:
dd iflag=dsync oflag=dsync if=new_led.bin of=/dev/sdb seek=1
mktools.c
/* 在BL0阶段,Irom内固化的代码读取nandflash或SD卡前16K的内容,
* 并比对前16字节中的校验和是否正确,正确则继续,错误则停止。
*/
#include
#include
#include
#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
这个代码的总体思路是将start.s 编译成为 led.elf 其中链接地址是0,然后将转为2进制输出到led.bin,后编译mktoos.c 生成可执行文件mktools,然后通过可执行文件将led.bin 加上头信息,并扩增为16K生成new_led.bin。然后执行
make load 就可以将new_led.bin 下载到sd卡的第一个扇区。好将SD插入210板子上,上电,可以看到4个led全部被点亮了。
可是这样不爽发现没,跑马灯没有,其实汇编也可以实现,但是有C环境我还是用C了。改一下start.s,这中间需要设置堆栈。
.global _start
_start:
ldr sp, =0xD0037D80 @设置栈,以便调用c函数
bl led_flow_water @调用c函数,LED闪烁,之所以用led_flow_water,
@是想说明这与名字无关,不一定用main
halt: @让CPU一直执行这条指令,防止PC指针跑飞
b halt
0xD0037D80是SVCStack的结束地址。但是这个不是必须的,其实可以是下图的中的任意的地址,但是要注意不要覆原来的程序。
led.c
#define GPJ2CON (*(volatile unsigned long *) 0xE0200280) #define GPJ2DAT (*(volatile unsigned long *) 0xE0200284) #define GPJ2_0_OUT (1<<0) #define GPJ2_1_OUT (1<<4) #define GPJ2_2_OUT (1<<8) #define GPJ2_3_OUT (1<<12) #define GPJ2_0_ON ~(1<<0) #define GPJ2_1_ON ~(1<<1) #define GPJ2_2_ON ~(1<<2) #define GPJ2_3_ON ~(1<<3) #define GPJ2_0_OFF (1<<0) #define GPJ2_1_OFF (1<<1) #define GPJ2_2_OFF (1<<2) #define GPJ2_3_OFF (1<<3) /* 延时函数 */ void delay(int count) { while (count--) ; } /* 流水灯函数 */ void led_flow_water() { unsigned int i = 0; GPJ2CON |= (GPJ2_0_OUT | GPJ2_1_OUT | GPJ2_2_OUT | GPJ2_3_OUT); // 配置引脚 while (1) { GPJ2DAT = ~(1<很简单的函数。
makefile
%.o : %.S
arm-linux-gcc -o $@ $< -c
%.o : %.c
arm-linux-gcc -o $@ $< -c
led.bin: start.o led.o
arm-linux-ld -Ttext 0 -o led.elf $^
arm-linux-objcopy -O binary led.elf $@
arm-linux-objdump -D led.elf > led.dis
gcc -o mktools mktools.c
./mktools $@ new_led.bin
clean:
rm *.o *.elf *.bin *.dis mktools
load:
dd iflag=dsync oflag=dsync if=new_led.bin of=/dev/sdb seek=1