SD卡 SD 卡(Seecure Digital Memory Cardl)是一種基于 Flash 的新一代存儲器,具有體積小、容量大、數據傳輸快、移動靈活、安全性能好等優點,是許多便攜式電子儀器理想的外部存儲介質
圖片1.png (27.73 KB, 下載次數: 71)
下載附件
2020-2-14 14:43 上傳
圖片2.png (77.04 KB, 下載次數: 65)
下載附件
2020-2-14 14:43 上傳
SD 卡支持兩種總線方式:SD 方式與 SPI 方式。其中 SD 方式采用 6 線制,使用 CLK、 CMD、DAT0~DAT3 進行數據通信。而 SPI 方式采用 4 線制,使用 CS、CLK、DataIn、DataOut 進行數據通信。SD 方式時的數據傳輸速度與 SPI 方式要快,采用單片機對 SD 卡進行讀寫時一般都采用 SPI 模式。采用不同的初始化方式可以使 SD 卡工作于 SD 方式或 SPI 方式。
這里只對其 SPI 方式進行介紹。 SPI 方式驅動 SD 卡的方法 SD 卡的 SPI 通信接口使其可以通過 SPI 通道進行數據讀寫。從應用的角度來看,采用 SPI 接口的好處在于,很多單片機內部自帶 SPI 控制器,不光給開發上帶來方便,同時也見降低了開發成本。然而,它也有不好的地方,如失去了 SD 卡的性能優勢,要解決這一問題,就要用 SD 方式,因為它提供更大的總線數據帶寬。SPI 接口的選用是在上電初始時向其寫入第一個命令時進行的。以下介紹 SD 卡的驅動方法,只實現簡單的扇區讀寫。 為了使SD卡初始化進入SPI模式,我們需要使用的命令有3個CMD0,CMD55,ACMD41(使用ACMD類的指令前應先發CMD55,CMD55起到一個切換到ACMD類命令的作用)。
為什么在使用CMD0以后不使用CMD1?CMD1是MMC卡使用的指令。我們上電或者發送CMD0后,應該首先發送CMD55+ACMD41確認是否有回應,如果有回應則為SD卡,如果等回應超時,則可能是MMC卡,再發CMD1確認。 SD卡調試步驟: 1. 上電時要延時足夠長的時間給SD卡一個準備的過程,SD卡初始化第一步發送CMD命令之前,在片選有效的情況下首先要發送至少74個時鐘,否則可能會出現SD卡不能初始化的問題。那么為什么要74個CLK呢?因為在上電初期,電壓的上升過程據SD卡組織的計算約合64個CLK周期才能到達SD卡的正常工作電壓他們管這個叫做Supply ramp up time,其后的10個CLK是為了與SD卡同步,之后開始CMD0的操作,嚴格按照此項操作,一定沒有問題。 2. SD卡發送CMD0命令,返回狀態為0x01,則復位成功。 3. SD卡發送復位命令CMD0后,要發送版本查詢命令CMD8,返回狀態一般分為兩種,若返回0x01,表示此SD卡接收CMD8,也就是說SD卡支持版本2;若返回0x05則表示SD卡支持版本1. 4. 理論上要求發送CMD58獲得SD卡電壓參數,但實際上都實現知道SD卡的工作電壓是3.3v,所以可以省略這一步。 5. 發送CMD55命令,返回0x01,起到轉換的作用。 6. 再發送ACMD41,等待返回0x00,則表示完成初始化。這里要說的是如果最后的回應內容還是0x01的話,可以循環發送CMD55+ACMD41,直到回應的內容0x00。 SD卡操作命令作步驟: 1.讀步驟: 發送CMD17(單塊)或CMD18(多塊)讀命令,返回0x00 接收數據開始令牌0xfe(或0xfc)+正式數據512B+CRC校驗2B 2.寫步驟: 發送CMD24(單塊)或CMD25(多塊)寫命令,返回0x00. 發送數據開始令牌0xfe(或0xfc)+正式數據512B+CRC校驗2B 3.擦除步驟: 發送CMD32,跟一個參數來指定首個要擦除的起始地址 發送CMD33,指定最后的地址 發送CMD38,擦除指定區間的內容。
向SD卡寫入一個CMD或者ACMD指令的過程是這樣的:
1.首先使CS為低電平,SD卡使能;其次在SD卡的Din寫入指令;寫入指令后還要附加8個填充時鐘,是SD卡完成內部操作;之后在SD卡的Dout上接受回應;回應接受完畢使CS為低電平,再附加8個填充時鐘。
2.在SD卡的Din沒有數據寫入時,應使Din保持高電平。 一些常用的指令格式: CMD0:0x40,0x00,0x00,0x00,0x00,0x95 CMD8:0x48,0x00,0x00,0x01,0xaa,0x87 CMD55:0x77,0x00,0x00,0x00,0x00,0xff 0x77=0x40+0x37(55的16進制表示) ACMD41:0x69,0x40,0x00,0x00,0x00,0xff CMD58:0x7a,0x00,0x00,0x00,0x00,0xff ACMD41屬于附加命令,發送起來要麻煩一些,必須提前通知SD卡下一條要發送的命令為ACMD,這個通知就是CMD55,它的4字節參數都為0即可 SD卡的命令格式: 每一個命令的長度都是固定的6個字節,前1個字節的值=命令號+0x40;中間4個字節為參數,不同的命令參數格式都不相同,但參數最多為4個字節;最后1個字節是CRC校驗碼和1位固定結束位‘1’。這里需要說明一下0x40的意思,任何命令都有一個固定的起始格式,即先0后1,這是固定的命令起始標志,前兩個字節的二進制碼就是:01xx xxxx SD 卡所有的命令都是由 48 個數據位組成的,其結構如表 2 所示。
圖片3.png (177.83 KB, 下載次數: 67)
下載附件
2020-2-14 14:43 上傳
圖片4.png (61.32 KB, 下載次數: 63)
下載附件
2020-2-14 14:43 上傳
需要特殊說明的是CRC的問題,這是一種檢驗錯誤的方法,具體問題度娘說的還算明白,在SPI模式中,CRC校驗默認是關閉的,也就是說這7位必須要發,但是SD卡會在讀到CRC以后自動忽略它,所以全部發1就可以。例外的是,CMD0,CMD8這兩個命令發送的時候SD卡還沒有進入SPI模式,也就是說CRC校驗在這個時候還是啟用狀態,因此這兩個命令的CRC效驗碼必須要寫正確,SD卡才會執行命令,否在在返回值R1中就會有相應的錯誤標志位提示開發人員CRC校驗碼錯誤。
圖片5.png (15.45 KB, 下載次數: 57)
下載附件
2020-2-14 14:43 上傳
圖片6.png (32.5 KB, 下載次數: 57)
下載附件
2020-2-14 14:43 上傳
圖片7.png (66.4 KB, 下載次數: 62)
下載附件
2020-2-14 14:43 上傳
圖片8.png (32.58 KB, 下載次數: 62)
下載附件
2020-2-14 14:43 上傳
圖片9.png (9.58 KB, 下載次數: 59)
下載附件
2020-2-14 14:43 上傳
SD卡的命令代碼: (1)復位
圖片10.png (56.03 KB, 下載次數: 64)
下載附件
2020-2-14 14:43 上傳
單片機源程序如下:
- /*********************************
- * @函數名:SD_SPI_Rest
- * @描 述:SD卡復位
- * @參 數:無
- * @返回值:成功 0 失敗 0x01
- *********************************/
- uchar SD_SPI_Rest()//SD卡復位,進入SPI模式,使用CMD0(命令0)
- {
- uchar time,temp,i;
- uchar pcmd[] = {0x40,0x00,0x00,0x00,0x00,0x95}; //命令0的字節序列
- SPI_Speed_Slow=1; //將SPI_Speed_Slow置為1
- SD_CS=1; //關閉片選
- for(i=0;i<0x0f;i++) //復位時,首先要發送最少74個時鐘信號,這是必須的!!!
- {
- SD_SPI_Write_Byte(0xff); //120個時鐘 0x0f=15*8=120個時鐘信號
- }
- SD_CS=0; //打開片選
- time=0;
- do
- {
- temp=SD_SPI_Write_CMD(pcmd);//連續寫入CMD0
- time++;
- if(time==TRY_TIME)
- {
- SD_CS=1; //關閉片選
- Send_String("初始化失敗"); //提示初始化失敗
- return(0x01);//CMD0寫入失敗
- }
- }while(temp!=0x01); //返回0x01是復位成功
- SD_CS=1; //關閉片選
- SD_SPI_Write_Byte(0xff); //按照SD卡的操作時序在這里補8個時鐘
- return 0;//返回0,說明復位操作成功
- }
復制代碼(2)查版本號 - /*********************************
- * @函數名:SD_SPI_Version
- * @描 述:讀SD卡版本號
- * @參 數:無
- * @返回值:成功 0 失敗 0x01
- *********************************/
- uchar SD_SPI_Version()
- {
- uchar time,temp;
- uchar pcmd[] = {0x48,0x00,0x00,0x01,0xaa,0x87}; // //CMD8
- SD_CS=0; //打開片選
- time=0;
- do
- temp=SD_SPI_Write_CMD(pcmd);
- time++;
- if(time==TRY_TIME)
- {
- SD_CS=1; //關閉片選
- Send_String("獲取版本號失敗");//獲取版本號失敗
- return(0x01);
- }
- }while(temp!=0x01); //返回0x01是2.0版本
- SD_CS=1; //關閉片選
- SD_SPI_Write_Byte(0xff); //按照SD卡的操作時序在這里補8個時鐘
- return 0;//返回0,說明復位操作成功
- }
復制代碼(3)初始化
圖片11.png (61.99 KB, 下載次數: 72)
下載附件
2020-2-14 14:43 上傳
- /*********************************
- * @函數名:SD_SPI_Init
- * @描 述:SD卡初始化
- * @參 數:無
- * @返回值:成功 0 失敗 0x01
- *********************************/
- uchar SD_SPI_Init() //初始化,使用ACMD41
- {
- uchar time,temp;
- uchar pcmd[] = {0x69,0x40,0x00,0x00,0x00,0xff}; //ACMD41的字節序列
- uchar pcmd1[]={0x77,0x00,0x00,0x00,0x00,0xff};//CMD55
- /*調用這兩個函數*/
- SD_SPI_Rest(); //復位SD卡
- SD_SPI_Version();//版本號
- SD_CS=0; //打開片選
- time=0;
- do
- {
- SD_SPI_Read_Byte(); //這4個讀函數,必須有,沒有將不會初始化成功
- SD_SPI_Read_Byte();
- SD_SPI_Read_Byte();
- SD_SPI_Read_Byte();
- temp=SD_SPI_Write_CMD(pcmd1); // 發送CMD55
- if(temp!=0x01)//上一次的返回值,這次沒有返回值
- {
- Send_String("CMD55錯誤");
- return 0xfb;
- }
- temp=SD_SPI_Write_CMD(pcmd);//發送ACMD41
- time++;
- if(time==TRY_TIME)
- {
- SD_CS=1; //關閉片選
- Send_String("初始化失敗");
- return(0x01);//ACMD41寫入失敗
- }
- }while(temp!=0x00); //當返回數據時0x00的時候,說明初始化成功
- SPI_Speed_Slow=0; //初始化完畢,將SPI_Speed_Slow設置為0,為了提高以后的數據傳輸速度
- SD_CS=1; //關閉片選
- SD_SPI_Write_Byte(0xff); //按照SD卡的操作時序在這里補8個時鐘
- return(0); //返回0,說明初始化操作成功
- }
復制代碼(4)寫一個扇區
圖片12.png (92.98 KB, 下載次數: 74)
下載附件
2020-2-14 14:43 上傳
- /*********************************
- * @函數名:SD_SPI_Write_Sector
- * @描 述:寫一扇區的數據
- * @參 數:addr:扇區地址 *buffer:指向數據緩沖區的指針
- * @返回值:成功 0x00 失敗 0x01
- *********************************/
- uchar SD_SPI_Write_Sector(uchar *buffer,ulong addr) //向SD卡中的指定地址的扇區寫入512個字節,使用CMD24(命令24)
- {
- uchar temp,time;
- uint i;
- uchar pcmd[] = {0x58,0x00,0x00,0x00,0x00,0xff}; //向SD卡中單個塊(512字節,一個扇區)寫入數據,用CMD24
- addr<<=9; //addr = addr * 512 將塊地址(扇區地址)轉為字節地址 [這里就限制了SD卡的最大容量為4G]
- pcmd[1]=((addr&0xff000000)>>24); //將字節地址寫入到CMD24字節序列中
- pcmd[2]=((addr&0x00ff0000)>>16);
- pcmd[3]=((addr&0x0000ff00)>>8);
- SD_CS=0;//打開SD卡片選
- time=0;
- do
- {
- temp=SD_SPI_Write_CMD(pcmd);//發送ACMD41
- time++;
- if(time==TRY_TIME)
- {
- SD_CS=1; //關閉片選
- return temp ; //命令寫入失敗
- }
- }while(temp!=0); //寫入地址,返回值是0x00正確
- for(i=0;i<100;i++) //這里要插入若干時鐘信號
- {
- SD_SPI_Write_Byte(0xff);
- }
- SD_SPI_Write_Byte(0xfe);//寫入開始字節 0xfe,后面就是要寫入的512個字節的數據
- for(i=0;i<512;i++) //將緩沖區中要寫入的512個字節寫入SD卡
- {
- SD_SPI_Write_Byte(buffer[i]); //寫入512個字節的數據
- }
- SD_SPI_Write_Byte(0xff);
- SD_SPI_Write_Byte(0xff); //兩個字節的CRC校驗碼,不用關心
- temp=SD_SPI_Read_Byte(); //讀取返回值
- if((temp&0x1F)!=0x05) //如果返回值是 XXX00101說明數據已經被SD卡接受了
- {
- SD_CS=1;
- return(0x01); //寫塊數據失敗
- }
- while(SD_SPI_Read_Byte()!=0xff);//等到SD卡不忙(數據被接受以后,
- // SD卡要將這些數據寫入到自身的FLASH中,需要一個時間)
- //忙時,讀回來的值為0x00,不忙時,為0xff
- SD_CS=1; //關閉片選
- SD_SPI_Write_Byte(0xff);//按照SD卡的操作時序在這里補8個時鐘
- return(0); //返回0,說明寫扇區操作成功
- }
復制代碼(5)讀一個扇區
圖片13.png (92.67 KB, 下載次數: 66)
下載附件
2020-2-14 14:43 上傳
- /*********************************
- * @函數名:SD_SPI_Read_Sector
- * @描 述:讀一扇區的數據
- * @參 數:addr:扇區地址 *buffer:指向數據緩沖區的指針
- * @返回值:成功 0x00 失敗 0x01
- *********************************/
- uchar SD_SPI_Read_Sector(uchar *buffer,ulong addr)//從SD卡的指定扇區中讀出512個字節,使用CMD17(17號命令)
- {
- uint j;
- uchar time,temp;
- uchar pcmd[]={0x51,0x00,0x00,0x00,0x00,0xff}; //CMD17的字節序列
- addr<<=9; //addr=addr*512 將塊地址(扇區地址)轉為字節地址
- pcmd[1]=((addr&0xff000000)>>24);//將字節地址寫入到CMD17字節序列中
- pcmd[2]=((addr&0x00FF0000)>>16);
- pcmd[3]=((addr&0x0000FF00)>>8);
- SD_CS=0;//打開片選
- time=0;
- do
- {
- temp=SD_SPI_Write_CMD(pcmd); //寫入CMD17
- time++;
- if(time==TRY_TIME)
- {
復制代碼注意: 1.2GB以內的SD卡(標準卡)和2GB以上的SD卡(大容量卡在地址訪問形式上不一樣。 2.對某一塊要進行寫操作時最好先執行擦除命令,這樣寫入的速度就能大大的提高。 3.對標準卡進行字節操作時,起始和終止必須在一個物理扇區內。不管是標準卡還是大容量卡一個讀寫讀寫命令只能對一個快進行操作,不允許跨物理層地址操作。
全部資料51hei下載地址:
SD卡.zip
(1.86 MB, 下載次數: 91)
2020-2-14 14:59 上傳
點擊文件名下載附件
SD卡資料和代碼 下載積分: 黑幣 -5
|