目录
1.外存设备
2.SD卡启动详解
3.SD卡启动实战
一、外存设备
1 :Flash:NandFlash、NorFlash
(1)NAND Flash一般地址线和数据线共用;而NOR Flash闪存数据线和地址线分开。
(2)大多数情况下闪存只是用来存储少量的代码,这时NOR闪存更适合一些。而NAND则是高数据存储密度的理想解决方案。
2 :SD卡、MMC卡、MicroSD、TF卡
(1)MMC在SD卡之前
(2)MicroSD = TF卡
3 :iNand、MoviNand、eSSD
4 :SSD(固态硬盘)
(1)固态硬盘体积小、读写速度快
(2)机械硬盘体积大、读写速度慢
二、SD卡介绍
(1)物理接口:SD卡由9个针脚与外界进行物理连接,这9个脚中有2个地,1个电源,6个信号线。
(2)SD协议与SPI协议(不同访问SD卡的时序)
(3)SD卡支持SPI协议:就是为了单片机方便使用。
(4)SD协议要求SoC中有SD控制器,运行在高速率下,要求SoC的主频不能太低。
三、SD卡启动详解
(1)启动过程:
210启动首先执行内部的iROM(也就是BL0),BL0会判断OMpin来决定从哪个设备启动,如果启动设备是SD卡,则BL0会从SD卡读取前16KB(不一定是16,反正16是工作的)到SRAM中去启动执行(这部分就是BL1,这就是steppingstone技术),
BL0执行时就是通过调用这些device copy function来读取外部SD卡/NandFlash中的BL1的。
(2)启动的第一种情况是整个镜像大小小于16KB。这时候相当于我的整个镜像作为BL1被steppingstone直接硬件加载执行了而已。
(3)启动的第二种情况就是整个镜像大小大于16KB。(只要大于16KB,哪怕是17KB,或者是700MB都是一样的)这时候就要把整个镜像分为2部分:第一部分16KB大小,第二部分是剩下的大小。然后第一部分作为BL1启动,负责去初始化DRAM并且将第二部分加载到DRAM中去执行(uboot就是这样做的)。
(4)Device Copy Function: CopySDMMCtoMem
这个内部函数可以将任何数据从 SD/MMC 设备复制到
内存。用户可以在 IROM 启动过程后使用此功能
完全。
外部源时钟参数用于适配 20MHz 的 EPLL 源时钟。
/**
- 此功能将 MMC(MoviNAND/iNand) 卡数据复制到内存中。
- 始终使用 EPLL 源时钟。
- 此功能工作在 20Mhz。
- @param u32 StartBlkAddress : 源卡(MoviNAND/iNand MMC)) 地址。(必须是块地址。)
- @param u16 blockSize :要复制的块数。
- @param u32* memoryPtr :要从中复制的缓冲区。
- @param bool with_init : 确定卡初始化。
- @return bool(u8) - 成功或失败。
四、SD卡启动实战
1.文件
BL1:
BL2:
从BL1开始分析:
start.S:
#define WTCON 0xE2700000
#define SVC_STACK 0xd0037d80
.global _start // 把_start链接属性改为外部,这样其他文件就可以看见_start了
_start:
// 第0步:开发板置锁
// 写法1
//ldr r0, =0xE010E81C
//ldr r1, =0x301
//str r1, [r0]
// 写法2
//ldr r0, =0xE010E81C
//ldr r1, [r0]
//orr r1, r1, #0x300
//orr r1, r1, #0x01
//str r1, [r0]
// 写法3
ldr r0, =0xE010E81C
ldr r1, [r0]
ldr r2, =0x301
orr r1, r1, r2
str r1, [r0]
// 第1步:关看门狗(向WTCON的bit5写入0即可)
// 第2步:设置SVC栈
// 第3步:开/关icache
//前三步代码省略
// 第4步:初始化ddr
bl sdram_asm_init
// 第5步:重定位,从SD卡第45扇区开始,复制32个扇区内容到DDR的0x23E00000
bl copy_bl2_2_ddr
// 汇编最后的这个死循环不能丢
b .
BL1是16KB,从BLOCK1~BLOCK32,共32个扇区
BL2假设是16KB,BLOCK45-BLOCK76,共32个扇区
copy_bl2_2_ddr
//在SRAM中执行,用的是iROM里面的Device Copy Function的CopySDMMCtoMem
#define SD_START_BLOCK 45
#define SD_BLOCK_CNT 32
#define DDR_START_ADDR 0x23E00000
typedef unsigned int bool;
typedef bool(*pCopySDMMC2Mem)(int, unsigned int, unsigned short, unsigned int*, bool);
typedef void (*pBL2Type)(void);
// 从SD卡第45扇区开始,复制32个扇区内容到DDR的0x23E00000,然后跳转到23E00000去执行
void copy_bl2_2_ddr(void)
{
// 第一步,读取SD卡扇区到DDR中
pCopySDMMC2Mem p1 = (pCopySDMMC2Mem)(*(unsigned int *)0xD0037F98);
//pCopySDMMC2Mem p1 = (pCopySDMMC2Mem)0xD0037F98);
// 通道号:0,或者2
// 开始扇区号:45
// 读取扇区个数:32
// 读取后放入内存地址:0x23E00000
// with_init:0
p1(2, SD_START_BLOCK, SD_BLOCK_CNT, (unsigned int *)DDR_START_ADDR, 0); // 读取SD卡到DDR中
// 第二步,跳转到DDR中的BL2去执行
pBL2Type p2 = (pBL2Type)DDR_START_ADDR;
p2();
}
BL2:
//重定位:
SECTIONS
{
. = 0x23E00000;
.text : {
start.o
* (.text)
}
.data : {
* (.data)
}
bss_start = .;
.bss : {
* (.bss)
}
bss_end = .;
}
//start.S
五、函数指针复习
1.定义函数指针,指向改函数
void func(void)
{
printf("666\n");
}
void (*pfunc)(void);
pfunc = func;
2.通过typedef
某函数:
char *(func)(char *a, char *b)
{
printf("xxx\n");
}
则对应的函数指针为:
char *(*pfunc)(char *a, char *b)
则,这个函数指针类型为:
char *(*)(char *, char *);
使用typedf改变类型:
typedef char *(*pFunc)(char *, char *);
其中pFunc是自定义的类型名
下次定义函数指针时,只需要
pFunc p1; //代替了char *(*p1)(char *, char *);
pFunc p2; //代替了char *(*p2)(char *, char *);
pFunc p3; //代替了char *(*p3)(char *, char *);
总结:
写函数指针时:
1.先在函数名称改为*pfunc
2.typedef直接添加在前面,则生成pfunc的类型名
调用函数指针时:
1.pfunc(参数/无参数);
2.(*pfunc)();