来源:朱有鹏老师的嵌入式课程笔记
主流的外存设备介绍
内存一般指的是RAM(random access memory,随机访问存储器),外存一般指ROM(read only memory,只读存储器),前者可以访问任意字节,掉电后丢失数据,后者以块为单位进行访问(比如已512字节为一个块,如果要读取第80字节数据,需要将整个512字节都读出来),掉电后数据不丢失。
软盘、硬盘、光盘、CD、光盘
磁存储,读写速度慢,可靠性低。广泛使用在桌面电脑,嵌入式设备几乎没有在用
Flash闪存
Flash存储即闪存计数,是当前存储技术发展的主流,利用电学原理存储1、0,无需磁存储设备的磁头等物理运动。因此读写速度快,寿命长。
纯粹的flash:NandFlash、NorFlash
NandFlash和NorFLash是出现最早的Flash颗粒组成芯片,仅针对存储最基本的功能提供接口,要求SoC来提供Flash读写控制器。
缺点包括:1、读写接口时序比较复杂。2、内部无坏块处理机制,需要SoC自己来管理Flash的坏块;3、各家厂家的Flash接口不一致,甚至同一个厂家的不同型号、系列的Flash接口都不一致,这就造成产品升级时很麻烦。
NorFlash可以总线访问,但是造假昂贵,一般用于bios中,NandFlash一般是块访问,是将来存储发展的方向。
SD卡、MMC卡、MicroSD卡、TF卡
这些卡其实是把纯粹的NandFlash进行了接口封装,使其具有统一、规范的接口。譬如SD卡都是遵照SD规范来发布的。这些规范规定了SD卡的读写速度、读写接口时序、读写命令集、卡大小尺寸、引脚个数及定义。这样做的好处就是不同厂家的SD卡可以通用
iNand、MoviNand、eSSD
这些东西的本质还是NandFlash,内部由Nand的存储颗粒构成,再集成了块设备管理单元,综合了SD卡为代表的各种卡的优势和原始的NandFlash芯片的优势。
优势:1、向SD卡学习,有统一的接口标准(包括引脚定义、物理封装、接口时序)。2、向原始的Nand学习,以芯片的方式来发布而不是以卡的方式;3、内部内置了Flash管理模块,提供了诸如坏块管理等功能,让Nand的管理容易了起来
SSD
内部也是NandFLash,和eSSD的区别就在于,SSD做成硬盘的样子,eSSD做成芯片的样子
SD卡基础知识
概述
SD卡和MMC卡区别:MMC卡的标准比SD卡早,SD卡能兼容MMC卡接口,但是反过来不行。
SD卡和NandFlash、NorFlash的区别:前者有统一的接口规范,后者没有。
SD卡和MicroSD卡区别:原理完全相同,只是大小不一样;SD卡有写保护,后者没有。MicroSD卡就是TF。
SD卡和SRAM/DDR/SROM的区别:后者的存储芯片可以通过总线访问,初始化之后直接由SoC寻址访问,SD卡的访问则需要遵循一定的协议(SPI协议)
SD卡编程接口
SD卡有9个引脚与外界连接
针脚 | 4位SD模式 | 1位SD模式 | SPI模式 | |||
---|---|---|---|---|---|---|
名称 | 描述 | 名称 | 描述 | 名称 | 描述 | |
1 | CD/DAT3 | 卡监测/数据位3 | CD | 卡监测 | CS | 芯片选择 |
2 | CMD | 命令/回复 | CMD | 命令/回复 | DI | 数据输入 |
3 | VSS1 | 地 | VSS1 | 地 | VSS1 | 地 |
4 | VCC | 电源 | VCC | 电源 | VCC | 电源 |
5 | CLK | 时钟 | CLK | 时钟 | CLK | 时钟 |
6 | VSS2 | 地 | VSS2 | 地 | VSS2 | 地 |
7 | DAT0 | 数据位0 | DAT | 数据位 | DO | 数据输出 |
8 | DAT1 | 数据位1 | RSV | 保留 | RSV | 保留 |
9 | DAT2 | 数据位2 | RSV | 保留 | RSV | 保留 |
SPI协议和SD卡协议
SD卡支持SD卡协议和SPI协议。SPI协议是单片机中广泛使用的协议,并不是为SD卡专门设计的,它的速度比SD卡协议慢,之所以支持SPI协议,也是为了兼容单片机。
SD协议开始专门用于SD卡通信,后来也扩展到其他的外设。SD协议对主频有要求。
SD卡的热插拔检测流程
首先使能SD卡插拔中断,然后SD卡插拔后会触发这个中断,在中断处理函数中通过SD卡控制器中的1bit判断SD卡的状态,如果是1表示插入,0表示拔出
SD卡启动详解
由于SD卡不能和SoC直接打交道,以前只有NorFlash可以作为启动介质,比如BIOS,三星为了卖NandFlash,研究出一种NandFlash启动的技术,简单来说就是,在SoC内部有一块小的SRAM,启动的时候从NandFlash中读取启动程序到SRAM中去执行。
SD卡启动步骤:
- 210启动首先执行内部的iROM(也就是BL0),BL0会判断OMpin来决定从哪个设备启动,如果启动设备是SD卡,则BL0会从SD卡读取前16KB,到SRAM中去启动执行(这部分就是BL1)
- BL1执行之后剩下的就是软件的事情了,SoC就不用再去操心了。
- 如果镜像小于16KB,那么BL1加载执行完毕就结束了,比如裸机部分的代码。如果镜像大于16KB,比如操作系统程序,可以将前16KB作为BL1,剩余部分作为BL2,BL1的工作就是初始化更大空间的DRAM,然后加载BL2到DRAM中去执行,剩下的就是BL2中程序做的事情,比如启动整个操作系统。
从BL0加载之后就可以从SD卡中读取BL1可以看出,BL0中包含了SD卡初始化程序。
我们在往SD卡烧录BL1的时候,需要跳过SD卡的第一个块区,整个烧录软件屏蔽了这个细节,如果自己烧录,则需要手动跳过,具体命令为:
#!/bin/sh
sudo dd iflag=dsync oflag=dsync if=210.bin of=/dev/sdb seek=1
SD卡编程实战
BL1、BL2编程流程
总体思路,通过BL1初始化DDR,然后从BL2读取程序,并转跳到BL2去执行,BL2实现LED灯的闪烁。需要确定以下问题:1)BL2的代码从SD卡的哪个扇区开始记录,一共多长;2)读取BL2的代码存放在DDR的哪个位置。
1)假设BL2从SD卡的45号扇区开始(BL1 16K,预留1个扇区,理论上33号扇区开始就可以放BL2程序,安全起见中间留一段缓存扇区)。
2)读取BL2的代码存放在DDR 0x23E00000位置(人为确定,地址随意)
BL1的主函数
#define WTCON 0xE2700000
#define SVC_STACK 0xd0037d80
.global _start // 把_start链接属性改为外部,这样其他文件就可以看见_start了
_start:
// 第1步:关看门狗(向WTCON的bit5写入0即可)
ldr r0, =WTCON
ldr r1, =0x0
str r1, [r0]
// 第2步:设置SVC栈 为了能够运行c程序
ldr sp, =SVC_STACK
// 第3步:开/关icache
mrc p15,0,r0,c1,c0,0; // 读出cp15的c1到r0中
//bic r0, r0, #(1<<12) // bit12 置0 关icache
orr r0, r0, #(1<<12) // bit12 置1 开icache
mcr p15,0,r0,c1,c0,0;
// 第4步:初始化ddr
bl sdram_asm_init
// 第5步:重定位,从SD卡第45扇区开始,复制32个扇区内容到DDR的0x23E00000
bl copy_bl2_2_ddr
// 汇编最后的这个死循环不能丢
b .
BL2就是LED灯闪烁的代码
从SD卡读取数据的步骤
SoC从SD卡读取数据需要经过SD/MMC控制器,该控制器屏蔽了SD卡访问的细节,并提供了接口函数供SoC调用,如从SD卡读取数据到内存,可以调用宏CopySDMMCtoMem ,其定义了一个地址为0xD0037F98的函数指针,细节为:
#define CopySDMMCtoMem(z,a,b,c,e)(((bool(*)(int, unsigned int, unsigned short, unsigned int*, bool))(*((unsigned
int *)0xD0037F98)))(z,a,b,c,e))
//z: 通道号:0,或者2
//a: 开始扇区号:45
//b: 读取扇区个数:32
//c: 读取后放入内存地址:0x23E00000
//e: with_init:0
那么我们从SD卡读数据到内存,只需调用上述接口函数即可,比如想从SD卡第45扇区开始,复制32个扇区内容到DDR的0x23E00000,然后跳转到23E00000去执行,可以通过如下代码实现:
#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中,这边强制类型转换是为了编译器能对函数的传参进行校验,否则直接使用CopySDMMCtoMem无法享受编译器的校验
pCopySDMMC2Mem p1 = (pCopySDMMC2Mem)0xD0037F98;
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();
}