基於stm32f103zet6的FAT16檔案系統學習0(讀SD卡扇區)

來源:互聯網
上載者:User

SD卡已經看了兩天了,主要是因為測試出來的卡容量不對,所以一直找原因,最終還是發現了,總比不過是單位上面出現了問題,或許是之前沒有接觸到SD的緣故吧,所以對其中的一些寄存器很不瞭解,一切都是重新開始,對照這寄存器手冊,理解程式,修改程式。一步步還是總結一下!

首先關於SD卡的協議是有必要瞭解的,我今天花了一上午的課堂時間來理解這個SD卡的協議,就是基於這個文檔的,這個文檔很適合入門SD協議的(個人認為)。http://download.csdn.net/detail/king_bingge/5218183

初識SD之後,就可以開始正式學習SD卡了!

一、要使用SD卡,那麼首先肯定得對SD卡進行初始化,那麼如何進行初始化呢?(命令的參數暫且不提)

1、這裡涉及到很多指令了。協議規定了在給SD卡上電之後需要給出至少74個時鐘脈衝後,才能進行相關的SD初始化工作,雖然是這麼說,但是我不給74個時鐘,他照樣能初始化,看看。

for(i=0;i<10;i++)SD_SPI_ReadWriteByte(0XF);

但是,或許為了能夠更加成功的初始化吧,所以有這個規定所以,我們還是規規矩矩的好,給它74個時鐘,沒關係的嘛!

2、然後就是協議中說到當我們複位或者上電的時候,SD卡的SD控制寄存器處於卡識別模式中的空閑模式的,暫且這樣稱吧。本來我們是不需要發送複位命令了的,但是我們不知道我們的SD所支援的電壓範圍。所以,我們最好還是先給出一條複位指令,然後緊接著一條擷取工作電壓的指令,這樣也是比較保險,如果多SD卡工作電壓有疑問的,那麼就得去看晶片手冊了。有了這個知識,那下面的代碼就不成問題了

