1.内存和外存的区别:
内存:RAM(random access memory,随机访问存储器,特点是任意字节读写,掉电丢失)。
外存:ROM(read only memory,只读存储器,类似于Flash SD卡之类的,用来存储东西,掉电不丢失,不能随机地址访问,只能以块为单位来访问)。
2.SD卡的编程接口
SD卡由9个针脚与外界进行物理连接,这9个脚中有2个地,1个电源,6个信号线。
3.SD协议与SPI协议
SD卡需要按照一定的接口协议(时序)来访问。
-> SPI协议:
SD卡支持SPI协议,是单片机中广泛使用的一种通信协议,并不是为SD卡专门发明的,SPI协议相对SD协议来说速度比较低。
-> SD协议:
SD协议是专门用来和SD卡通信的,SD协议要求SoC中有SD控制器,运行在高速率下,要求SoC的主频不能太低。
4.SD/MMC控制器
SD卡内部除了存储单元Flash外,还有SD卡管理模块,我们SoC和SD卡通信时,通过9针引脚以SD协议/SPI协议向SD卡管理模块发送命令、时钟、数据等信息,然后从SD卡返回信息给SoC来交互。工作时每一个任务(譬如初始化SD卡、譬如读一个块、譬如写、譬如擦除····)都需要一定的时序来完成。
5.S5PV210的SD卡启动详解
第一:CPU上电后先从内部IROM中读取预先设置的代码(BL0),首先做了一些基本的初始化(CPU时钟、关看门狗···);然后(BL0)会判断我们选择的启动模式,然后从相应的外部存储器去读取第一部分启动代码(BL1,大小为16KB)到内部SRAM。
第二:运行刚上一步读取来的BL1(16KB),然后执行。BL1负责初始化NandFlash,然后将BL2读取到IRAM(剩余的80KB)然后运行。
第三:从IRAM运行BL2,BL2初始化DRAM,然后将OS读取到DRAM中,然后启动OS,启动过程结束。
210内置了一块96KB大小的SRAM(叫iRAM),同时还有一块内置的64KB大小的NorFlash(叫iROM)。
6.SD卡启动流程(bin文件小于16KB时和大于16KB时)
启动的第一种情况:整个镜像大小小于16KB。这时候相当于我的整个镜像作为BL1被steppingstone直接硬件加载执行了而已。
启动的第二种情况:整个镜像大小大于16KB。(只要大于16KB,哪怕是17KB,或者是700MB都是一样的)这时候就要把整个镜像分为2部分:第一部分16KB大小,第二部分是剩下的大小。然后第一部分作为BL1启动,负责去初始化DRAM并且将第二部分加载到DRAM中去执行(uboot就是这样做的)。
7.用函数指针方式调用device copy function
三星在iROM中事先内置了一些代码去初始化外部SD卡/NandFlash,并且内置了读取各种SD卡/NandFlash的代码在iROM中。BL0执行时就是通过调用这些device copy function来读取外部SD卡/NandFlash中的BL1的。
-> 宏定义方式来调用。
// 第一种方法:宏定义
#define CopySDMMCtoMem(z,a,b,c,e) (((bool(*)(int, unsigned int, unsigned short, unsigned int*, bool))(*((unsigned int *)0xD0037F98)))(z,a,b,c,e))
-> 函数指针方式来调用。
typedef unsigned int bool;
// 第二种方法:用函数指针方式调用
typedef bool(*pCopySDMMC2Mem)(int, unsigned int, unsigned short, unsigned int*, bool);
// 实际使用时
pCopySDMMC2Mem p1 = (pCopySDMMC2Mem)0xD0037F98;
p1(x, x, x, x, x); // 第一种调用方法
(*p1)(x, x, x, x, x); // 第二种调用方法
*p1(x, x, x, x, x); // 错误,因为p1先和()结合,而不是先和*结合。
8.S5PV210的SD卡启动实战
我们的代码分为2部分:第一部分BL1小于等于16KB,第二部分为任意大小,iROM代码执行完成后从SD卡启动会自动读取BL1到SRAM中执行;BL1执行时负责初始化DDR,然后手动将BL2从SD卡copy到DDR中正确位置,然后BL1远跳转到BL2中执行BL2.
-> 先处理BL1
BL1中要完成:关看门狗、设置栈、开iCache、初始化DDR、从SD卡复制BL2到DDR中特定位置,跳转执行BL2.
start.s:
sd_relocate.c:
-> 再处理BL2
进入BL2文件夹:
start.S:
.global _start // 把_start链接属性改为外部,这样其他文件就可以看见_start了
_start:
ldr pc, =main // ldr指令实现长跳转
// 汇编最后的这个死循环不能丢
b .
led.c:单纯用来测试SD卡启动的
#define GPJ0CON 0xE0200240
#define GPJ0DAT 0xE0200244
#define rGPJ0CON *((volatile unsigned int *)GPJ0CON)
#define rGPJ0DAT *((volatile unsigned int *)GPJ0DAT)
void delay(void);
void led1(void)
{
rGPJ0CON = 0x11111111;
rGPJ0DAT = ((0<<3) | (1<<4) | (1<<5));
}
void led2(void)
{
rGPJ0CON = 0x11111111;
rGPJ0DAT = ((0<<3) | (0<<4) | (1<<5));
}
void led3(void)
{
rGPJ0CON = 0x11111111;
rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));
}
// 该函数要实现led闪烁效果
void main(void)
{
// led初始化,也就是把GPJ0CON中设置为输出模式
//volatile unsigned int *p = (unsigned int *)GPJ0CON;
//volatile unsigned int *p1 = (unsigned int *)GPJ0DAT;
rGPJ0CON = 0x11111111;
while (1)
{
// led亮
rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));
// 延时
delay();
// led灭
rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));
// 延时
delay();
}
}
void delay(void)
{
volatile unsigned int i = 900000; // volatile 让编译器不要优化,这样才能真正的减
while (i--); // 才能消耗时间,实现delay
}