熟悉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。