retry=20;do{r1=SD_SendCmd(CMD0,0,0x95);//進入IDLE狀態}while((r1!=0X01) && retry--); SD_Type=0;//預設無卡if(r1==0X01){if(SD_SendCmd(CMD8,0x1AA,0x87)==1)//SD V2.0{for(i=0;i<4;i++)buf[i]=SD_SPI_ReadWriteByte(0XFF);//Get trailing return value of R7 respif(buf[2]==0X01&&buf[3]==0XAA)//卡是否支援2.7~3.6V

3、協議上還提到ACMD41命令的目的是給予 SD卡控制器一個識別 SD卡是否可以在所給Vdd 範圍下工作的機制,如果 SD 記憶卡無法在指定 Vdd 範圍內工作,則它會進入非使用中(Inactive state ),所以我們接下來需要發送這個命令,但是在發送這個命令之前,要知道這是一個應用型的命令,所以要加上CMD55命令,所以有了下面的代碼。

if(buf[2]==0X01&&buf[3]==0XAA)//卡是否支援2.7~3.6V{retry=0XFFFE;do{SD_SendCmd(CMD55,0,0X01);//發送CMD55r1=SD_SendCmd(CMD41,0x40000000,0X01);//發送CMD41}while(r1&&retry--);if(retry&&SD_SendCmd(CMD58,0,0X01)==0)//鑒別SD2.0卡版本開始//擷取供電狀態{for(i=0;i<4;i++)buf[i]=SD_SPI_ReadWriteByte(0XFF);//得到OCR值if(buf[0]&0x40)SD_Type=SD_TYPE_V2HC;    //檢查CCSelse SD_Type=SD_TYPE_V2;   }

這樣就擷取了卡的類型了,至此卡的初始化基本完成,當然根據協議上,我們還可以在這裡修改相對位址之類的。如果有必要的話,可以這樣做!

二、初始化完SD卡,接下來如果你想查看我們SD的容量,可以這樣做!

之前就是因為卡容量的問題,所以鬱悶了好久,理解了個大概!注意這裡函數名是讀取扇區數,實際上返回的值是我們卡的容量,這裡得注意了。

1、首先看代碼

u32 SD_GetSectorCount(void){    u8 csd[16];    u32 Capacity_KB,Capacity_MB ;    u8 n;u16 csize;      //取CSD資訊,如果期間出錯,返回0    if(SD_GetCSD(csd)!=0) return 0;    n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;csize = (csd[8] >> 6) + ((u16)csd[7] << 2) + ((u16)(csd[6] & 3) << 10) + 1;Capacity_KB= (u32)csize << (n - 10);//得到扇區數 ,這裡的單位是KBCapacity_MB = Capacity_KB/1024;    return Capacity_MB;}

這個計算的問題必須得看SD卡的手冊,也就是128位的CSD寄存器。這裡我把我分析的過程貼出來,我不得不說比較亂,或許只有我自己能看懂了,懶得整理了,僅供參考!

My SD_CardCSD寄存器中的值如下:00 7f ff 32    bit(127-96)csd0 - csd35f 59 83 cb    bit(95--64)csd4 - csd7    0101 1111 0101 1001 1000 0011 1100 101176 db df ff    bit(63--32)csd8 - csd11        0111 0110 1101 1011 1101 1111 1111 111196 40 00 97    bit(31---0)csd12 -csd15csize  {62,73}csize_muti{47,49}read {80,83}csize = 1111 0010 1101 = 3885csize_muti = 111 = 7read = 1001 = 9 計算公式:blocknr = (csize+1)*mult= mult = (csize_muti < 8)*(2^(csize_muti + 2))   block_len = (read < 12)*(2^(read)) capacity = blocknr * block_len = 13*4*3516*98304依據下面代碼來計算我的容量:n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;csize = (csd[8] >> 6) + ((u16)csd[7] << 2) + ((u16)(csd[6] & 3) << 10) + 1;Capacity_KB= (u32)csize << (n - 10);//得到扇區數 ,這裡的單位是KB  // 00 7f ff 32 5f 59 83 cb  76 db df ff 96 40 00 97Capacity_MB = Capacity_KB/1024;1、(csd[8] >> 6) 得到的是 bit62和bit63的值去掉2位2、((u16)csd[7] << 2)得到的是bit64--bit69的值去掉6位3、((u16)(csd[6] & 3) << 10)得到的是bit70--bit73的值

其實我的問題還是出現在單位上面!

這樣我們就能看到顯示的容量值了,我的是1G的。列印出來是971M,和windows下面的是一致的。其實我們可以通過讀SD卡的開機磁區,從而把相關的資訊讀取出來,而不需要使用那些個寄存器。那麼現在我們的計算公式就是(這隻是我自己信手寫的,如果想要理解,你必須得看扇區的內容咯,我就是對照著那個MBR來寫的)

x=(((buf_read[34])*64*1024+(buf_read[33])*256+(buf_read[32])))*512/1024/1024;//列印大小printf("\n SD Sector Size:%d  Mb\n",x);

雖然不怎麼雅觀,但是能用就是了。

2、接下來看如何用SPI讀一個扇區吧,先看代碼

u8 SD_ReadDisk(u8*buf,u32 sector,u8 cnt){u8 r1;if(SD_Type!=SD_TYPE_V2HC)sector <<= 9;//轉換為位元組地址if(cnt==1){r1=SD_SendCmd(CMD17,sector,0X01);//讀命令if(r1==0)//指令發送成功{r1=SD_RecvData(buf,512);//接收512個位元組   }}else{r1=SD_SendCmd(CMD18,sector,0X01);//連續讀命令do{r1=SD_RecvData(buf,512);//接收512個位元組 buf+=512;  }while(--cnt && r1==0); SD_SendCmd(CMD12,0,0X01);//發送停止命令}   SD_DisSelect();//取消片選return r1;//}

這幾行代碼能實現單個和多個扇區的讀寫,跟蹤進去可以能夠看到這個函數

//SPIx 讀寫一個位元組//TxData:要寫入的位元組//傳回值:讀取到的位元組u8 SPIx_ReadWriteByte(u8 TxData){u8 retry=0; while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //檢查指定的SPI標誌位設定與否:發送緩衝空標誌位{retry++;if(retry>200)return 0;}  SPI_I2S_SendData(SPI1, TxData);  //通過外設SPIx發送一個資料retry=0;while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); //檢查指定的SPI標誌位設定與否:接受緩衝非空標誌位{retry++;if(retry>200)return 0;}      return SPI_I2S_ReceiveData(SPI1); //返回通過SPIx最近接收的資料    }

這個函數的功能實現了既可以進行發送又可以進行讀取資料,現在來總結一下!

讀一個扇區的過程

1、先發命令r1=SD_SendCmd(CMD17,sector,0X01);//讀單個扇區的命令

2、然後將接收到得資料存在臨時數組裡面

u8 SD_RecvData(u8*buf,u16 len){    if(SD_GetResponse(0xFE))return 1;//等待SD卡發回資料起始令牌0xFE    while(len--)//開始接收資料    {        *buf=SPIx_ReadWriteByte(0xFF);        buf++;    }    //下面是2個偽CRC(dummy CRC)    SD_SPI_ReadWriteByte(0xFF);    SD_SPI_ReadWriteByte(0xFF);          return 0;//讀取成功}

那麼對應的寫扇區也類似的

1、先發寫單個扇區的命令 r1=SD_SendCmd(CMD24,sector,0X01);//寫命令

2、將Buffer裡面的內容寫到對應的扇區裡面去

u8 SD_SendBlock(u8*buf,u8 cmd){u16 t;    if(SD_WaitReady())return 1;//等待準備失效SD_SPI_ReadWriteByte(cmd);if(cmd!=0XFD)//不是結束指令{for(t=0;t<512;t++)SPIx_ReadWriteByte(buf[t]);//提高速度,減少函數傳參時間    SD_SPI_ReadWriteByte(0xFF);//忽略crc    SD_SPI_ReadWriteByte(0xFF);t=SD_SPI_ReadWriteByte(0xFF);//接收響應if((t&0x1F)!=0x05)return 2;//響應錯誤      }           return 0;//寫入成功}

到這裡,讀寫扇區就完成了,下一步就是,使用檔案系統來進行操作了。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.