单片机读取SD卡CSD寄存器获取容量详解(V1.0&V2.0)
目前,SD卡CSD寄存器有V1.0和V2.0两个版本。
下面是V1.0和V2.0的CSD寄存器各位的定义表格。
1.单片机如何识别SD卡是V1.0还是V2.0的?
答:CSD寄存器为128个位,即16个字节。通过检测CSD寄存器的bit126是0还是1来判断。如果是0,即是V1.0版本的;如果是1,即是V2.0版本的。
单片机获取CSD数据流程(SPI方式):定义一个数组csd[16],CS引脚拉低,发送命令9,然后SPI发送命令0xff,同时观察SPI接收到的数据,如果数据不是0xfe,就再发送0xff,如此循环,直到接收到0xfe,就可以开始接收16个字节CSD寄存器数据了,这16个字节即CSD的128个位,接收完16个字节数据以后,然后再发送两个0xff,把CS拉高,再发送一个0xff,就完成了。
csd[0]是CSD寄存器的bit120~127,csd[15]是CSD寄存器的bit0~7。也就是说数据是从高位开始发送。
列个表格方便写程序:
uint8_t SD_GetCSD(uint8_t *csd_data) { uint8_t res; res=SD_SendCommand(CMD9,0,0xFF); if(res)return res; SD_ReceiveData(csd_data, 16, RELEASE); return 0; } uint8_t SD_ReceiveData(uint8_t *data, uint16_t len, uint8_t release) { uint8_t n; uint16_t d; SD_CS_Low; if(SD_GetResponse(0xFE)) { SD_CS_High; return 1; } while(len--) { *data=SPI0_communication(0xFF); data++; } SPI0_communication(0xFF); SPI0_communication(0xFF); if(release==RELEASE) { SD_CS_High; SPI0_communication(0xFF); } return 0; } uint8_t SD_GetResponse(uint8_t Response) { uint16_t Count=0xFFF; while ((SPI0_communication(0XFF)!=Response)&&Count)Count--; if (Count==0)return MSD_RESPONSE_FAILURE; else return MSD_RESPONSE_NO_ERROR; } uint8_t SD_SendCommand(uint8_t cmd, uint32_t arg, uint8_t crc) { uint8_t r1; uint8_t repeat=0; SD_CS_High; SPI0_communication(0xff); SPI0_communication(0xff); SPI0_communication(0xff); SD_CS_Low; SPI0_communication(cmd | 0x40);//·?±??????ü?? SPI0_communication(arg >> 24); SPI0_communication(arg >> 16); SPI0_communication(arg >> 8); SPI0_communication(arg); SPI0_communication(crc); while((r1=SPI0_communication(0xFF))==0xFF) { repeat++; if(repeat>200)break; } SD_CS_High; SPI0_communication(0xFF); return r1; }
2.V1.0容量的计算
从V1.0的CSD寄存器可以看到,关于容量的位如下所示:
手册上提供的容量计算公式和方法如下:
C_SIZE是bit62~73,即共12个位,在接收到的数组中,位于csd[6]的低2位,csd[7]的8位,csd[8]的高2位。
C_SIZE?= (csd[8] >> 6) + ((uint16_t)csd[7] << 2) + ((uint16_t)(csd[6] & 3) << 10) ;
C_SIZE_MULT是bit47~49,共3个位,在接收到的数组中,位与csd[10]的最高位和csd[9]的低两位。
C_SIZE_MULT =?((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1);
READ_BL_LEN是bit80~83,共4个位,在接收的数组中,位于csd[5]的低四位。
READ_BL_LEN = csd[5] & 15;
技巧提示:上面的公式中,“&”符号用来清除不需要的位,如csd[6]&3,3写成二进制即0000 0011,即“把csd[6]寄存器的高6位清除,保留低2位”
需要用到的位都计算好了,就可以带入公式了。
由公式memory capacity = BLOCKNR*BLOCK_LEN和BLOCKNR=(C_SIZE+1)*MULT可得:
memory capacity = (C_SIZE+1)*MULT*BLOCK_LEN
公式中,3个参数在上面都计算好了,所以:
memory capacity = (C_SIZE+1)<<(C_SIZE_MULT+2+BLOCK_LEN);
计算出来的单位是字节。
如果需要转换成Kbyte,左移10位即可实现,也可以把结果除以1024,或者把上面的公式写成如下:
memory capacity = (C_SIZE+1)<<(C_SIZE_MULT+2+BLOCK_LEN-10);
技巧提示:c语言中,2的n次方,可以用1<<n(左移n位)来解决。例如2的1次方就是1左移1位,2的2次方就是1左移2位,2的3次方就是1左移3位……你可以自己举个例子算算。
3.V2.0容量的计算
V2.0的CSD寄存器中,关于容量的寄存器如下图所示:
V2.0只有1个C_SIZE和容量相关了,共22个位,从bit48~69,位于csd[7] csd[8] csd[9]
手册上提供的公式和方法如下图所示:
这个计算起来,比V1.0容易多了。乘以512,即左移9位。
csize = csd[9] + ((uint32_t)csd[8] << 8) + ((uint32_t)(csd[7] & 63) << 16) + 1; Capacity = csize << 9;
注意:这时候算出来的容量单位是Kbyte,实际应用中,注意单位的转换。
1G=1024M, 1M=1024K,1K=1024byte
4.总结
读取SD卡CSD寄存器,通过bit126位判断遵循哪个协议,再计算容量。
uint32_t SD_GetCapacity(void) { uint8_t csd[16]; uint32_t Capacity; uint16_t n; uint16_t csize; if(SD_GetCSD(csd)!=0) return 0; if((csd[0]&0xC0)==0x40)//判断bit126是否为1 { csize = csd[9] + ((uint32_t)csd[8] << 8) + ((uint32_t)(csd[7] & 63) << 16) + 1; Capacity = csize << 9; } else { n = (csd[5] & 0x0F) + ((csd[10] & 0x80) >> 7) + ((csd[9] & 0x03) << 1) + 2; csize = (csd[8] >> 6) + ((uint16_t)csd[7] << 2) + ((uint16_t)(csd[6] & 0x03) << 10) + 1; Capacity = (uint32_t)csize << (n - 10); } return Capacity; }
此函数计算出来的容量单位为Kbyte,结果除以1024就是Mbyte,再除以1024就是Gbyte。2G的卡,结果可能是1.8G;8G的卡,结果可能是7.6G,代表用户可用的容量。