SPI驅動SD卡 親測成功
阿新 • • 發佈:2018-12-20
SD卡指令定義 :
// SD卡型別定義 #define SD_TYPE_ERR 0X00 #define SD_TYPE_MMC 0X01 #define SD_TYPE_V1 0X02 #define SD_TYPE_V2 0X04 #define SD_TYPE_V2HC 0X06 // SD卡指令表 #define CMD0 0 //卡復位 #define CMD1 1 #define CMD8 8 //命令8 ,SEND_IF_COND #define CMD9 9 //命令9 ,讀CSD資料 #define CMD10 10 //命令10,讀CID資料 #define CMD12 12 //命令12,停止資料傳輸 #define CMD16 16 //命令16,設定SectorSize 返回0x00 #define CMD17 17 //命令17,讀sector #define CMD18 18 //命令18,讀 Multi sector #define CMD23 23 //命令23,設定多sector寫入前預先擦除N個block #define CMD24 24 //命令24,寫sector #define CMD25 25 //命令25,寫Multi sector #define CMD41 41 //命令41,返回0x00 #define CMD55 55 //命令55,返回0x01 #define CMD58 58 //命令58,讀OCR資訊 #define CMD59 59 //命令59,使能/禁止CRC,應返回0x00 //資料寫入迴應字意義 #define MSD_DATA_OK 0x05 #define MSD_DATA_CRC_ERROR 0x0B #define MSD_DATA_WRITE_ERROR 0x0D #define MSD_DATA_OTHER_ERROR 0xFF //SD卡迴應標誌字 #define MSD_RESPONSE_NO_ERROR 0x00 #define MSD_IN_IDLE_STATE 0x01 #define MSD_ERASE_RESET 0x02 #define MSD_ILLEGAL_COMMAND 0x04 #define MSD_COM_CRC_ERROR 0x08 #define MSD_ERASE_SEQUENCE_ERROR 0x10 #define MSD_ADDRESS_ERROR 0x20 #define MSD_PARAMETER_ERROR 0x40 #define MSD_RESPONSE_FAILURE 0xFF #define GPIO_NSS GPIO_Pin_12 #define GPIO_SCK GPIO_Pin_13 #define GPIO_MISO GPIO_Pin_14 #define GPIO_MOSI GPIO_Pin_15 #define CS_H GPIO_SetBits(GPIOB,GPIO_NSS); #define CS_L GPIO_ResetBits(GPIOB,GPIO_NSS); void SPI_init(void); u8 SPI_ReadWrite_Byte(u8 TxData); u8 SPI_Read_Byte(u8 txdata); u8 SD_SPI_ReadWriteByte(u8 data); void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler); u8 SD_WaitReady(void); u8 SD_GetResponse(u8 Response); u8 SD_Init_Config(void); u8 SD_ReadDisk2(u8*buf,u32 sector,u8 cnt); u8 SD_WriteDisk2(u8*buf,u32 sector,u8 cnt); u32 SD_GetSectorCount(void); u8 SD_GetCID(u8 *cid_data); u8 SD_GetCSD(u8 *csd_data);
SPI設定:
void SPI_init(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);//使能GPIOb時鐘 RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);//使能SPI2時鐘 //GPIOFB3,4,5初始化設定 GPIO_InitStructure.GPIO_Pin = GPIO_MOSI|GPIO_SCK|GPIO_MISO;//PB3~5複用功能輸出 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//複用功能 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推輓輸出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_40MHz;//40MHz GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉 GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化 GPIO_InitStructure.GPIO_Pin = GPIO_NSS;//PB3~5複用功能輸出 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//複用功能 GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化 CS_H; GPIO_PinAFConfig(GPIOB,GPIO_PinSource13,GPIO_AF_SPI2); //PB3複用為 SPI2 GPIO_PinAFConfig(GPIOB,GPIO_PinSource14,GPIO_AF_SPI2); //PB4複用為 SPI2 GPIO_PinAFConfig(GPIOB,GPIO_PinSource15,GPIO_AF_SPI2); //PB5複用為 SPI2 SPI_I2S_DeInit(SPI2); SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //設定SPI單向或者雙向的資料模式:SPI設定為雙線雙向全雙工 SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //設定SPI工作模式:設定為主SPI SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //設定SPI的資料大小:SPI傳送接收8位幀結構 SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //串行同步時鐘的空閒狀態為高電平 SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //串行同步時鐘的第二個跳變沿(上升或下降)資料被取樣 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS訊號由硬體(NSS管腳)還是軟體(使用SSI位)管理:內部NSS訊號有SSI位控制 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //定義波特率預分頻的值:波特率預分頻值為256 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定資料傳輸從MSB位還是LSB位開始:資料傳輸從MSB位開始 SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值計算的多項式 SPI_Init(SPI2, &SPI_InitStructure); //根據SPI_InitStruct中指定的引數初始化外設SPIx暫存器 SPI_Cmd(SPI2, ENABLE); //使能SPI外設 SPI_ReadWrite_Byte(0xff); } //spi讀寫一個位元組 u8 SPI_ReadWrite_Byte(u8 TxData) { while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET){}//等待發送區空 SPI_I2S_SendData(SPI2, TxData); //通過外設SPIx傳送一個byte 資料 while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET){} //等待接收完一個byte return SPI_I2S_ReceiveData(SPI2); //返回通過SPIx最近接收的資料 } //設定SPI速率 初始速率不能高於400k void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler) { assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判斷有效性 SPI2->CR1&=0XFFC7;//位3-5清零,用來設定波特率 SPI2->CR1|=SPI_BaudRatePrescaler; //設定SPI2速度 SPI_Cmd(SPI2,ENABLE); //使能SPI1 }
SD卡的一些操作函式:
//取消片選 釋放SPI匯流排 void SD_DisSelect(void) { CS_H; SPI_ReadWrite_Byte(0xff); } //等待卡準備好 //準備好了返回值0;其他錯誤程式碼 u8 SD_WaitReady(void) { u32 t=0; do { if(SPI_ReadWrite_Byte(0xff)==0XFF)return 0;//OK t++; }while(t<0XFFFFFF); return 1; } //選擇SD卡並等待卡準備好 //返回值 0成功,1失敗 u8 SD_Select(void) { CS_L; if(SD_WaitReady()==0)return 0; SD_DisSelect(); return 1; } //獲得響應 //Response 要得到的迴應? //其他響應錯誤 u8 SD_GetResponse(u8 Response) { u16 Count=0xFFFF; //等待次數 while ((SPI_ReadWrite_Byte(0xff)!=Response)&&Count)Count--; if (Count==0)return MSD_RESPONSE_FAILURE; else return MSD_RESPONSE_NO_ERROR; } //從SD卡讀取一個數據包的內容 //buf資料快取區 //len讀取的資料長度 u8 SD_RecvData(u8*buf,u16 len) { if(SD_GetResponse(0xFE))return 1; while(len--) { *buf=SPI_ReadWrite_Byte(0xff); buf++; } SPI_ReadWrite_Byte(0xff); SPI_ReadWrite_Byte(0xff); return 0; } //向SD卡寫入一個數據包的內容512位元組 //buf資料快取 //命令 //返回值0成功 其他失敗 u8 SD_SendBlock(u8*buf,u8 cmd) { u16 t; if(SD_WaitReady())return 1; SPI_ReadWrite_Byte(cmd); if(cmd!=0XFD) { for(t=0;t<512;t++)SPI_ReadWrite_Byte(buf[t]); SPI_ReadWrite_Byte(0xFF); SPI_ReadWrite_Byte(0xFF); t=SPI_ReadWrite_Byte(0xFF); if((t&0x1F)!=0x05)return 2; } return 0; } //向SD卡傳送一個命令 //u8 cmd 命令 //u32 arg 命令引數 u8 SD_SendCmd(u8 cmd, u32 arg, u8 crc) { u8 r1; u8 Retry=0; SD_DisSelect(); if(SD_Select())return 0XFF; SPI_ReadWrite_Byte(cmd | 0x40); SPI_ReadWrite_Byte(arg >> 24); SPI_ReadWrite_Byte(arg >> 16); SPI_ReadWrite_Byte(arg >> 8); SPI_ReadWrite_Byte(arg); SPI_ReadWrite_Byte(crc); if(cmd==CMD12) SPI_ReadWrite_Byte(0xff);//Skip a stuff byte when stop reading Retry=0X1F; do { r1=SPI_ReadWrite_Byte(0xFF); }while((r1&0X80) && Retry--); return r1; } //獲取SD卡的CID資訊包括 包括製造商資訊 //u8 *cid_data 存放CID的記憶體至少16個位元組 //返回 0:NO_ERR // 1:錯誤 u8 SD_GetCID(u8 *cid_data) { u8 r1; r1=SD_SendCmd(CMD10,0,0x01); if(r1==0x00) { r1=SD_RecvData(cid_data,16); //接收16個位元組的資料 } SD_DisSelect(); if(r1)return 1; else return 0; } //獲取SD卡的CSD資訊包括 包括容量和速度資訊 //u8 *cid_data 存放CsD的記憶體至少16個位元組 //返回 0:NO_ERR // 1:錯誤 u8 SD_GetCSD(u8 *csd_data) { u8 r1; r1=SD_SendCmd(CMD9,0,0x01); if(r1==0) { r1=SD_RecvData(csd_data, 16); } SD_DisSelect(); if(r1)return 1; else return 0; } //獲取SD卡總扇區數 //返回值:0: 取出容量出錯 // 其他:SD容量(扇區數/512位元組) u32 SD_GetSectorCount(void) { u8 csd[16]; u32 Capacity; u8 n; u16 csize; //取CSD資訊 if(SD_GetCSD(csd)!=0) return 0; if((csd[0]&0xC0)==0x40) //V2.00卡 { csize = csd[9] + ((u16)csd[8] << 8) + 1; Capacity = (u32)csize << 10;//得到扇區數 }else//V1.XX卡 { 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= (u32)csize << (n - 9);//得到扇區數 } return Capacity; } //初始化SD u8 SD_Init_Config(void) { u8 r1; // u16 retry; // u8 buf[4]; u16 i; SPI_init(); //初始化IO for(i=0;i<10;i++)SPI_ReadWrite_Byte(0XFF);//傳送至少74個脈衝 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]=SPI_ReadWrite_Byte(0XFF); //Get trailing return value of R7 resp if(buf[2]==0X01&&buf[3]==0XAA)//卡是否支援2.7~3.6V { retry=0XFFFE; do { SD_SendCmd(CMD55,0,0X01); //傳送CMD55 r1=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]=SPI_ReadWrite_Byte(0XFF);//得到OCR值 if(buf[0]&0x40)SD_Type=SD_TYPE_V2HC; //檢測CCS else SD_Type=SD_TYPE_V2; } } }else//SD V1.x/ MMC V3 { SD_SendCmd(CMD55,0,0X01); //傳送CMD55 r1=SD_SendCmd(CMD41,0,0X01); //傳送CMD41 if(r1<=1) { SD_Type=SD_TYPE_V1; retry=0XFFFE; do { SD_SendCmd(CMD55,0,0X01); //傳送CMD55 r1=SD_SendCmd(CMD41,0,0X01);//傳送CMD41 }while(r1&&retry--); }else { SD_Type=SD_TYPE_MMC;//MMC V3 retry=0XFFFE; do { r1=SD_SendCmd(CMD1,0,0X01); }while(r1&&retry--); } if(retry==0||SD_SendCmd(CMD16,512,0X01)!=0)SD_Type=SD_TYPE_ERR; } } SD_DisSelect(); SPI2_SetSpeed(SPI_BaudRatePrescaler_2); if(SD_Type)return 0; else if(r1)return r1; return 0xaa; } u8 SD_ReadDisk2(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); } }else { r1=SD_SendCmd(CMD18,sector,0X01); do { r1=SD_RecvData(buf,512); buf+=512; }while(--cnt && r1==0); SD_SendCmd(CMD12,0,0X01); } SD_DisSelect(); return r1; } //u8*buf資料快取 //sector起始扇區 //扇區數 u8 SD_WriteDisk2(u8*buf,u32 sector,u8 cnt) { u8 r1; if(SD_Type!=SD_TYPE_V2HC)sector *= 512; if(cnt==1) { r1=SD_SendCmd(CMD24,sector,0X01); if(r1==0) { r1=SD_SendBlock(buf,0xFE); } }else { if(SD_Type!=SD_TYPE_MMC) { SD_SendCmd(CMD55,0,0X01); SD_SendCmd(CMD23,cnt,0X01); } r1=SD_SendCmd(CMD25,sector,0X01); if(r1==0) { do { r1=SD_SendBlock(buf,0xFC); buf+=512; }while(--cnt && r1==0); r1=SD_SendBlock(0,0xFD); } } SD_DisSelect(); return r1; }