新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > 單片機對SD卡讀寫系列(二)

        單片機對SD卡讀寫系列(二)

        作者: 時間:2016-11-23 來源:網絡 收藏
        寫命令的例程:

        本文引用地址:http://www.104case.com/article/201611/320311.htm
        1. //-----------------------------------------------------------------------------------------------
        2. SD卡中寫入命令,并返回回應的第二個字節
        3. //-----------------------------------------------------------------------------------------------
        4. unsignedcharWrite_Command_SD(unsignedchar*CMD)
        5. {
        6. unsignedchartmp;
        7. unsignedcharretry=0;
        8. unsignedchari;
        9. //禁止SD卡片選
        10. SPI_CS=1;
        11. //發送8個時鐘信號
        12. Write_Byte_SD(0xFF);
        13. //使能SD卡片選
        14. SPI_CS=0;
        15. //向SD卡發送6字節命令
        16. for(i=0;i<0x06;i++)
        17. {
        18. Write_Byte_SD(*CMD++);
        19. }
        20. //獲得16位的回應
        21. Read_Byte_SD();//readthefirstbyte,ignoreit.
        22. do
        23. {//讀取后8位
        24. tmp=Read_Byte_SD();
        25. retry++;
        26. }
        27. while((tmp==0xff)&&(retry<100));
        28. return(tmp);
        29. }

        2)初始化
        SD卡的初始化是非常重要的,只有進行了正確的初始化,才能進行后面的各項操作。在初始化過程中,SPI的時鐘不能太快,否則會造初始化失敗。在初始化成功后,應盡量提高SPI的速率。在剛開始要先發送至少74個時鐘信號,這是必須的。在很多讀者的實驗中,很多是因為疏忽了這一點,而使初始化不成功。隨后就是寫入兩個命令CMD0與CMD1,使SD卡進入SPI模式
        初始化時序圖:


        初始化例程:

        1. //--------------------------------------------------------------------------
        2. 初始化SD卡到SPI模式
        3. //--------------------------------------------------------------------------
        4. unsignedcharSD_Init()
        5. {
        6. unsignedcharretry,temp;
        7. unsignedchari;
        8. unsignedcharCMD[]={0x40,0x00,0x00,0x00,0x00,0x95};
        9. SD_Port_Init();//初始化驅動端口
        10. Init_Flag=1;//將初始化標志置1
        11. for(i=0;i<0x0f;i++)
        12. {
        13. Write_Byte_SD(0xff);//發送至少74個時鐘信號
        14. }
        15. //向SD卡發送CMD0
        16. retry=0;
        17. do
        18. {//為了能夠成功寫入CMD0,在這里寫200次
        19. temp=Write_Command_SD(CMD);
        20. retry++;
        21. if(retry==200)
        22. {//超過200次
        23. return(INIT_CMD0_ERROR);//CMD0Error!
        24. }
        25. }
        26. while(temp!=1);//回應01h,停止寫入
        27. //發送CMD1到SD卡
        28. CMD[0]=0x41;//CMD1
        29. CMD[5]=0xFF;
        30. retry=0;
        31. do
        32. {//為了能成功寫入CMD1,寫100次
        33. temp=Write_Command_SD(CMD);
        34. retry++;
        35. if(retry==100)
        36. {//超過100次
        37. return(INIT_CMD1_ERROR);//CMD1Error!
        38. }
        39. }
        40. while(temp!=0);//回應00h停止寫入
        41. Init_Flag=0;//初始化完畢,初始化標志清零
        42. SPI_CS=1;//片選無效
        43. return(0);//初始化成功
        44. }

        3)讀取CID
        CID寄存器存儲了SD卡的標識碼。每一個卡都有唯一的標識碼。
        CID寄存器長度為128位。它的寄存器結構如下:

        名稱
        數據寬度
        CID劃分
        生產標識號
        MID
        8
        [127:120]
        OEM/應用標識
        OID
        16
        [119:104]
        產品名稱
        PNM
        40
        [103:64]
        產品版本
        PRV
        8
        [63:56]
        產品序列號
        PSN
        32
        [55:24]
        保留
        4
        [23:20]
        生產日期
        MDT
        12
        [19:8]
        CRC7校驗合
        CRC
        7
        [7:1]
        未使用,始終為1
        1
        [0:0]

        它的讀取時序如下:

        與此時序相對應的程序如下:

        1. //------------------------------------------------------------------------------------
        2. 讀取SD卡的CID寄存器16字節成功返回0
        3. //-------------------------------------------------------------------------------------
        4. unsignedcharRead_CID_SD(unsignedchar*Buffer)
        5. {
        6. //讀取CID寄存器的命令
        7. unsignedcharCMD[]={0x4A,0x00,0x00,0x00,0x00,0xFF};
        8. unsignedchartemp;
        9. temp=SD_Read_Block(CMD,Buffer,16);//read16bytes
        10. return(temp);
        11. }

        4)讀取CSD
        CSD(Card-Specific Data)寄存器提供了讀寫SD卡的一些信息。其中的一些單元可以由用戶重新編程。 讀取CSD 的時序:


        相應的程序例程如下:

        1. //-----------------------------------------------------------------------------------------
        2. 讀SD卡的CSD寄存器共16字節返回0說明讀取成功
        3. //-----------------------------------------------------------------------------------------
        4. unsignedcharRead_CSD_SD(unsignedchar*Buffer)
        5. {
        6. //讀取CSD寄存器的命令
        7. unsignedcharCMD[]={0x49,0x00,0x00,0x00,0x00,0xFF};
        8. unsignedchartemp;
        9. temp=SD_Read_Block(CMD,Buffer,16);//read16bytes
        10. return(temp);
        11. }

        4)讀取SD卡信息
        綜合上面對CID與CSD寄存器的讀取,可以知道很多關于SD卡的信息,以下程序可以獲取這些信息。如下:

        1. //-----------------------------------------------------------------------------------------------
        2. //返回
        3. //SD卡的容量,單位為M
        4. //sectorcountandmultiplierMBarein
        5. u08==C_SIZE/(2^(9-C_SIZE_MULT))
        6. //SD卡的名稱
        7. //-----------------------------------------------------------------------------------------------
        8. voidSD_get_volume_info()
        9. {
        10. unsignedchari;
        11. unsignedcharc_temp[5];
        12. VOLUME_INFO_TYPESD_volume_Info,*vinf;
        13. vinf=&SD_volume_Info;//Initthepointoer;
        14. /讀取CSD寄存器
        15. Read_CSD_SD(sectorBuffer.dat);
        16. //獲取總扇區數
        17. vinf->sector_count=sectorBuffer.dat[6]&0x03;
        18. vinf->sector_count<<=8;
        19. vinf->sector_count+=sectorBuffer.dat[7];
        20. vinf->sector_count<<=2;
        21. vinf->sector_count+=(sectorBuffer.dat[8]&0xc0)>>6;
        22. //獲取multiplier
        23. vinf->sector_multiply=sectorBuffer.dat[9]&0x03;
        24. vinf->sector_multiply<<=1;
        25. vinf->sector_multiply+=(sectorBuffer.dat[10]&0x80)>>7;
        26. //獲取SD卡的容量
        27. vinf->size_MB=vinf->sector_count>>(9-vinf->sector_multiply);
        28. //getthenameofthecard
        29. Read_CID_SD(sectorBuffer.dat);
        30. vinf->name[0]=sectorBuffer.dat[3];
        31. vinf->name[1]=sectorBuffer.dat[4];
        32. vinf->name[2]=sectorBuffer.dat[5];
        33. vinf->name[3]=sectorBuffer.dat[6];
        34. vinf->name[4]=sectorBuffer.dat[7];
        35. vinf->name[5]=0x00;//endflag
        36. }
        37. 以上程序將信息裝載到一個結構體中,這個結構體的定義如下:
        38. typedefstructSD_VOLUME_INFO
        39. {//SD/SDCardinfo
        40. unsignedintsize_MB;
        41. unsignedcharsector_multiply;
        42. unsignedintsector_count;
        43. unsignedcharname[6];
        44. }VOLUME_INFO_TYPE;

        5)扇區讀
        扇區讀是對SD卡驅動的目的之一。SD卡的每一個扇區中有512個字節,一次扇區讀操作將把某一個扇區內的512個字節全部讀出。過程很簡單,先寫入命令,在得到相應的回應后,開始數據讀取。
        扇區讀的時序:

        扇區讀的程序例程:

        1. unsignedcharSD_Read_Sector(unsignedlongsector,unsignedchar*buffer)
        2. {
        3. unsignedcharretry;
        4. //命令16
        5. unsignedcharCMD[]={0x51,0x00,0x00,0x00,0x00,0xFF};
        6. unsignedchartemp;
        7. //地址變換由邏輯塊地址轉為字節地址
        8. sector=sector<<9;//sector=sector*512
        9. CMD[1]=((sector&0xFF000000)>>24);
        10. CMD[2]=((sector&0x00FF0000)>>16);
        11. CMD[3]=((sector&0x0000FF00)>>8);
        12. //將命令16寫入SD卡
        13. retry=0;
        14. do
        15. {//為了保證寫入命令一共寫100次
        16. temp=Write_Command_MMC(CMD);
        17. retry++;
        18. if(retry==100)
        19. {
        20. return(READ_BLOCK_ERROR);//blockwriteError!
        21. }
        22. }
        23. while(temp!=0);
        24. //ReadStartByteformMMC/SD-Card(FEh/StartByte)
        25. //Nowdataisready,youcanreaditout.
        26. while(Read_Byte_MMC()!=0xfe);
        27. readPos=0;
        28. SD_get_data(512,buffer);//512字節被讀出到buffer中
        29. return0;
        30. }
        31. 其中SD_get_data函數如下:
        32. //----------------------------------------------------------------------------
        33. 獲取數據到buffer中
        34. //----------------------------------------------------------------------------
        35. voidSD_get_data(unsignedintBytes,unsignedchar*buffer)
        36. {
        37. unsignedintj;
        38. for(j=0;j<="" span="" style="word-wrap: break-word;">
        39. *buffer++=Read_Byte_SD();
        40. }

        6)扇區寫
        扇區寫是SD卡驅動的另一目的。每次扇區寫操作將向SD卡的某個扇區中寫入512個字節。過程與扇區讀相似,只是數據的方向相反與寫入命令不同而已。
        扇區寫的時序:

        扇區寫的程序例程:

        1. //--------------------------------------------------------------------------------------------
        2. 寫512個字節到SD卡的某一個扇區中去返回0說明寫入成功
        3. //--------------------------------------------------------------------------------------------
        4. unsignedcharSD_write_sector(unsignedlongaddr,unsignedchar*Buffer)
        5. {
        6. unsignedchartmp,retry;
        7. unsignedinti;
        8. //命令24
        9. unsignedcharCMD[]={0x58,0x00,0x00,0x00,0x00,0xFF};
        10. addr=addr<<9;//addr=addr*512
        11. CMD[1]=((addr&0xFF000000)>>24);
        12. CMD[2]=((addr&0x00FF0000)>>16);
        13. CMD[3]=((addr&0x0000FF00)>>8);
        14. //寫命令24到SD卡中去
        15. retry=0;
        16. do
        17. {//為了可靠寫入,寫100次
        18. tmp=Write_Command_SD(CMD);
        19. retry++;
        20. if(retry==100)
        21. {
        22. return(tmp);//sendcommamdError!
        23. }
        24. }
        25. while(tmp!=0);
        26. //在寫之前先產生100個時鐘信號
        27. for(i=0;i<100;i++)
        28. {
        29. Read_Byte_SD();
        30. }
        31. //寫入開始字節
        32. Write_Byte_MMC(0xFE);
        33. //現在可以寫入512個字節
        34. for(i=0;i<512;i++)
        35. {
        36. Write_Byte_MMC(*Buffer++);
        37. }
        38. //CRC-Byte
        39. Write_Byte_MMC(0xFF);//DummyCRC
        40. Write_Byte_MMC(0xFF);//CRCCode
        41. tmp=Read_Byte_MMC();//readresponse
        42. if((tmp&0x1F)!=0x05)//寫入的512個字節是未被接受
        43. {
        44. SPI_CS=1;
        45. return(WRITE_BLOCK_ERROR);//Error!
        46. }
        47. //等到SD卡不忙為止
        48. //因為數據被接受后,SD卡在向儲存陣列中編程數據
        49. while(Read_Byte_MMC()!=0xff){};
        50. //禁止SD卡
        51. SPI_CS=1;
        52. return(0);//寫入成功
        53. }

        單片機采用STC89LE單片機(SD卡的初始化電壓為2.0V~3.6V,操作電壓為3.1V~3.5V,因此不能用5V單片機,或進行分壓處理),工作于22.1184M的時鐘下,由于所采用的單片機中沒硬件SPI,采用軟件模擬SPI,因此讀寫速率都較慢。如果要半SD卡應用于音頻、視頻等要求高速場合,則需要選用有硬件SPI的控制器,或使用SD模式,有了 SPI模式的基礎,SD模式應該不是什么難事。



        評論


        技術專區

        關閉
        主站蜘蛛池模板: 弥渡县| 宜黄县| 县级市| 公安县| 宝丰县| 武陟县| 宜丰县| 富蕴县| 崇明县| 钦州市| 那曲县| 宁波市| 通山县| 丁青县| 特克斯县| 武夷山市| 开平市| 阆中市| 洪江市| 商城县| 农安县| 乌拉特后旗| 万安县| 孙吴县| 苏尼特左旗| 禹城市| 瑞昌市| 唐海县| 开鲁县| 陇西县| 云和县| 石棉县| 闸北区| 南雄市| 额济纳旗| 威海市| 五家渠市| 长治市| 石台县| 松溪县| 来安县|