【STM32】SD卡讀寫(五)-STM32CubeMX HAL庫SPI操作 SD卡帶FATFS

1、添加SD卡SPI模式驅動

/** * @brief SPI1 Initialization Function * @param None * @retval None */static void MX_SPI1_Init(void){ /* USER CODE BEGIN SPI1_Init 0 */ /* USER CODE END SPI1_Init 0 */ /* USER CODE BEGIN SPI1_Init 1 */ /* USER CODE END SPI1_Init 1 */ /* SPI1 parameter configuration*/ hspi1.Instance = SPI1; hspi1.Init.Mode = SPI_MODE_MASTER; hspi1.Init.Direction = SPI_DIRECTION_2LINES; hspi1.Init.DataSize = SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; hspi1.Init.NSS = SPI_NSS_HARD_OUTPUT; hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256; hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi1.Init.TIMode = SPI_TIMODE_DISABLE; hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; hspi1.Init.CRCPolynomial = 10; if (HAL_SPI_Init(&hspi1) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN SPI1_Init 2 */ /* USER CODE END SPI1_Init 2 */ }

編寫SD卡SPI驅動 SDCard.c

#include "main.h"#include "SDCard.h" /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /* USART1 init function *//**************************************************************************/#define Dummy_Byte 0xFF /* 私有變量 ------------------------------------------------------------------*/SPI_HandleTypeDef hspiflash; extern SPI_HandleTypeDef hspi1;u8 SD_Type=0;/* function ------------------------------------------------------------------*//** * 函數功能: 從串行Flash讀取一個字節數據 * 輸入參數: 無 * 返 回 值: uint8_t:讀取到的數據 * 說 明:This function must be used only if the Start_Read_Sequence * function has been previously called. */uint8_t SPI_FLASH_ReadByte(void){ uint8_t d_read,d_send=Dummy_Byte; if(HAL_SPI_TransmitReceive(&hspiflash,&d_send,&d_read,1,0xFFFFFF)!=HAL_OK) d_read=Dummy_Byte; return d_read; } void SPI_I2S_SendData(SPI_TypeDef* SPIx, u16 Data){ /* Check the parameters */ assert_param(IS_SPI_ALL_PERIPH(SPIx)); /* Write in the DR register the data to be sent */ SPIx->DR = Data;} u16 SPI_I2S_ReceiveData(SPI_TypeDef* SPIx){ /* Check the parameters */ assert_param(IS_SPI_ALL_PERIPH(SPIx));//assert_param(IS_SPI_DIRECTION_2LINES_OR_1LINE(hspi->Init.Direction)); /* Return the data in the DR register */ return SPIx->DR;} /** * 函數功能: 往串行Flash讀取寫入一個字節數據並接收一個字節數據 * 輸入參數: byte:待發送數據 * 返 回 值: uint8_t:接收到的數據 * 說 明:無 */uint8_t SPI_FLASH_SendByte(uint8_t byte){ uint8_t d_read,d_send=byte;// if(HAL_SPI_TransmitReceive(&hspiflash,&d_send,&d_read,1,0xFFFFFF)!=HAL_OK)//{//d_read=Dummy_Byte;//} //等待發送緩衝區空// while(__HAL_SPI_GET_FLAG(&hspi1, SPI_FLAG_TXE)); //發一個字節// SPI_I2S_SendData(SPI1, d_send);HAL_SPI_Transmit(&hspi1,&d_send,1,1000);//HAL_SPI_Receive(&hspi1,&d_read,1,1000); //等待數據接收 while(__HAL_SPI_GET_FLAG(&hspi1, SPI_FLAG_RXNE)); //取數據 d_read = SPI_I2S_ReceiveData(SPI1); return d_read; } /******************************************************************************** Function Name : SD_WaitReady* Description : 等待SD卡Ready* Input : None* Output : None* Return : u8 * 0: 成功* other:失敗*******************************************************************************/u8 SD_WaitReady(void){ u8 r1; u16 retry; retry = 0; do { r1 = SPI_FLASH_SendByte(0xFF); if(retry==0xfffe) { return 1; } }while(r1!=0xFF); return 0;} /******************************************************************************** Function Name : SD_SendCommand* Description : 向SD卡發送一個命令* Input : u8 cmd 命令 * u32 arg 命令參數* u8 crc crc校驗值* Output : None* Return : u8 r1 SD卡返回的響應*******************************************************************************/u8 SD_SendCommand(u8 cmd, u32 arg, u8 crc){ unsigned char r1; unsigned char Retry = 0; //???????? SPI_FLASH_SendByte(0xff); //片選端置低,選中SD卡 FLASH_SPI_CS_ENABLE(); //發送 SPI_FLASH_SendByte(cmd | 0x40); //分別寫入命令 SPI_FLASH_SendByte(arg >> 24); SPI_FLASH_SendByte(arg >> 16); SPI_FLASH_SendByte(arg >> 8); SPI_FLASH_SendByte(arg); SPI_FLASH_SendByte(crc); //等待響應,或超時退出 while((r1 = SPI_FLASH_SendByte(0xFF))==0xFF) { Retry++; if(Retry > 200) { break; } } //關閉片選 FLASH_SPI_CS_DISABLE(); //在總線上額外增加8個時鐘,讓SD卡完成剩下的工作 SPI_FLASH_SendByte(0xFF); //返回狀態值 return r1;} /******************************************************************************** Function Name : SD_SendCommand_NoDeassert* Description : 向SD卡發送一個命令(結束是不失能片選,還有後續數據傳來)* Input : u8 cmd 命令 * u32 arg 命令參數* u8 crc crc校驗值* Output : None* Return : u8 r1 SD卡返回的響應*******************************************************************************/u8 SD_SendCommand_NoDeassert(u8 cmd, u32 arg, u8 crc){ unsigned char r1; unsigned char Retry = 0; //???????? SPI_FLASH_SendByte(0xff); //片選端置低,選中SD卡 FLASH_SPI_CS_ENABLE(); //發送 SPI_FLASH_SendByte(cmd | 0x40); //分別寫入命令 SPI_FLASH_SendByte(arg >> 24); SPI_FLASH_SendByte(arg >> 16); SPI_FLASH_SendByte(arg >> 8); SPI_FLASH_SendByte(arg); SPI_FLASH_SendByte(crc); //等待響應,或超時退出 while((r1 = SPI_FLASH_SendByte(0xFF))==0xFF) { Retry++; if(Retry > 200) { break; } } //返回響應值 return r1;}void SPI_SetSpeed(u8 SpeedSet){hspi1.Instance = SPI1; hspi1.Init.Mode = SPI_MODE_MASTER; hspi1.Init.Direction = SPI_DIRECTION_2LINES; hspi1.Init.DataSize = SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; hspi1.Init.NSS = SPI_NSS_HARD_OUTPUT;//如果速度設置輸入0,則低速模式,非0則高速模式if(SpeedSet==SPI_SPEED_LOW){ hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;}else{hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;} hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi1.Init.TIMode = SPI_TIMODE_DISABLE; hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; hspi1.Init.CRCPolynomial = 10; if (HAL_SPI_Init(&hspi1) != HAL_OK) { Error_Handler(); }// if(SpeedSet==SPI_SPEED_LOW)//{// hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;//}//else//{//hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;//}//HAL_SPI_Init(&hspi1);//moon.mp3: 4707774 Byte size 目標文件 設為buffer[512] //speed:實驗測試數據,最大速度 392314 Byte/S,//Prescaler_128, 59592 Byte/S//Prescaler_64, 104617 Byte/S//Prescaler_32, 168134 Byte/S 162337 Byte/S//Prescaler_16, 261543 Byte/S 247777 Byte/S//Prescaler_8, 313851 Byte/S 336269 Byte/S//Prescaler_4, 392314 Byte/S 392314 Byte/S//Prescaler_2, 392314 Byte/S}/******************************************************************************** Function Name : SD_Init* Description : 初始化SD卡* Input : None* Output : None* Return : u8 * 0:NO_ERR* 1:TIME_OUT* 99:NO_CARD*******************************************************************************/u8 SD_Init(void){ u16 i; // 用來循環計數 u8 r1; // 存放SD卡的返回值 u16 retry; // 用來進行超時計數 u8 buff[6]; //如果沒有檢測到卡插入,直接退出,返回錯誤標誌// if(!SD_DET())// {// //return 99; // return STA_NODISK; // FatFS錯誤標誌:沒有插入磁盤// } //SD卡上電 //SD_PWR_ON(); // 純延時,等待SD卡上電完成 //for(i=0;i<0xf00;i++); /******************************************************* //這個地方要加一句,設置SPI速度為低速。 //為什麼有的卡可以呢?因為SPI初始化時是低速的,SD卡初始化 //完成後設置為高速,有的卡只要初始化一次就行,程序就ok; //但有的卡需要多次復位,呵呵,這個地方差這一句, //這種卡就用不成咯! *******************************************************/ SPI_SetSpeed(0); //設置SPI速度為低速 //先產生>74個脈衝,讓SD卡自己初始化完成 for(i=0;i<100;i++) { SPI_FLASH_SendByte(0xFF); } //-----------------SD卡復位到idle開始----------------- //循環連續發送CMD0,直到SD卡返回0x01,進入IDLE狀態 //超時則直接退出 retry = 0; do { //發送CMD0,讓SD卡進入IDLE狀態 r1 = SD_SendCommand(CMD0, 0, 0x95); retry++; }while((r1 != 0x01) && (retry<200)); //跳出循環後,檢查原因:初始化成功?or 重試超時? if(retry==200) { return 1; //超時返回1 } //-----------------SD卡復位到idle結束----------------- //獲取卡片的SD版本信息 r1 = SD_SendCommand_NoDeassert(8, 0x1aa, 0x87); //如果卡片版本信息是v1.0版本的,即r1=0x05,則進行以下初始化 if(r1 == 0x05) {printf("\\r\\n SD卡版本信息:V1.0 \\r\\n"); //設置卡類型為SDV1.0,如果後面檢測到為MMC卡,再修改為MMC SD_Type = SD_TYPE_V1; //如果是V1.0卡,CMD8指令後沒有後續數據 //片選置高,結束本次命令 FLASH_SPI_CS_DISABLE(); //多發8個CLK,讓SD結束後續操作 SPI_FLASH_SendByte(0xFF); //-----------------SD卡、MMC卡初始化開始----------------- //髮卡初始化指令CMD55+ACMD41 // 如果有應答,說明是SD卡,且初始化完成 // 沒有回應,說明是MMC卡,額外進行相應初始化 retry = 0; do { //先發CMD55,應返回0x01;否則出錯 r1 = SD_SendCommand(CMD55, 0, 0); if(r1 != 0x01) { return r1; } //得到正確響應後,發ACMD41,應得到返回值0x00,否則重試200次 r1 = SD_SendCommand(ACMD41, 0, 0); retry++; }while((r1!=0x00) && (retry<400)); // 判斷是超時還是得到正確回應 // 若有回應:是SD卡;沒有回應:是MMC卡 //----------MMC卡額外初始化操作開始------------ if(retry==400) {printf("\\r\\n SD卡信息: MMC卡 \\r\\n"); retry = 0; //發送MMC卡初始化命令(沒有測試) do { r1 = SD_SendCommand(1, 0, 0); retry++; }while((r1!=0x00)&& (retry<400)); if(retry==400) { return 1; //MMC卡初始化超時 } //寫入卡類型 SD_Type = SD_TYPE_MMC; }else{printf("\\r\\n SD卡信息: SD卡 \\r\\n");} //----------MMC卡額外初始化操作結束------------ //設置SPI為高速模式 SPI_SetSpeed(1); SPI_FLASH_SendByte(0xFF); //禁止CRC校驗 /*r1 = SD_SendCommand(CMD59, 0, 0x01); if(r1 != 0x00) { return r1; //命令錯誤,返回r1 } */ //設置Sector Size r1 = SD_SendCommand(CMD16, 512, 0xff); if(r1 != 0x00) { return r1; //命令錯誤,返回r1 } //-----------------SD卡、MMC卡初始化結束----------------- }//SD卡為V1.0版本的初始化結束 //下面是V2.0卡的初始化 //其中需要讀取OCR數據,判斷是SD2.0還是SD2.0HC卡 else if(r1 == 0x01) {printf("\\r\\n SD卡版本信息:V2.0 \\r\\n"); //V2.0的卡,CMD8命令後會傳回4字節的數據,要跳過再結束本命令 buff[0] = SPI_FLASH_SendByte(0xFF); //should be 0x00 buff[1] = SPI_FLASH_SendByte(0xFF); //should be 0x00 buff[2] = SPI_FLASH_SendByte(0xFF); //should be 0x01 buff[3] = SPI_FLASH_SendByte(0xFF); //should be 0xAA FLASH_SPI_CS_DISABLE(); //the next 8 clocks SPI_FLASH_SendByte(0xFF); //判斷該卡是否支持2.7V-3.6V的電壓範圍 if(buff[2]==0x01 && buff[3]==0xAA) { //支持電壓範圍,可以操作 retry = 0; //髮卡初始化指令CMD55+ACMD41 do { r1 = SD_SendCommand(CMD55, 0, 0); if(r1!=0x01) { return r1; } r1 = SD_SendCommand(ACMD41, 0x40000000, 0);if(retry>200) {return r1; //超時則返回r1狀態}}while(r1!=0); //初始化指令發送完成,接下來獲取OCR信息 //-----------鑑別SD2.0卡版本開始-----------r1 = SD_SendCommand_NoDeassert(CMD58, 0, 0);if(r1!=0x00){return r1; //如果命令沒有返回正確應答,直接退出,返回應答}//讀OCR指令發出後,緊接著是4字節的OCR信息buff[0] = SPI_FLASH_SendByte(0xFF);buff[1] = SPI_FLASH_SendByte(0xFF); buff[2] = SPI_FLASH_SendByte(0xFF);buff[3] = SPI_FLASH_SendByte(0xFF);//OCR接收完成,片選置高FLASH_SPI_CS_DISABLE();SPI_FLASH_SendByte(0xFF); //檢查接收到的OCR中的bit30位(CCS),確定其為SD2.0還是SDHC//如果CCS=1:SDHC CCS=0:SD2.0if(buff[0]&0x40) //檢查CCS{SD_Type = SD_TYPE_V2HC;printf("\\r\\n SD卡信息: SDHC \\r\\n");}else{SD_Type = SD_TYPE_V2;printf("\\r\\n SD卡信息: SD2.0 \\r\\n");}//-----------鑑別SD2.0卡版本結束----------- //設置SPI為高速模式SPI_SetSpeed(1); } } return r1;} /******************************************************************************** Function Name : SD_ReceiveData* Description : 從SD卡中讀回指定長度的數據,放置在給定位置* Input : u8 *data(存放讀回數據的內存>len)* u16 len(數據長度)* u8 release(傳輸完成後是否釋放總線CS置高 0:不釋放 1:釋放)* Output : None* Return : u8 * 0:NO_ERR* other:錯誤信息*******************************************************************************/u8 SD_ReceiveData(u8 *data, u16 len, u8 release){ u16 retry; u8 r1; // 啟動一次傳輸 FLASH_SPI_CS_ENABLE(); //等待SD卡發回數據起始令牌0xFE retry = 0; do { r1 = SPI_FLASH_SendByte(0xFF); retry++; if(retry>2000) //2000次等待後沒有應答,退出報錯 { FLASH_SPI_CS_DISABLE(); return 1; } }while(r1 != 0xFE); //開始接收數據 while(len--) { *data = SPI_FLASH_SendByte(0xFF); data++; } //下面是2個偽CRC(dummy CRC) SPI_FLASH_SendByte(0xFF); SPI_FLASH_SendByte(0xFF); //按需釋放總線,將CS置高 if(release == RELEASE) { //傳輸結束 FLASH_SPI_CS_DISABLE(); SPI_FLASH_SendByte(0xFF); } return 0;} /******************************************************************************** Function Name : SD_GetCID* Description : 獲取SD卡的CID信息,包括製造商信息* Input : u8 *cid_data(存放CID的內存,至少16Byte)* Output : None* Return : u8 * 0:NO_ERR* 1:TIME_OUT* other:錯誤信息*******************************************************************************/u8 SD_GetCID(u8 *cid_data){ u8 r1; //發CMD10命令,讀CID r1 = SD_SendCommand(CMD10, 0, 0xFF); if(r1 != 0x00) { return r1; //沒返回正確應答,則退出,報錯 } //接收16個字節的數據 SD_ReceiveData(cid_data, 16, RELEASE); return 0;} /******************************************************************************** Function Name : SD_GetCSD* Description : 獲取SD卡的CSD信息,包括容量和速度信息* Input : u8 *cid_data(存放CID的內存,至少16Byte)* Output : None* Return : u8 * 0:NO_ERR* 1:TIME_OUT* other:錯誤信息*******************************************************************************/u8 SD_GetCSD(u8 *csd_data){ u8 r1; //發CMD9命令,讀CSD r1 = SD_SendCommand(CMD9, 0, 0xFF); if(r1 != 0x00) { return r1; //沒返回正確應答,則退出,報錯 } //接收16個字節的數據 SD_ReceiveData(csd_data, 16, RELEASE); return 0;} /******************************************************************************** Function Name : SD_GetCapacity* Description : 獲取SD卡的容量* Input : None* Output : None* Return : u32 capacity * 0: 取容量出錯 *******************************************************************************/u32 SD_GetCapacity(void){ u8 csd[16]; u32 Capacity; u8 r1; u16 i;u16 temp; //取CSD信息,如果期間出錯,返回0 if(SD_GetCSD(csd)!=0) { return 0; } //如果為SDHC卡,按照下面方式計算 if((csd[0]&0xC0)==0x40) { Capacity = ((((u32)csd[8])<<8) + (u32)csd[9] + 1)*(u32)1024; } else { //下面代碼為網上版本 ////////////formula of the capacity/////////////// // // memory capacity = BLOCKNR * BLOCK_LEN // //BLOCKNR = (C_SIZE + 1)* MULT // // C_SIZE_MULT+2 //MULT = 2 // // READ_BL_LEN //BLOCK_LEN = 2 /**********************************************/ //C_SIZE i = csd[6]&0x03; i<<=8; i += csd[7]; i<<=2; i += ((csd[8]&0xc0)>>6); //C_SIZE_MULT r1 = csd[9]&0x03; r1<<=1; r1 += ((csd[10]&0x80)>>7); //BLOCKNR r1+=2; temp = 1; while(r1) { temp*=2; r1--; } Capacity = ((u32)(i+1))*((u32)temp); // READ_BL_LEN i = csd[5]&0x0f; //BLOCK_LEN temp = 1; while(i) { temp*=2; i--; } //The final result Capacity *= (u32)temp; //Capacity /= 512; } return (u32)Capacity;} /******************************************************************************** Function Name : SD_ReadSingleBlock* Description : 讀SD卡的一個block* Input : u32 sector 取地址(sector值,非物理地址) * u8 *buffer 數據存儲地址(大小至少512byte) * Output : None* Return : u8 r1 * 0: 成功* other:失敗*******************************************************************************/u8 SD_ReadSingleBlock(u32 sector, u8 *buffer){u8 r1; //設置為高速模式SPI_SetSpeed(SPI_SPEED_HIGH);//如果不是SDHC,將sector地址轉成byte地址//sector = sector<<9; r1 = SD_SendCommand(CMD17, sector, 0);//讀命令 if(r1 != 0x00){return r1;}r1 = SD_ReceiveData(buffer, 512, RELEASE);if(r1 != 0){return r1; //讀數據出錯!}else{return 0;}} /******************************************************************************** Function Name : SD_WriteSingleBlock* Description : 寫入SD卡的一個block* Input : u32 sector 扇區地址(sector值,非物理地址) * u8 *buffer 數據存儲地址(大小至少512byte) * Output : None* Return : u8 r1 * 0: 成功* other:失敗*******************************************************************************/u8 SD_WriteSingleBlock(u32 sector, const u8 *data){ u8 r1; u16 i; u16 retry; //設置為高速模式 SPI_SetSpeed(SPI_SPEED_HIGH); //如果不是SDHC,給定的是sector地址,將其轉換成byte地址// if(SD_Type!=SD_TYPE_V2HC)// {// sector = sector<<9;// } r1 = SD_SendCommand(CMD24, sector, 0x00); if(r1 != 0x00) { return r1; //應答不正確,直接返回 } //開始準備數據傳輸 FLASH_SPI_CS_ENABLE(); //先放3個空數據,等待SD卡準備好 SPI_FLASH_SendByte(0xff); SPI_FLASH_SendByte(0xff); SPI_FLASH_SendByte(0xff); //放起始令牌0xFE SPI_FLASH_SendByte(0xFE); //放一個sector的數據 for(i=0;i<512;i++) { SPI_FLASH_SendByte(*data++); } //發2個Byte的dummy CRC SPI_FLASH_SendByte(0xff); SPI_FLASH_SendByte(0xff); //等待SD卡應答 r1 = SPI_FLASH_SendByte(0xff); if((r1&0x1F)!=0x05) { FLASH_SPI_CS_DISABLE(); return r1; } //等待操作完成 retry = 0; while(!SPI_FLASH_SendByte(0xff)) { retry++; if(retry>0xfffe) //如果長時間寫入沒有完成,報錯退出 { FLASH_SPI_CS_DISABLE(); return 1; //寫入超時返回1 } } //寫入完成,片選置1 FLASH_SPI_CS_DISABLE(); SPI_FLASH_SendByte(0xff); return 0;} /******************************************************************************** Function Name : SD_ReadMultiBlock* Description : 讀SD卡的多個block* Input : u32 sector 取地址(sector值,非物理地址) * u8 *buffer 數據存儲地址(大小至少512byte)* u8 count 連續讀count個block* Output : None* Return : u8 r1 * 0: 成功* other:失敗*******************************************************************************/u8 SD_ReadMultiBlock(u32 sector, u8 *buffer, u8 count){ u8 r1; //設置為高速模式 SPI_SetSpeed(SPI_SPEED_HIGH); //如果不是SDHC,將sector地址轉成byte地址// sector = sector<<9; //SD_WaitReady(); //發讀多塊命令r1 = SD_SendCommand(CMD18, sector, 0);//讀命令if(r1 != 0x00) { return r1; } //開始接收數據 do { if(SD_ReceiveData(buffer, 512, NO_RELEASE) != 0x00) { break; } buffer += 512; } while(--count); //全部傳輸完畢,發送停止命令 SD_SendCommand(CMD12, 0, 0); //釋放總線 FLASH_SPI_CS_DISABLE(); SPI_FLASH_SendByte(0xFF); if(count != 0) { return count; //如果沒有傳完,返回剩餘個數 } else { return 0; }} /******************************************************************************** Function Name : SD_WriteMultiBlock* Description : 寫入SD卡的N個block* Input : u32 sector 扇區地址(sector值,非物理地址) * u8 *buffer 數據存儲地址(大小至少512byte)* u8 count 寫入的block數目* Output : None* Return : u8 r1 * 0: 成功* other:失敗*******************************************************************************/u8 SD_WriteMultiBlock(u32 sector, const u8 *data, u8 count){ u8 r1; u16 i; //設置為高速模式 SPI_SetSpeed(SPI_SPEED_HIGH); //如果不是SDHC,給定的是sector地址,將其轉換成byte地址// if(SD_Type != SD_TYPE_V2HC)// {// sector = sector<<9;// } //如果目標卡不是MMC卡,啟用ACMD23指令使能預擦除 if(SD_Type != SD_TYPE_MMC) { r1 = SD_SendCommand(ACMD23, count, 0x00); } //發多塊寫入指令 r1 = SD_SendCommand(CMD25, sector, 0x00); if(r1 != 0x00) { return r1; //應答不正確,直接返回 } //開始準備數據傳輸 FLASH_SPI_CS_ENABLE(); //先放3個空數據,等待SD卡準備好 SPI_FLASH_SendByte(0xff); SPI_FLASH_SendByte(0xff); //--------下面是N個sector寫入的循環部分 do { //放起始令牌0xFC 表明是多塊寫入 SPI_FLASH_SendByte(0xFC); //放一個sector的數據 for(i=0;i<512;i++) { SPI_FLASH_SendByte(*data++); } //發2個Byte的dummy CRC SPI_FLASH_SendByte(0xff); SPI_FLASH_SendByte(0xff); //等待SD卡應答 r1 = SPI_FLASH_SendByte(0xff); if((r1&0x1F)!=0x05) { FLASH_SPI_CS_DISABLE(); //如果應答為報錯,則帶錯誤代碼直接退出 return r1; } //等待SD卡寫入完成 if(SD_WaitReady()==1) { FLASH_SPI_CS_DISABLE(); //等待SD卡寫入完成超時,直接退出報錯 return 1; } //本sector數據傳輸完成 }while(--count); //髮結束傳輸令牌0xFD r1 = SPI_FLASH_SendByte(0xFD); if(r1==0x00) { count = 0xfe; } if(SD_WaitReady()) { while(1) { } } //寫入完成,片選置1 FLASH_SPI_CS_DISABLE(); SPI_FLASH_SendByte(0xff); return count; //返回count值,如果寫完則count=0,否則count=1} /* USER CODE END 1 */

SDCard.h 根據YS-F1Pro SPI Flash 改寫 SD

/* Define to prevent recursive inclusion -------------------------------------*/#ifndef __SDCard_H#define __SDCard_H#ifdef __cplusplus extern "C" {#endif /* Includes ------------------------------------------------------------------*/#include "main.h" /* USER CODE BEGIN Includes *//* 包含頭文件 ----------------------------------------------------------------*/#include "stm32f1xx_hal.h"/* USER CODE END Includes */ /* 類型定義 ------------------------------------------------------------------*//* 宏定義 --------------------------------------------------------------------*///#define SPI_FLASH_ID 0xEF3015 //W25X16 2MB//#define SPI_FLASH_ID 0xEF4015 //W25Q16 4MB//#define SPI_FLASH_ID 0XEF4017 //W25Q64 8MB#define SPI_FLASH_ID 0XEF4018 //W25Q128 16MB YS-F1Pro開發默認使用 #define FLASH_SPIx SPI1#define FLASH_SPIx_RCC_CLK_ENABLE() __HAL_RCC_SPI1_CLK_ENABLE()#define FLASH_SPIx_RCC_CLK_DISABLE() __HAL_RCC_SPI1_CLK_DISABLE() #define FLASH_SPI_GPIO_ClK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() #define FLASH_SPI_GPIO_PORT GPIOA#define FLASH_SPI_SCK_PIN GPIO_PIN_5#define FLASH_SPI_MISO_PIN GPIO_PIN_6#define FLASH_SPI_MOSI_PIN GPIO_PIN_7 #define FLASH_SPI_CS_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() #define FLASH_SPI_CS_PORT GPIOA#define FLASH_SPI_CS_PIN GPIO_PIN_4 #define FLASH_SPI_CS_ENABLE() HAL_GPIO_WritePin(FLASH_SPI_CS_PORT, FLASH_SPI_CS_PIN, GPIO_PIN_RESET)#define FLASH_SPI_CS_DISABLE() HAL_GPIO_WritePin(FLASH_SPI_CS_PORT, FLASH_SPI_CS_PIN, GPIO_PIN_SET) /* 擴展變量 ------------------------------------------------------------------*/extern SPI_HandleTypeDef hspiflash; /* 函數聲明 ------------------------------------------------------------------*/ /* USER CODE BEGIN Private defines */ /* USER CODE END Private defines */#define u8 uint8_t#define u16 uint16_t#define u32 uint32_t /* Private define ------------------------------------------------------------*//* SD卡類型定義 */#define SD_TYPE_MMC 0#define SD_TYPE_V1 1#define SD_TYPE_V2 2#define SD_TYPE_V2HC 4 /* SPI總線速度設置*/#define SPI_SPEED_LOW 0#define SPI_SPEED_HIGH 1 /* SD傳輸數據結束後是否釋放總線宏定義 */#define NO_RELEASE 0#define RELEASE 1 /* SD卡指令表 */#define CMD0 0 //卡復位#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 ACMD23 23 //命令23,設置多sector寫入前預先擦除N個block#define CMD24 24 //命令24,寫sector#define CMD25 25 //命令25,寫Multi sector#define ACMD41 41 //命令41,應返回0x00#define CMD55 55 //命令55,應返回0x01#define CMD58 58 //命令58,讀OCR信息#define CMD59 59 //命令59,使能/禁止CRC,應返回0x00 /* Private macro -------------------------------------------------------------*///SD卡CS片選使能端操作:#define SD_CS_ENABLE() GPIO_ResetBits(GPIOA,GPIO_PIN_4) //選中SD卡#define SD_CS_DISABLE() GPIO_SetBits(GPIOA,GPIO_PIN_4) //不選中SD卡//#define SD_PWR_ON() GPIO_ResetBits(GPIOD,GPIO_Pin_10) //SD卡上電//#define SD_PWR_OFF() GPIO_SetBits(GPIOD,GPIO_Pin_10) //SD卡斷電//#define SD_DET() !GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2) //檢測有卡 //1-有 0-無 /* Private function prototypes -----------------------------------------------*/void SPI_Configuration(void);void SPI_SetSpeed(u8 SpeedSet); u8 SPI_ReadWriteByte(u8 TxData); //SPI總線讀寫一個字節u8 SD_WaitReady(void); //等待SD卡就緒u8 SD_SendCommand(u8 cmd, u32 arg, u8 crc); //SD卡發送一個命令u8 SD_SendCommand_NoDeassert(u8 cmd, u32 arg, u8 crc);u8 SD_Init(void); //SD卡初始化 //u8 SD_ReceiveData(u8 *data, u16 len, u8 release);//SD卡讀數據u8 SD_GetCID(u8 *cid_data); //讀SD卡CIDu8 SD_GetCSD(u8 *csd_data); //讀SD卡CSDu32 SD_GetCapacity(void); //取SD卡容量 u8 SD_ReadSingleBlock(u32 sector, u8 *buffer); //讀一個sectoru8 SD_WriteSingleBlock(u32 sector, const u8 *buffer); //寫一個sectoru8 SD_ReadMultiBlock(u32 sector, u8 *buffer, u8 count); //讀多個sectoru8 SD_WriteMultiBlock(u32 sector, const u8 *data, u8 count); //寫多個sector/* USER CODE BEGIN Prototypes */ extern u8 SD_Init(void); /* USER CODE END Prototypes */ #ifdef __cplusplus}#endif#endif /*__ usart_H */

2、添加FATFS文件系統


3、修改FATFS文件系統SD卡驅動部分sdcard_diskio.c

/* 包含頭文件 ----------------------------------------------------------------*/#include "main.h"#include <string.h>#include "SDCard.h"#include "ff_gen_drv.h" /* 私有類型定義 --------------------------------------------------------------*//* 私有宏定義 ----------------------------------------------------------------*/#define BLOCK_SIZE 512 /* 私有變量 ------------------------------------------------------------------*/static volatile DSTATUS Stat = STA_NOINIT; /* 擴展變量 ------------------------------------------------------------------*//* 私有函數原形 --------------------------------------------------------------*/DSTATUS SD_initialize (BYTE);DSTATUS SD_status (BYTE);DRESULT SD_read (BYTE, BYTE*, DWORD, UINT); #if _USE_WRITE == 1 // 如果允許寫操作 DRESULT SD_write (BYTE, const BYTE*, DWORD, UINT);#endif /* _USE_WRITE == 1 */ #if _USE_IOCTL == 1 // 如果輸入輸出操作控制 DRESULT SD_ioctl (BYTE, BYTE, void*);#endif /* _USE_IOCTL == 1 */ /* 定義SD卡接口函數 */const Diskio_drvTypeDef SD_Driver ={ SD_initialize, // SD卡初始化 SD_status, // SD卡狀態獲取 SD_read, // SD卡讀數據#if _USE_WRITE == 1 SD_write, // SD卡寫數據#endif /* _USE_WRITE == 1 */ #if _USE_IOCTL == 1 SD_ioctl, // 獲取SD卡信息#endif /* _USE_IOCTL == 1 */}; /* 函數體 --------------------------------------------------------------------*//** * 函數功能: SD卡初始化配置 * 輸入參數: 無 * 返 回 值: 無 * 說 明: 無 */ //extern void MX_SPI1_Init(void);DSTATUS SD_initialize(BYTE lun){ Stat = STA_NOINIT; /* 初始化SDIO外設 */// MX_SPI1_Init(); /* 獲取SD卡狀態 */int result;result = SD_Init();// if(HAL_SD_GetStatus(&hsdcard)==SD_TRANSFER_OK)// {// Stat &= ~STA_NOINIT;// }if (result == 0) { Stat = RES_OK; } else { Stat = RES_ERROR; } return Stat;} /** * 函數功能: SD卡狀態獲取 * 輸入參數: lun : 不用 * 返 回 值: DSTATUS:SD卡狀態返回值 * 說 明: 無 */DSTATUS SD_status(BYTE lun){ Stat = STA_NOINIT; // if(HAL_SD_GetStatus(&hsdcard) == SD_TRANSFER_OK)// {// Stat &= ~STA_NOINIT;// }Stat = RES_OK; return Stat;} /** * 函數功能: 從SD卡讀取數據到緩衝區 * 輸入參數: lun : 不用 * buff:存放讀取到數據緩衝區指針 * sector:扇區地址(LBA) * count:扇區數目 * 返 回 值: DSTATUS:操作結果 * 說 明: SD卡讀操作使用DMA傳輸 */DRESULT SD_read(BYTE lun, BYTE *buff, DWORD sector, UINT count){ DRESULT res = RES_OK;// if((DWORD)buff&3)// {// DWORD scratch[BLOCK_SIZE/4];// while (count--) // {// res = SD_read(lun,(void *)scratch, sector++, 1);// if (res != RES_OK) // {// break;// }// memcpy(buff, scratch, BLOCK_SIZE);// buff += BLOCK_SIZE;// }// return res;// }// // if(HAL_SD_ReadBlocks_DMA(&hsdcard,(uint32_t*)buff,(uint64_t)(sector * BLOCK_SIZE),BLOCK_SIZE,count) != SD_OK)// {// res = RES_ERROR;// }// if(res==RES_OK)// {// if(HAL_SD_CheckReadOperation(&hsdcard, 0xFFFFFFFF) != SD_OK)// {// res = RES_ERROR;// }// }int result;if(count==1) { result=SD_ReadSingleBlock(sector,buff); } else { result = SD_ReadMultiBlock(sector, buff, count); } if (result == 0) { res = RES_OK; } else { res = RES_ERROR; } return res;} /** * 函數功能: 將緩衝區數據寫入到SD卡內 * 輸入參數: lun : 不用 * buff:存放待寫入數據的緩衝區指針 * sector:扇區地址(LBA) * count:扇區數目 * 返 回 值: DSTATUS:操作結果 * 說 明: SD卡寫操作沒有使用DMA傳輸 */#if _USE_WRITE == 1DRESULT SD_write(BYTE lun, const BYTE *buff, DWORD sector, UINT count){ DRESULT res = RES_OK;// if((DWORD)buff&3)// {// DRESULT res = RES_OK;// DWORD scratch[BLOCK_SIZE / 4]; // while (count--) // {// memcpy( scratch,buff,BLOCK_SIZE);// res = SD_write(lun,(void *)scratch, sector++, 1);// if (res != RES_OK) // {// break;// }// buff += BLOCK_SIZE;// }// return res;// }// // if(HAL_SD_WriteBlocks(&hsdcard,(uint32_t*)buff,(uint64_t)(sector * BLOCK_SIZE),BLOCK_SIZE, count) != SD_OK)// {// res = RES_ERROR;// } int result; if(count==1) { result=SD_WriteSingleBlock(sector, buff); } else { result = SD_WriteMultiBlock(sector, buff, count); } if (result == 0) { res = RES_OK; } else { res = RES_ERROR; } return res;}#endif /* _USE_WRITE == 1 */ /** * 函數功能: 輸入輸出控制操作(I/O control operation) * 輸入參數: lun : 不用 * cmd:控制命令 * buff:存放待寫入或者讀取數據的緩衝區指針 * 返 回 值: DSTATUS:操作結果 * 說 明: 無 */#if _USE_IOCTL == 1DRESULT SD_ioctl(BYTE lun, BYTE cmd, void *buff){ DRESULT res = RES_ERROR; // if (Stat & STA_NOINIT) return RES_NOTRDY;// // switch (cmd)// {// /* Make sure that no pending write process */// case CTRL_SYNC ://FLASH_SPI_CS_ENABLE();//if(SD_WaitReady()==0)//{//res = RES_OK;//}//else//{//res = RES_ERROR;//}////FLASH_SPI_CSDISABLE();// break;// // /* 獲取SD卡總扇區數目(DWORD) */// case GET_SECTOR_COUNT ://*(WORD*)buff = 512;//res = RES_OK;// break;// // /* 獲取讀寫扇區大小(WORD) */// case GET_SECTOR_SIZE ://*(DWORD*)buff = SD_GetCapacity();//res = RES_OK;// break;// // /* 獲取擦除塊大小(DWORD) */// case GET_BLOCK_SIZE :// *(DWORD*)buff = BLOCK_SIZE;// break;// // default:// res = RES_PARERR;// } uint8_t CSD[16] = {0};uint8_t csddata[16] = {0};uint32_t csize;uint32_t Capacity; switch (cmd) { case CTRL_SYNC: res = RES_OK; break; case GET_SECTOR_COUNT:SD_GetCID(CSD);SD_GetCSD(csddata);//SDGetCIDCSD(CSD, csddata); csize = csddata[9] + ((uint32_t)csddata[8] << 8) + ((uint32_t)(csddata[7] & 0x3f) << 16) + 1; Capacity = csize << 9; *((DWORD *)buff) = Capacity; res = RES_OK; break; case GET_SECTOR_SIZE: *(WORD *)buff = 512; //spi flash的扇區大小是 512 Bytes return RES_OK; case GET_BLOCK_SIZE: *((DWORD *)buff) = 4096; res = RES_OK; break; default: res = RES_PARERR; break; } return res;} #endif /* _USE_IOCTL == 1 */

4、在Main.c文件中添加 測試應用程序

char SPIFLASHPath[4]; /* 串行Flash邏輯設備路徑 */char SDPath[4]; /* SD卡邏輯設備路徑 */ FATFS fs;/* FatFs文件系統對象 */FIL file;/* 文件對象 */FRESULT f_res; /* 文件操作結果 */UINT fnum; /* 文件成功讀寫數量 */BYTE ReadBuffer[1024]={0}; /* 讀緩衝區 */BYTE WriteBuffer[]= "歡迎使用硬石STM32開發板 今天是個好日子,新建文件系統測試文件\\n";/* 寫緩衝區*/ extern Diskio_drvTypeDef SD_Driver;

添加文件系統操作結果處理

/** * 函數功能: FatFS文件系統操作結果信息處理. * 輸入參數: FatFS文件系統操作結果:FRESULT * 返 回 值: 無 * 說 明: 無 */static void printf_fatfs_error(FRESULT fresult){ switch(fresult) { case FR_OK: //(0) printf("》操作成功。\\n"); break; case FR_DISK_ERR: //(1) printf("!!硬件輸入輸出驅動出錯。\\n"); break; case FR_INT_ERR: //(2) printf("!!斷言錯誤。\\n"); break; case FR_NOT_READY: //(3) printf("!!物理設備無法工作。\\n"); break; case FR_NO_FILE: //(4) printf("!!無法找到文件。\\n"); break; case FR_NO_PATH: //(5) printf("!!無法找到路徑。\\n"); break; case FR_INVALID_NAME: //(6) printf("!!無效的路徑名。\\n"); break; case FR_DENIED: //(7) case FR_EXIST: //(8) printf("!!拒絕訪問。\\n"); break; case FR_INVALID_OBJECT: //(9) printf("!!無效的文件或路徑。\\n"); break; case FR_WRITE_PROTECTED: //(10) printf("!!邏輯設備寫保護。\\n"); break; case FR_INVALID_DRIVE: //(11) printf("!!無效的邏輯設備。\\n"); break; case FR_NOT_ENABLED: //(12) printf("!!無效的工作區。\\n"); break; case FR_NO_FILESYSTEM: //(13) printf("!!無效的文件系統。\\n"); break; case FR_MKFS_ABORTED: //(14) printf("!!因函數參數問題導致f_mkfs函數操作失敗。\\n"); break; case FR_TIMEOUT: //(15) printf("!!操作超時。\\n"); break; case FR_LOCKED: //(16) printf("!!文件被保護。\\n"); break; case FR_NOT_ENOUGH_CORE: //(17) printf("!!長文件名支持獲取堆空間失敗。\\n"); break; case FR_TOO_MANY_OPEN_FILES: //(18) printf("!!打開太多文件。\\n"); break; case FR_INVALID_PARAMETER: // (19) printf("!!參數無效。\\n"); break; }}

添加測試程序:

/* 註冊一個FatFS設備:SD卡 */ if(FATFS_LinkDriver(&SD_Driver, SDPath) == 0) { //在SD卡掛載文件系統,文件系統掛載時會對SD卡初始化 f_res = f_mount(&fs,(TCHAR const*)SDPath,1); printf_fatfs_error(f_res); /*----------------------- 格式化測試 ---------------------------*/ /* 如果沒有文件系統就格式化創建創建文件系統 */ if(f_res == FR_NO_FILESYSTEM) { printf("》SD卡還沒有文件系統,即將進行格式化...\\n"); /* 格式化 */ f_res=f_mkfs((TCHAR const*)SDPath,0,0); if(f_res == FR_OK) { printf("》SD卡已成功格式化文件系統。\\n"); /* 格式化後,先取消掛載 */ f_res = f_mount(NULL,(TCHAR const*)SDPath,1); /* 重新掛載*/ f_res = f_mount(&fs,(TCHAR const*)SDPath,1); } else { printf("《《格式化失敗。》》\\n"); while(1); } } else if(f_res!=FR_OK) { printf("!!SD卡掛載文件系統失敗。(%d)\\n",f_res); printf_fatfs_error(f_res); while(1); } else { printf("》文件系統掛載成功,可以進行讀寫測試\\n"); } /*----------------------- 文件系統測試:寫測試 -----------------------------*/ /* 打開文件,如果文件不存在則創建它 */ printf("****** 即將進行文件寫入測試... ******\\n"); f_res = f_open(&file, "FatFs讀寫測試文件.txt",FA_CREATE_ALWAYS | FA_WRITE ); if ( f_res == FR_OK ) { printf("》打開/創建FatFs讀寫測試文件.txt文件成功,向文件寫入數據。\\n"); /* 將指定存儲區內容寫入到文件內 */ f_res=f_write(&file,WriteBuffer,sizeof(WriteBuffer),&fnum); if(f_res==FR_OK) { printf("》文件寫入成功,寫入字節數據:%d\\n",fnum); printf("》向文件寫入的數據為:\\n%s\\n",WriteBuffer); } else { printf("!!文件寫入失敗:(%d)\\n",f_res); } /* 不再讀寫,關閉文件 */ f_close(&file); } else { printf("!!打開/創建文件失敗。\\n"); } /*------------------- 文件系統測試:讀測試 ------------------------------------*/ printf("****** 即將進行文件讀取測試... ******\\n"); f_res = f_open(&file, "FatFs讀寫測試文件.txt", FA_OPEN_EXISTING | FA_READ); if(f_res == FR_OK) { printf("》打開文件成功。\\n"); f_res = f_read(&file, ReadBuffer, sizeof(ReadBuffer), &fnum); if(f_res==FR_OK) { printf("》文件讀取成功,讀到字節數據:%d\\n",fnum); printf("》讀取得的文件數據為:\\n%s \\n", ReadBuffer); } else { printf("!!文件讀取失敗:(%d)\\n",f_res); } } else { printf("!!打開文件失敗。\\n"); } /* 不再讀寫,關閉文件 */ f_close(&file); /* 不再使用,取消掛載 */ f_res = f_mount(NULL,(TCHAR const*)SDPath,1); } /* 註銷一個FatFS設備:SD卡 */ FATFS_UnLinkDriver(SDPath);

3、使用串口查看測試結果