熟悉SD/MMC的相关寄存器对协议的理解有一定的辅助作用,所以这篇文章来介绍一下SD/MMC相关的寄存器有哪些呢?
1.SD卡内部架构
在熟悉SD/MMC相关寄存器之前,我们先来看看SD卡的内部架构是怎么样的,如下图所示:
2.SD/MMC相关寄存器的介绍
从上图中总结出:SD卡内部有7个寄存器.
一、OCR,CID,CSD和SCR寄存器保存卡的配置信息;
二、RCA寄存器保存着通信过程中卡当前暂时分配的地址(只适合SD模式);
三、CSR寄存器卡状态(Card Status)和SSR寄存器SD状态(SD Status)寄存器保存着卡的状态(例如,是否写成功,通信的CRC校验是否正确等),这两个寄存器的内容与通信模式(SD模式或SPI模式)相关.
四、MMC卡没有SCR和SSR寄存器.
下面分别对7个寄存器中比较重要的寄存器详细解释一下,分别是CID、CSD、SCR、OCR、RCA这5个寄存器。
2.1. Card Identification Register(CID)
这个 CID 寄存器有 16 字节长,如下表所示,它包含了本卡的特别识别码(ID 号)。 这些信息是在卡的生产期间被编程(烧录),主控制器不 能修改它们的内容。 注意:SD卡的 CID 寄存器和 MMC 卡的 CID 寄存器在记录结构上是不同的。
2.2.Card Specific Data Register(CSD)
这个描述数据寄存器(CSD)有 128 字节长,如下表所示,此卡的包含了访问该卡数据时的必要配置信息。“cell type”栏内定义了CSD的区域是只读(R)、一次编程(R/W)或可擦除的(R/W/E)[“R/W”是指可以多次擦写,“R/W(1)”是指只能一次写入,不可擦除]。该张表中所显示的值都对应真实的CSD结构中的各自区域和编码。CSD区域的样式是依照栏标记(和一个复选标记√)的样式。注意SD卡内的 CSD寄存器和MultiMedia卡的CSD寄存器有着不同的结构。
在SD3.0协议中,CSD分为版本1.0和版本2.0,版本1.0对应标准容量的SD卡,版本2.0对应高容量和超高容量的SD卡。
CSD Version 2.0的如下:
2.3.SD card Configuration Register (SCR)
除了 CSD 寄存器外,还有一个配置寄存器的名字是:SD 卡配置寄存器(SCR)。SCR 提供了SD 卡的一些特殊特性在这张卡内。它的大小是64 位。这个寄存器内容由制造商在生产厂内设置,MMC卡没有SCR。
SCR_STRUCTURE 关于SD卡内的物理级说明中SCR结构的版本号。
SD_SPEC描述这张SD卡在物理级上所支持的说明版本。
DATA_STAT_AFTER_ERASE 定义了数据在擦除后的状态。是“0”或“1”中的任何一个(这要依赖卡的供应商)。
SD_SECURITY 描述了该卡所支持的安全算法。0:无 1:安全协议1.0 安全说明版本 0.96 2:安全协议2.0 安全说明版本 1.0 - 1.01。其他保留
SD_BUS_WIDTHS描述该卡所支持的所有数据总线宽度。从SD 卡支持最少1 位或4 位宽度这两种总线模式开始,任何SD 卡都将最少要设置0 和2 这两个位(即SD_BUS_WIDTH = 0101 ),1.4位保留。
2.4.Operating Conditions Register (OCR)
这个 32 位的工作条件寄存器储存了卡的 VDD 电压轮廓图。任何标准的 SD 卡主控制器可以使用 2V 至 3.6V 的工作电压来让 SD 卡能执行这个电压识别操作(CMD1)。而访问存储器的阵列操作无论如何都需要 2.7V 至 3.6V 的工作电压。OCR 寄存器显示了在访问卡的数据时所需要的电压范围。OCR 寄存器的结构描述:
2.5.RCA寄存器
该16位卡地址寄存器保存了在卡识别过程中卡发布的器件地址。该地址用于在卡识别后主机利用该地址与卡进行通信。该寄存器只有在SD总线模式下才有效。
3.0 mmc.c (方便查看)
- #include "mmc.h"
- /****************************************************************************
- SD卡操作的主要重点在于命令的返回,读写地址的定位
- 这部分程序使用IO口模拟方式读写SD卡,未验证,
- 下一部分使用SPI接口和MCI接口 ,Countinue...... ,对于本程序,这三种模式下,所
- 不同的对于和SD卡的通讯部分,其他部分基本相同
- 不同功能程序如下:MMC_Port_Init(...) ; MMC_Init()SPI与MCI不同;Read_Byte_MMC(...)和
- Write_Byte_MMC(...)三种模式下都不同;详见注释
- **/
- // Port Init
- void MMC_Port_Init(void) //配置 spi 端口
- //****************************************************************************
- {
- #ifdef Direct_IO //*********** I/O口模拟SPI模式 **********
- AT91F_PMC_EnablePeriphClock(AT91C_BASE_PMC,1<<AT91C_ID_PIOA) ;
- AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA ,SPI_DI|SPI_DO|SPI_Clock|MMC_Chip_Select,0) ;
- AT91F_PIO_OutputEnable(AT91C_BASE_PIOA,SPI_DO|SPI_Clock|MMC_Chip_Select) ;
- #endif
- #ifdef SPI_Mode //***************** SPI 模式 ******************
- AT91F_PMC_EnablePeriphClock(AT91C_BASE_PMC,1<<AT91C_ID_PIOA) ;
- AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA ,SPI_DI|SPI_DO|SPI_Clock|MMC_Chip_Select,0) ;
- AT91F_PIO_OutputEnable(AT91C_BASE_PIOA,SPI_DO|SPI_Clock|MMC_Chip_Select) ;
- // config SPI 模式
- AT91F_SPI_CfgMode(AT91C_BASE_SPI0,AT91C_SPI_MSTR|AT91C_SPI_PS_FIXED
- |AT91C_SPI_FDIV|AT91C_SPI_DLYBCS) ; //配置模式 master,FiX,MCK/32
- //拨特率=mck/64/7 ,bit width=8
- AT91C_BASE_SPI0->SPI_CSR[0] |=(AT91C_SPI_BITS_8|(AT91C_SPI_SCBR & 0x07)) ;
- AT91F_SPI_Reset(AT91C_BASE_SPI0) ;
- AT91F_SPI_Enable(AT91C_BASE_SPI0) ;
- #endif
- #ifdef MCI_Mode //***************** 使用MCI模式 *************
- ; // in use MCI mode ,place code here ! but not support MCI in AT91SAM7X256 .
- // achieve the foundation in use AT91SAM7A3
- #endif
- }
- //****************************************************************************
- //Init MMC/SD card(SPI-MODE)
- unsigned char MMC_Init(void) // 在SPI和MCI模式下 有所不同
- //****************************************************************************
- {
- unsigned char retry,temp;
- unsigned char i;
- //CRC 校验在每个命令都需要,其生成多项式: G(x) = x7 + x3 + 1.
- unsigned char CMD[] = {0x40,0x00,0x00,0x00,0x00,0x95}; // Com0 复位
- MMC_Port_Init(); //Init SPI port
- for(i=0;i<200;i++) //Wait MMC/SD ready...
- {
- asm("nop") ; //__asm("nop") ; 嵌入汇编语言
- }
- Init_Flag=1; //标志为卡片状态 为正在初始化
- // mmc 协议说,要进入SPI模式,在卡接收复位命令的时候,CS应该为低.
- // 但SD卡复位时应该不需要吧
- //************************** 如果必须的话,Place code here**************************//
- AT91F_PIO_ClearOutput(AT91C_BASE_PIOA,MMC_Chip_Select) ; //***********还是职为低,确保进入SPI模式
- //在初始化的时候要给SD卡至少80个脉冲,(from internet)
- for (i=0;i<0x0f;i++)
- {
- Write_Byte_MMC(0xff); //send 80 clock at least!!!
- }
- //Send Command CMD0 to MMC/SD Card
- retry=0;
- do //retry n times to send CMD0 command
- {
- temp=Write_Command_MMC(CMD);
- retry++;
- if(retry==200)
- { //time out
- return(INIT_CMD0_ERROR); //CMD0 Error!
- }
- }
- while(temp!=1);
- //Send Command CMD1 to MMC/SD-Card :Activates the card’s initialization process.
- //启动卡的初始化进程 ,只在SPI模式才支持 ,见 90页
- CMD[0] = 0x41; //Command 1
- CMD[5] = 0xFF;
- retry=0;
- do
- { //retry 100 times to send CMD1 command
- temp=Write_Command_MMC(CMD); //写成功返回 1
- retry++;
- if(retry==100)
- { //time out
- return(INIT_CMD1_ERROR); //CMD1 Error!
- }
- }
- while(temp!=0); //直到写成功
- Init_Flag=0; //Init is completed,clear the flag
- //进入SPi模式后,解除。可以不用
- AT91F_PIO_SetOutput(AT91C_BASE_PIOA,MMC_Chip_Select) ; //******set MMC_Chip_Select to high
- return(0); //All commands have been taken.
- }
- //****************************************************************************
- //returns the :
- // size of the card in MB ( ret * 1024^2) == bytes
- // sector count and multiplier MB are in u08 == C_SIZE / (2^(9-C_SIZE_MULT))
- // name of the media
- // SD的参数大多都在 CSD寄存器中,只要读取CSD就可以知道容量
- // 必须注意,由于wo人在定义读操作的时候是先读到高字节后低字节,在对应数据信息的时候,
- // 请倒置,或自己数数吧 ! CSD寄存器信息详见 SD卡协议 62 页
- void MMC_get_volume_info(void)
- //****************************************************************************
- {
- VOLUME_INFO_TYPE MMC_volume_Info,*SDinf;
- SDinf=&MMC_volume_Info; //Init the pointoer;
- // read the CSD register
- Read_CSD_MMC(sectorBuffer.data) ;
- // 需要计算容量的相关参数如下:
- // C_SIZE(每扇区块个数) :CSD 寄存器[73:62]位
- // C_SIZE_MULT(扇区总数) : [49:47]
- // READ_BL_LEN(每块可读数据长度): [83:80]
- //列如最大容量为 4096*512*2048 = 4 GBytes.
- //实列:: 一个 32 MByte SD 卡 当 BLOCK_LEN = 512 时,可以编码为 C_SIZE_MULT = 3 , C_SIZE = 2000
- // 得到 C_SIZE 的值: CSD[73:62]
- // [73:72] == sectorBuffer.data[6] && 0x03
- // [71:64] == sectorBuffer.data[7]
- // [63:62] == sectorBuffer.data[8] && 0xc0
- // 为了符合资料上数据位的定义,也可以将读到的数据反转,即:[73:62]=(127-73)/8=6.7,所以从最高位
- // 开始数的第6字节开始,也就是数组的第6项
- SDinf->sector_count = sectorBuffer.data[6] & 0x03; // 读C_SIZE_MULT 取出[73:72]
- SDinf->sector_count <<= 8;
- SDinf->sector_count += sectorBuffer.data[7]; // [71:64]
- SDinf->sector_count <<= 2;
- SDinf->sector_count += (sectorBuffer.data[8] & 0xc0) >> 6; // [63:62]
- // 得到 C_SIZE_MULT数据,CSD [49:47] 从读到的数据缓冲sectorBuffer.data
- // [49:48] == sectorBuffer.data[9] && 0x03
- // [47] == sectorBuffer.data[10] && 0x80
- SDinf->sector_multiply = sectorBuffer.data[9] & 0x03; //[49:48]
- SDinf->sector_multiply <<= 1;
- SDinf->sector_multiply += (sectorBuffer.data[10] & 0x80) >> 7; //[47]
- //READ_BL_LEN =[83:80]== sectorBuffer.data[5] && 0x80
- SDinf->READ_BL_LEN=sectorBuffer.data[5] &0x0F ;
- // 计算总容量 ( MBs)
- //mega bytes in u08 == (C_SIZE+1) * (2^(C_SIZE_MULT+READ_BL_LEN+2)) /1000 000
- SDinf->size_MB = (SDinf->sector_count+1) >> (SDinf->sector_multiply+SDinf->READ_BL_LEN+2-8);
- // mega bytes in u08 == C_SIZE / (2^(9-C_SIZE_MULT))
- //SDinf->size_MB = SDinf->sector_count >> (9-SDinf->sector_multiply);
- // get the name of the card,位置在寄存器CID的 PNM(Product name) ,[103:64],共40位,即
- // 第三字节到第七字节
- Read_CID_MMC(sectorBuffer.data);
- SDinf->name[0] = sectorBuffer.data[3];
- SDinf->name[1] = sectorBuffer.data[4];
- SDinf->name[2] = sectorBuffer.data[5];
- SDinf->name[3] = sectorBuffer.data[6];
- SDinf->name[4] = sectorBuffer.data[7];
- SDinf->name[5] = 0x00;
- // de得到产品的序列号 ,CID [55:24],32位2进制数 ,有兴趣可以读出来
- {
- asm("nop") ; // 读PSN 的代码
- }
- }
- //****************************************************************************
- //Send a Command to MMC/SD-Card
- //Return: the second byte of response register of MMC/SD-Card
- unsigned char Write_Command_MMC(unsigned char *CMD)
- //****************************************************************************
- {
- unsigned char tmp;
- unsigned char retry=0;
- unsigned char i;
- //set MMC_Chip_Select to high (MMC/SD-Card disable)
- AT91F_PIO_SetOutput(AT91C_BASE_PIOA,MMC_Chip_Select) ;
- //send 8 Clock Impulse
- Write_Byte_MMC(0xFF);
- //set MMC_Chip_Select to low (MMC/SD-Card active)
- AT91F_PIO_ClearOutput(AT91C_BASE_PIOA,MMC_Chip_Select) ;
- //send 6 Byte Command to MMC/SD-Card
- for (i=0;i<0x06;i++)
- {
- Write_Byte_MMC(*CMD++);
- }
- //get 16 bit response
- Read_Byte_MMC(); //read the first byte,ignore it.
- do
- { //Only last 8 bit is used here.Read it out.
- tmp = Read_Byte_MMC(); // R1响应总共48bit ,后8比特为crc7+end bit
- retry++;
- }
- while((tmp==0xff)&&(retry<100));
- return(tmp);
- }
- //****************************************************************************
- unsigned char Read_Byte_MMC(void) //SPI读数据函数
- //****************************************************************************
- {
- unsigned char temp=0;
- unsigned char i;
- //Software SPI
- for (i=0; i<8; i++) //MSB First
- {
- AT91F_PIO_ClearOutput(AT91C_BASE_PIOA,SPI_Clock) ; //Clock Impuls (Low)
- if(Init_Flag) delay_us_8_;
- //read mmc data out pin
- if((AT91F_PIO_GetInput(AT91C_BASE_PIOA) & (SPI_DI))!=0) //读输入数据
- temp = (temp << 1) + 1;
- else
- temp = (temp << 1) + 0;
- AT91F_PIO_SetOutput(AT91C_BASE_PIOA,SPI_Clock) ; //set Clock Impuls High
- if(Init_Flag) delay_us_8_;
- }
- return (temp);
- //上面的读的方法是直接驱动总线,也可以直接接收 SPI_RDR寄存器 ,如下:
- //return( AT91F_SPI_GetChar (NowSD_SPI)) ;
- }
- //****************************************************************************
- //Routine for sending a byte to MMC/SD-Card
- void Write_Byte_MMC(unsigned char value)
- //****************************************************************************
- {
- unsigned char i;
- //Software SPI
- for (i=0; i<8; i++)
- { //write a byte
- if (((value >> (7-i)) & 0x01)==0x01)
- AT91F_PIO_SetOutput(AT91C_BASE_PIOA,SPI_DO) ;//Send bit by bit(MSB First)
- else
- AT91F_PIO_ClearOutput(AT91C_BASE_PIOA,SPI_DO);
- AT91F_PIO_ClearOutput(AT91C_BASE_PIOA,SPI_Clock); //set Clock Impuls low
- if(Init_Flag) delay_us_8_;
- AT91F_PIO_SetOutput(AT91C_BASE_PIOA,SPI_Clock); //set Clock Impuls High
- if(Init_Flag) delay_us_8_;
- }//write a byte
- AT91F_PIO_SetOutput(AT91C_BASE_PIOA,SPI_DO); //set Output High
- //使用如下的库函数来发送数据,但发送前不检测状态是否为 ready
- /*
- AT91F_SPI_PutChar (NowSD_SPI,value,0) ;
- */
- }
- //****************************************************************************
- //Routine for writing a Block(512Byte) to MMC/SD-Card
- //Return 0 if sector writing is completed.
- unsigned char MMC_write_sector(unsigned long addr,unsigned char *Buffer)
- //****************************************************************************
- {
- unsigned char tmp,retry;
- unsigned int i;
- // Command 24 is a 写数据块命令.
- // 根据协议,命令共6字节,首字节为:开始位+传输位+命令编号,接下来四个字节为命令参数(此为地址)
- // 最后一字节为 7bit CRC + 1bit停止位
- unsigned char CMD[] = {0x58,0x00,0x00,0x00,0x00,0xFF}; // 0x58 =1011000
- //24 =11000
- AT91F_PIO_GetInterruptStatus(AT91C_BASE_PIOA); //clear all interrupt.
- addr = addr << 9; //addr = addr * 512
- CMD[1] = ((addr & 0xFF000000) >>24 ); // [25~32]
- CMD[2] = ((addr & 0x00FF0000) >>16 );
- CMD[3] = ((addr & 0x0000FF00) >>8 ) ;
- //Send Command CMD24 to MMC/SD-Card (Write 1 Block/512 Bytes)
- retry=0;
- do
- { //Retry 100 times to send command.
- tmp=Write_Command_MMC(CMD);
- retry++;
- if(retry==100)
- {
- return(tmp); //send commamd Error!
- }
- }
- while(tmp!=0);
- //Before writing,send 100 clock to MMC/SD-Card
- for (i=0;i<100;i++)
- {
- Read_Byte_MMC();
- }
- //Send Start Byte to MMC/SD-Card
- Write_Byte_MMC(0xFE);
- //Now send real data Bolck (512Bytes) to MMC/SD-Card
- for (i=0;i<512;i++)
- {
- Write_Byte_MMC(*Buffer++); //send 512 bytes to Card
- }
- //CRC-Byte
- Write_Byte_MMC(0xFF); //Dummy CRC
- Write_Byte_MMC(0xFF); //CRC Code
- tmp=Read_Byte_MMC(); // read response
- if((tmp & 0x1F)!=0x05) // data block accepted ? 检测后8位,
- {
- AT91F_PIO_SetOutput(AT91C_BASE_PIOA,MMC_Chip_Select) ;
- return(WRITE_BLOCK_ERROR); //Error!
- }
- //Wait till MMC/SD-Card is not busy
- while (Read_Byte_MMC()!=0xff){}; //等待命令结束 最后一位为 1
- //set MMC_Chip_Select to high (MMC/SD-Card Invalid)
- AT91F_PIO_SetOutput(AT91C_BASE_PIOA,MMC_Chip_Select) ;
- return(0);
- }
- //****************************************************************************
- //Routine for reading data Registers of MMC/SD-Card
- //Return 0 if no Error.
- unsigned char MMC_Read_Block(unsigned char *CMD,unsigned char *Buffer,unsigned int Bytes)
- //****************************************************************************
- {
- unsigned int i; unsigned retry,temp;
- //Send Command CMD to MMC/SD-Card
- retry=0;
- do
- { //Retry 100 times to send command.
- temp=Write_Command_MMC(CMD); //检测后7比特 ,
- retry++;
- if(retry==100)
- {
- return(READ_BLOCK_ERROR); //block write Error!
- }
- }
- while(temp!=0);
- //Read Start Byte form MMC/SD-Card (FEh/Start Byte)
- while (Read_Byte_MMC() != 0xfe){};
- //Write blocks(normal 512Bytes) to MMC/SD-Card
- for (i=0;i<Bytes;i++)
- {
- *Buffer++ = Read_Byte_MMC();
- }
- //CRC-Byte
- Read_Byte_MMC();//CRC - Byte
- Read_Byte_MMC(); //CRC - Byte
- //set MMC_Chip_Select to high (MMC/SD-Card invalid)
- AT91F_PIO_SetOutput(AT91C_BASE_PIOA,MMC_Chip_Select) ;
- return('0');
- }
- //****************************************************************************
- //Routine for reading Blocks(512Byte) from MMC/SD-Card
- //Return 0 if no Error.
- unsigned char MMC_read_sector(unsigned long addr,unsigned char *Buffer)
- //****************************************************************************
- {
- //Command 16 is reading Blocks from MMC/SD-Card
- unsigned char CMD[] = {0x51,0x00,0x00,0x00,0x00,0xFF}; //Command 17 read a single block
- unsigned char temp;
- AT91F_PIO_GetInterruptStatus(AT91C_BASE_PIOA) ; //clear all interrupt.
- //Address conversation(logic block address-->byte address)
- addr = addr << 9; //addr = addr * 512
- CMD[1] = ((addr & 0xFF000000) >>24 );
- CMD[2] = ((addr & 0x00FF0000) >>16 );
- CMD[3] = ((addr & 0x0000FF00) >>8 );
- temp=MMC_Read_Block(CMD,Buffer,512);
- return(temp);
- }
- //***************************************************************************
- //读 CID Registers from MMC/SD-Card (16Bytes)
- //错误返回0.
- unsigned char Read_CID_MMC(unsigned char *Buffer)
- //***************************************************************************
- {
- //Command for reading CID Registers //Command 10
- unsigned char CMD[] = {0x4A,0x00,0x00,0x00,0x00,0xFF};
- unsigned char temp;
- temp=MMC_Read_Block(CMD,Buffer,16); //read 16 bytes ***CID为128字节的寄存器
- return(temp);
- }
- //***************************************************************************
- //读 CSD Reg from MMC/SD-Card (16Bytes)
- //错误返回0.
- unsigned char Read_CSD_MMC(unsigned char *Buffer)
- //***************************************************************************
- {
- //Command for reading CSD Registers
- unsigned char CMD[] = {0x49,0x00,0x00,0x00,0x00,0xFF}; //Command 9
- unsigned char temp;
- temp=MMC_Read_Block(CMD,Buffer,16); //read 16 bytes CSD长度为512=16*16
- return(temp);
- }
- //***************************************************************************
- //返回制: [0]-success or something error!
- unsigned char MMC_Start_Read_Sector(unsigned long sector) //读一个扇区
- //***************************************************************************
- {
- unsigned char retry;
- //Command 16 is reading Blocks from MMC/SD-Card
- unsigned char CMD[] = {0x51,0x00,0x00,0x00,0x00,0xFF}; //command 17 1010001
- unsigned char temp;
- AT91F_PIO_GetInterruptStatus(AT91C_BASE_PIOA); //clear all interrupt.
- //Address conversation(logic block address-->byte address)
- sector = sector << 9; //sector = sector * 512
- CMD[1] = ((sector & 0xFF000000) >>24 );
- CMD[2] = ((sector & 0x00FF0000) >>16 );
- CMD[3] = ((sector & 0x0000FF00) >>8 );
- //Send Command CMD to MMC/SD-Card
- retry=0;
- do
- { //Retry 100 times to send command.
- temp=Write_Command_MMC(CMD);
- retry++;
- if(retry==100)
- {
- return(READ_BLOCK_ERROR); //block write Error!
- }
- }
- while(temp!=0);
- //Read Start Byte form MMC/SD-Card (FEh/Start Byte)
- //Now data is ready,you can read it out.
- while (Read_Byte_MMC() != 0xfe); //FE =11111110
- return 0; //Open a sector successfully!
- }
- //***************************************************************************
- void MMC_get_data(unsigned int Bytes,unsigned char *buffer) // 读取当前位置后一定数目的字节数据
- //***************************************************************************
- {
- unsigned int j;
- AT91F_PIO_GetInterruptStatus(AT91C_BASE_PIOA); //clear all interrupt.
- for (j=0;((j<Bytes) && (readPos<512));j++)
- {
- *buffer++ = Read_Byte_MMC();
- readPos++; //read a byte,increase read position
- }
- if (readPos==512) //如果读完整个扇区,最后一字节即为CRC校验位
- { //CRC-Bytes
- Read_Byte_MMC(); //CRC - Byte
- Read_Byte_MMC(); //CRC - Byte
- readPos=0; //reset sector read offset
- sectorPos++; //Need to read next sector
- LBA_Opened=0; //Set to 1 when a sector is opened.
- //set MMC_Chip_Select to high (MMC/SD-Card invalid)
- AT91F_PIO_SetOutput(AT91C_BASE_PIOA,MMC_Chip_Select) ; //MMC disable
- }
- }
- //***************************************************************************
- void MMC_get_data_LBA(unsigned long lba, unsigned int Bytes,unsigned char *buffer)
- //***************************************************************************
- { //get data from lba address of MMC/SD-Card
- //If a new sector has to be read then move head
- if (readPos==0) MMC_Start_Read_Sector(lba);
- MMC_get_data(Bytes,buffer);
- }
- //***************************************************************************
- void MMC_GotoSectorOffset(unsigned long LBA,unsigned int offset)
- //***************************************************************************
- {
- //Find the offset in the sector
- unsigned char temp[1];
- MMC_LBA_Close(); //close MMC when read a new sector(readPos=0)
- while (readPos<offset) MMC_get_data_LBA(LBA,1,temp); //go to offset
- }
- //***************************************************************************
- void MMC_LBA_Close()
- //***************************************************************************
- {
- unsigned char temp[1];
- while((readPos!=0x00)|(LBA_Opened==1))
- { //read MMC till readPos==0x00
- MMC_get_data(1, temp); //dummy read,temp is a valid data.
- }
- }
- //----------------------------------------------------------------------------
最近总结了一下关于eMMC分区的一些资料,在此分享给大家,希望对大家在这方面的工作有所帮助:
大家一般所熟悉的分区的概念是在逻辑上将一个磁盘或存储设备分为几个区,每个区当做独立磁盘,以方便使用和管理。例如第一个磁盘的第一个分区叫做sda1,第二个磁盘的第二个分区叫做sda2;第二个磁盘的第一个分区叫做sdb1,第二个磁盘的第二个分区叫做sdb2。
大家所最为熟知的分区方式同时也是最主流的主要有两种:MBR(MasterBoot Record)和GPT(GUID PartitionTable)。前者应用于绝大多数使用BIOS引导的PC设备(苹果使用EFI的方式),而后者主要是针对MBR的一些缺点进行了改进同时还可以兼容MBR并且支持2TB以上的存储(MBR不支持2TB以上的存储设备)。
Android 2.x.x 版本上使用的是MBR,4.0版本以后就是使用的GPT分区方式。
注意,不管是MBR还是GPT,他们的分区都是指“逻辑上”的!!!即通过软件实现的,文件系统级别的。而我现在要说明的是eMMC本身自己的分区,即物理上的,不是通过软件就能实现的分区。
EMMC的分区有一些是AP不能修改的(如BOOT1、BOOT2和RPMB分区),有一些是可以通过特定的命令和寄存器就可以修改的(如EnhancedPartition和GPAP)。下面就来集体说明一下:
通常,从厂家出来的eMMC主要由这几个部分组成:
1. BOOT Area Partition 1
2. BOOT Area Partition 2
3. RPMB
4. User Data Area
5. Vender private area
上面这5个部分中前4项是AP可以通过配置寄存器进行读写并且User DataArea还可以进行分区配置的,如上图所示;下面来对上面的5个部分做一个详细说明:
1. Boot 1 & Boot 2
这两个分区是由厂家在生产过程中配置好了的,并且其大小是不能由AP进行配置的,当然,如果你的公司够牛,量足够大,并且也有这个需求的时候可以去要求厂家重新配置此区域大小,给你专门供货。
Boot 1 和Boot2这两个区域在存储的稳定性、可靠性及擦除次数上都远比UDA要好(至于原因请往后看),所以很多chipset上都会使用这两个区域来存放一下关键数据,如bootimage,default配置参数等等。当然不同的chipset的配置方法也不尽相同。这个可以找chipset的工程师询问。据笔者所知:以大陆市占最大的两家chipset为例,MTK使用UDA来存放bootdata,而使用bootarea来存放配置参数;Qualcomm则使用Boot 1 来存放boot data,boot 2 来存放配置参数。
另外,不同的eMMC版本一般对Boot area和RPMB的容量大小需求也不同,如下作一个简单的参考:
2. RPMB
RPMB是ReplayProtected Memory Block的缩写,他的存在目的是用来给系统存放一些特殊的、需要进行访问授权的数据;他的请求及回应类型如下所:
据笔者所知,目前大陆的手机及平板厂商还没有一家使用到此区域的。
3. UDA
User Data Area就是AP及用户可以进行读写存储的区域,通常其大小为整块EMMC表示大小的93%左右,即4GB的eMMCUDA的区域只有4GB*93%=3809MB。
之前说的BOOT1&2、RPMB和UDA区域我们都可以认为他们在物理上是独立的(当然都是存在于同一块die上)。即他们各自的物理起始地址都是0x0。这个在出厂的时候就会设置完成。下面我们就来说两种可以在物理上进行独立分区的方式:
a) GPAP
GPAP即GeneralPurpose Area Partitions,eMMC 的spec上定义每个eMMC 最多可以通过配置寄存器来定义4个GPAP:
GPAP配置定义完成之后每一个GPAP的起始地址都为0x0;即可以相应地将其认为是独立的一块区域。只是在存放数据的时候会需要从新根据他的起始地址进行计算然后再存储数据。这样必然会增加一定的工作量;据笔者所知,目前大陆的手机及平板几乎没有用到这个功能。都是使用一整块的UDA,然后通过文件系统去进行逻辑上的分区使用。
我想肯定会有读者想问那这个功能到底有什么用呢?我想说eMMC是一个通过的存储设备,并不止是为手机和平板使用。当一个设备有多个CPU的时候并且他们的功能还不同时,这个时候使用GPAP这个功能就非常方便了。
b) Enhanced Partition
EnhancedPartition这也是一个在手机及平板上使用较少的功能。为什么通过配置原本的UDA就可以变成“Enhanced”的呢?既然这么有用,为什么不将整个UDA配置成为”Enhanced”的呢?别着急,我来一一作答。
我们知道eMMC只是指他的接口标准,而他真正的存储介质还是NAND Flash,而NAND又分为SLC、MLC和TLC(详细区别请参考我之前的文章),他们的稳定性、可靠性和擦除次数又有很大区别,当然中国厂商最关心的成本也相差很大;目前市场上主流的eMMC还是以MLC的NAND存储介质为主,而TLC的eMMC也在逐渐的增加。其中以Samsung的TLC 的emmc最为成熟市占率也最高。我们这里先以MLC的EMMC来进行介绍:
以现在市面上最先进的NAND制程20nm的MLC为例,擦除次数大概在3000~5000cycle。而SLC的擦除次数则在25000~40000cycle。很明显SLC要比MLC性能更好,数据存储更稳定。
而我们这里介绍的EnhancedPartition的主要功能就是将MLC配置成为SLC。现在大家明白他为什么被称之为“Enhanced”的了!是相对于MLC(也就是defaultstorage media)来说的。
当然,从MLC配置为SLC不是没有代价的,这个代价自然就是容量变小,会变多小呢?容量只有原来的一半!!!原本1GB的MLC通过配置成SLC就只剩下512MB了,你说谁会愿意这样去大容量地转换呢?
据笔者所知,目前使用过这个功能的就只有台湾的HTC,他们是用来存储bootdata。另外,大陆也有一家大厂正在尝试使用EnhancedPartition来做为swap 虚拟内存使用。
4. Vender Private Area
在eMMC里面除了AP能操作(即可识别并且可以通过地址进行访问)的boot1&2、RPMB和UDA之外,其实还有一小部分区域是AP看不见也不能进行操作的。这部分区域是由生产厂家预留的,他主要是用来存放这样一些内容:eMMC的FW(想知道是什么请参考我之前的文章),eMMC在boot的时候的code,FTL(FlashTransilation Layer)以及在厂家生产过程中产生的坏块等等。
PS: 不是所有厂家的eMMC都支持EnhancedPartition这个功能,但是只要这个eMMC是支持这个功能的那么他的BOOTArea和RPMB就必须就Enhanced storagemedia。