S5PV210 裸机开发驱动之LED灯

LED 基本上是学习每款芯片(驱动级别的)的第一步。好,我们看点灯实验

webee的电路图接法是:


S5PV210 裸机开发驱动之LED灯_第1张图片

可以看到控制led信号低时,LED灯亮。而控制这些管脚的是GPJ2_0-GPJ2_3。

看芯片手册,以GPA0为例

S5PV210 裸机开发驱动之LED灯_第2张图片

其实控制GPIO的功能有6个寄存器,表格中前4个是在正常模式下,而后2个是断电状态(并非CPU断电,而是处于某种低功耗状态)。

正常:

GPA0CON:控制输入输出及其他功能的

GPA0DAT:input时是pin管脚的状态,而输出时能控制pin管脚的输出。

GPA0PUD:上下拉控制,一般用于输入模式

GPA0DRV:控制电流驱动能力,控制倍数输出。

低功耗:

GPC0CONPDN:控制低功耗输入输出状态。

GPC0PUDPDN:控制上拉下拉等。

我们现在只讨论正常模式下。

在讨论正式代码之前,先了解一下210启动的细节(摘抄webee210):

210的内存映射:

S5PV210 裸机开发驱动之LED灯_第3张图片

对应的表格:

S5PV210 裸机开发驱动之LED灯_第4张图片

由上面二个图可知, 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


makefile:

%.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  \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;
}
这个代码的总体思路是将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



你可能感兴趣的:(S5PV210 裸机开发驱动之LED灯)