全球观速讯丨STM32F429 Discovery开发板应用:实现SPI-SD Card文件写入(搭载FatFS文件系统)
MCU:STM32F429ZIT6开发环境:STM32CubeMX+MDK5
外购了一个SPI接口的SD Card模块,想要实现SD卡存储数据的功能。
(相关资料图)
首先需要打开STM32CubeMX工具。输入开发板MCU对应型号,找到开发板对应封装的MCU型号,双击打开(图中第三)。
此时,双击完后会关闭此界面,然后打开一个新界面。
然后,我们开始基本配置。
现在我们选择一个LED作为系统LED,该步骤可以忽略,只是本人喜欢这样子。以硬件原理图的LD3为例子。
基本配置除了时钟树外,基本上已经配置好了。
现在配置时钟树
基本配置已经配置完,现在开始配置实验使用的内容。
配置USART1,重定向printf函数作为串口输出。
然后配置SPI1,作为驱动SD Card读写的接口。
然后配置文件系统,可以让文件的使用更方便。
现在配置按键,触发中断处理一些事情。
配置完成,完善工程,生成工程。
到此,STM32CubeMX工具的使用结束!可以发现在桌面已经生成了SDCard_rw工程。
使用MDK5打开SDCard_rw工程打开。点击魔法棒,勾选微库。选择对应的下载器,勾选下载完复位允许。USB线一端接开发板USB_Device,一端接PC。
现在可以开始实验了
在usart.c中重定向printf函数,并在usart.h中声明。
1 //重定向c库函数printf到串口DEBUG_USART,重定向后可使用printf函数2 int fputc(int ch, FILE *f)3 {4 HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000); 5 return (ch);6 }
在sdcard_write工程下创建UserCode文件夹,编写drive_spisd.c和drive_spisd.h。
然后在MDK5这里的SDCard_rw工程添加一个新文件夹UserCode,装入drive_spisd.c。并在魔法棒这里加入头文件路径。
drive_spisd.c如下
1 /* Includes ------------------------------------------------------------------*/ 2 #include "drive_spisd.h" 3 /* Private includes ----------------------------------------------------------*/ 4 #include "spi.h" 5 #include "ff.h" 6 #include "usart.h" 7 /* Private typedef -----------------------------------------------------------*/ 8 9 /* Private define ------------------------------------------------------------*/ 10 11 /* Private macro -------------------------------------------------------------*/ 12 13 /* Private variables ---------------------------------------------------------*/ 14 uint8_t test; 15 uint8_t SD_TYPE = 0x00; 16 MSD_CARDINFO SD0_CardInfo; 17 char SD_FileName[] = "hello.txt"; 18 /* Private function prototypes -----------------------------------------------*/ 19 static int SD_SendCMD(uint8_t cmd, uint32_t arg, uint8_t crc); 20 static uint8_t SD_ReceiveData(uint8_t *data, uint16_t len); 21 static uint8_t SD_SendBlock(uint8_t*buf, uint8_t cmd); 22 /* Private user code ---------------------------------------------------------*/ 23 24 /** 25 * @brief SPI_CS片选 26 * @note None 27 * @retval None 28 */ 29 void SPISD_CS(uint8_t p) 30 { 31 HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, (p==0)?GPIO_PIN_SET:GPIO_PIN_RESET); 32 } 33 34 /** 35 * @brief 发送命令(CMD0~CMD63),发完释放 36 * @note 命令格式:0+传输标志(1-命令、0-响应)+CONTENT(6+32)+7CRC+1 37 * @retval None 38 */ 39 static int SD_SendCMD(uint8_t cmd, uint32_t arg, uint8_t crc) 40 { 41 uint8_t r1,retry; 42 43 SPISD_CS(0); //取消片选 44 HAL_Delay(20); 45 SPISD_CS(1); //选通 46 47 //SD卡的SPI通信协议规定,每个命令操作之前都需要发送至少8个时钟周期 48 do 49 { 50 retry = SPI_ReadWrite(0xFF); 51 }while(retry != 0xFF); 52 53 SPI_ReadWrite(cmd | 0x40); 54 SPI_ReadWrite(arg >> 24); 55 SPI_ReadWrite(arg >> 16); 56 SPI_ReadWrite(arg >> 8); 57 SPI_ReadWrite(arg); 58 SPI_ReadWrite(crc); 59 60 if(cmd == CMD12) 61 SPI_ReadWrite(0xFF); 62 do 63 { 64 r1 = SPI_ReadWrite(0xFF); 65 }while(r1 & 0x80); 66 67 return r1; 68 } 69 70 //SD卡初始化 71 uint8_t SD_Init(void) 72 { 73 uint8_t r1,i; 74 uint8_t buff[6] = {0}; 75 uint16_t retry; 76 77 SPI_SetSpeed(SPI_BAUDRATEPRESCALER_256); 78 SPISD_CS(0); 79 for(retry=0;retry<10;retry++) 80 SPI_ReadWrite(0xFF); 81 82 //SD卡进入IDLE状态 83 do 84 { 85 r1 = SD_SendCMD(CMD0 ,0, 0x95); 86 }while(r1 != 0x01); 87 88 //查看SD卡的类型 89 SD_TYPE = 0; 90 r1 = SD_SendCMD(CMD8, 0x1AA, 0x87); 91 if(r1 == 0x01) 92 { 93 for(i=0;i<4;i++) 94 buff[i] = SPI_ReadWrite(0xFF); //Get trailing return value of R7 resp 95 if( buff[2]==0X01 && buff[3]==0XAA ) //卡是否支持2.7~3.6V 96 { 97 retry = 0XFFFE; 98 do 99 { 100 SD_SendCMD(CMD55, 0, 0X01); //发送CMD55101 r1 = SD_SendCMD(CMD41, 0x40000000, 0X01); //发送CMD41102 }while(r1&&retry--);103 104 if(retry && SD_SendCMD(CMD58, 0, 0X01) == 0) //鉴别SD2.0卡版本开始105 {106 for(i=0;i<4;i++)107 buff[i] = SPI_ReadWrite(0XFF); //得到OCR值108 SD_TYPE = (buff[0]&0x40) ? V2HC:V2; 109 }110 }else111 {112 SD_SendCMD(CMD55, 0, 0X01); //发送CMD55113 r1 = SD_SendCMD(CMD41, 0, 0X01); //发送CMD41114 if(r1<=1)115 { 116 SD_TYPE = V1;117 retry = 0XFFFE;118 do //等待退出IDLE模式119 {120 SD_SendCMD(CMD55, 0, 0X01); //发送CMD55121 r1 = SD_SendCMD(CMD41, 0, 0X01); //发送CMD41122 }while(r1&&retry--);123 }else //MMC卡不支持CMD55+CMD41识别124 {125 SD_TYPE = MMC; //MMC V3126 retry = 0XFFFE;127 do //等待退出IDLE模式128 { 129 r1 = SD_SendCMD(CMD1, 0, 0X01); //发送CMD1130 }while(r1&&retry--); 131 }132 if( retry==0 || SD_SendCMD(CMD16, 512, 0X01)!=0 )133 SD_TYPE = ERR; //错误的卡134 }135 }136 SPISD_CS(0);137 SPI_SetSpeed(SPI_BAUDRATEPRESCALER_4);138 139 return SD_TYPE?0:1;140 }141 142 void FileSystem_Init(void)143 {144 FATFS *fs;145 DWORD fre_clust, AvailableSize, UserSize; 146 uint8_t res;147 uint8_t *work;148 uint16_t TotalSpace;149 150 res = SD_Init(); 151 if(res == 1)152 printf("SD卡初始化失败! \r\n"); 153 154 res = f_mount(&USERFatFS, USERPath, 1); //挂载155 if(res == FR_NO_FILESYSTEM) //没有文件系统,格式化156 {157 printf("没有文件系统! \r\n"); 158 159 work = malloc(_MIN_SS);160 res = f_mkfs(USERPath, FM_FAT, 0, work, _MIN_SS); //格式化sd卡161 free(work);162 163 if(res == FR_OK)164 { 165 res = f_mount(NULL, USERPath, 1); //格式化后先取消挂载166 res = f_mount(&USERFatFS, USERPath, 1); //重新挂载 167 if(res == FR_OK)168 {169 printf("SD卡已经成功挂载,可以进进行文件写入测试! \r\n");170 } 171 }172 else173 {174 printf("格式化失败! \r\n"); 175 }176 }else if(res == FR_OK)177 {178 printf("挂载成功! \r\n"); 179 }else180 {181 printf("挂载失败! (%d)\r\n", res);182 } 183 184 res = f_getfree(USERPath, &fre_clust, &fs); /* 根目录 */185 if ( res == FR_OK ) 186 {187 TotalSpace = (uint16_t)(((fs->n_fatent - 2) * fs->csize ) / 2 /1024);188 AvailableSize = (uint16_t)((fre_clust * fs->csize) / 2 /1024);189 UserSize = TotalSpace - AvailableSize; 190 /* Print free space in unit of MB (assuming 512 bytes/sector) */191 printf("\r\n%d MB total drive space.\r\n%ld MB available.\r\n%ld MB used.\r\n",TotalSpace, AvailableSize, UserSize);192 }193 else 194 {195 printf("Get SDCard Capacity Failed (%d)\r\n", res);196 } 197 // f_mount(NULL, USERPath, 1); //取消挂载198 } 199 200 /**201 * @brief 读取指定长度数据202 * @note None203 * @retval None204 */205 static uint8_t SD_ReceiveData(uint8_t *data, uint16_t len)206 {207 uint8_t r1;208 209 SPISD_CS(1); 210 do211 { 212 r1 = SPI_ReadWrite(0xFF); 213 HAL_Delay(100);214 }while(r1 != 0xFE); 215 216 while(len--)217 {218 *data = SPI_ReadWrite(0xFF);219 data++;220 }221 SPI_ReadWrite(0xFF);222 SPI_ReadWrite(0xFF); 223 224 return 0;225 }226 227 /**228 * @brief 向SD卡写入一个数据包(512字节)的内容229 * @note None230 * @retval None231 */232 static uint8_t SD_SendBlock(uint8_t*buf, uint8_t cmd)233 { 234 uint8_t r1; 235 uint16_t t; 236 237 do{238 r1 = SPI_ReadWrite(0xFF);239 }while(r1!=0xFF);240 241 SPI_ReadWrite(cmd);242 if(cmd != 0XFD) //不是结束指令243 {244 for(t=0; t<512; t++)245 SPI_ReadWrite(buf[t]); //提高速度,减少函数传参时间246 SPI_ReadWrite(0xFF); //忽略crc247 SPI_ReadWrite(0xFF);248 t = SPI_ReadWrite(0xFF); //接收响应249 if( (t&0x1F) != 0x05 )250 return 2; //响应错误 251 }252 253 return 0; //写入成功254 }255 256 /**257 * @brief CSD,卡的操作条件信息,128bit258 * @note None259 * @retval None260 */261 uint8_t SD_GetCSD(uint8_t *csd_data)262 {263 uint8_t r1; 264 265 r1 = SD_SendCMD(CMD9, 0, 0x01); //读取CSD寄存器266 if(r1 == 0x00)267 r1 = SD_ReceiveData(csd_data, 16); //接收16个字节的数据 268 SPISD_CS(0); //取消片选269 270 return r1?1:0;271 }272 273 /**274 * @brief CID,卡识别号,128bit275 * @note None276 * @retval None277 */278 uint8_t SD_GetCID(uint8_t *cid_data)279 {280 uint8_t r1;281 282 r1 = SD_SendCMD(CMD10, 0, 0x01); //读取CID寄存器283 if(r1==0x00)284 r1 = SD_ReceiveData(cid_data, 16); //接收16个字节的数据 285 SPISD_CS(0); //取消片选286 287 return r1?1:0;288 }289 290 //获取SD卡的总扇区数291 uint32_t SD_GetSectorCount(void)292 {293 uint8_t n;294 uint8_t csd[16];295 uint16_t csize;296 uint32_t Capacity; 297 298 if(SD_GetCSD(csd) != 0) //取CSD信息,如果期间出错,返回0299 return 0; 300 301 if( (csd[0]&0xC0) == 0x40 ) //如果为SDHC卡,按照下面方式计算。V2.00的卡302 { 303 csize = csd[9] + ((uint16_t)csd[8] << 8) + 1;304 Capacity = (uint32_t)csize << 10; //得到扇区数 305 }else //V1.xx的卡306 { 307 n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;308 csize = (csd[8] >> 6) + ((uint16_t)csd[7] << 2) + ((uint16_t)(csd[6] & 3) << 10) + 1;309 Capacity = (uint32_t)csize << (n - 9); //得到扇区数 310 }311 312 return Capacity;313 }314 315 int MSD0_GetCardInfo(PMSD_CARDINFO SD0_CardInfo)316 {317 uint8_t r1;318 uint8_t CSD_Tab[16], CID_Tab[16];319 320 /* Send CMD9, Read CSD */321 r1 = SD_SendCMD(CMD9, 0, 0xFF);322 if(r1 != 0x00)323 return r1;324 if(SD_ReceiveData(CSD_Tab, 16))325 return 1;326 327 /* Send CMD10, Read CID */328 r1 = SD_SendCMD(CMD10, 0, 0xFF);329 if(r1 != 0x00)330 return r1; 331 if(SD_ReceiveData(CID_Tab, 16))332 return 2;333 334 /* Byte 0 */335 SD0_CardInfo->CSD.CSDStruct = (CSD_Tab[0] & 0xC0) >> 6;336 SD0_CardInfo->CSD.SysSpecVersion = (CSD_Tab[0] & 0x3C) >> 2;337 SD0_CardInfo->CSD.Reserved1 = CSD_Tab[0] & 0x03;338 /* Byte 1 */339 SD0_CardInfo->CSD.TAAC = CSD_Tab[1] ;340 /* Byte 2 */341 SD0_CardInfo->CSD.NSAC = CSD_Tab[2];342 /* Byte 3 */343 SD0_CardInfo->CSD.MaxBusClkFrec = CSD_Tab[3];344 /* Byte 4 */345 SD0_CardInfo->CSD.CardComdClasses = CSD_Tab[4] << 4;346 /* Byte 5 */347 SD0_CardInfo->CSD.CardComdClasses |= (CSD_Tab[5] & 0xF0) >> 4;348 SD0_CardInfo->CSD.RdBlockLen = CSD_Tab[5] & 0x0F;349 /* Byte 6 */350 SD0_CardInfo->CSD.PartBlockRead = (CSD_Tab[6] & 0x80) >> 7;351 SD0_CardInfo->CSD.WrBlockMisalign = (CSD_Tab[6] & 0x40) >> 6;352 SD0_CardInfo->CSD.RdBlockMisalign = (CSD_Tab[6] & 0x20) >> 5;353 SD0_CardInfo->CSD.DSRImpl = (CSD_Tab[6] & 0x10) >> 4;354 SD0_CardInfo->CSD.Reserved2 = 0; /* Reserved */355 SD0_CardInfo->CSD.DeviceSize = (CSD_Tab[6] & 0x03) << 10;356 /* Byte 7 */357 SD0_CardInfo->CSD.DeviceSize |= (CSD_Tab[7]) << 2;358 /* Byte 8 */359 SD0_CardInfo->CSD.DeviceSize |= (CSD_Tab[8] & 0xC0) >> 6;360 SD0_CardInfo->CSD.MaxRdCurrentVDDMin = (CSD_Tab[8] & 0x38) >> 3;361 SD0_CardInfo->CSD.MaxRdCurrentVDDMax = (CSD_Tab[8] & 0x07);362 /* Byte 9 */363 SD0_CardInfo->CSD.MaxWrCurrentVDDMin = (CSD_Tab[9] & 0xE0) >> 5;364 SD0_CardInfo->CSD.MaxWrCurrentVDDMax = (CSD_Tab[9] & 0x1C) >> 2;365 SD0_CardInfo->CSD.DeviceSizeMul = (CSD_Tab[9] & 0x03) << 1;366 /* Byte 10 */367 SD0_CardInfo->CSD.DeviceSizeMul |= (CSD_Tab[10] & 0x80) >> 7;368 SD0_CardInfo->CSD.EraseGrSize = (CSD_Tab[10] & 0x7C) >> 2;369 SD0_CardInfo->CSD.EraseGrMul = (CSD_Tab[10] & 0x03) << 3;370 /* Byte 11 */371 SD0_CardInfo->CSD.EraseGrMul |= (CSD_Tab[11] & 0xE0) >> 5;372 SD0_CardInfo->CSD.WrProtectGrSize = (CSD_Tab[11] & 0x1F);373 /* Byte 12 */374 SD0_CardInfo->CSD.WrProtectGrEnable = (CSD_Tab[12] & 0x80) >> 7;375 SD0_CardInfo->CSD.ManDeflECC = (CSD_Tab[12] & 0x60) >> 5;376 SD0_CardInfo->CSD.WrSpeedFact = (CSD_Tab[12] & 0x1C) >> 2;377 SD0_CardInfo->CSD.MaxWrBlockLen = (CSD_Tab[12] & 0x03) << 2;378 /* Byte 13 */379 SD0_CardInfo->CSD.MaxWrBlockLen |= (CSD_Tab[13] & 0xc0) >> 6;380 SD0_CardInfo->CSD.WriteBlockPaPartial = (CSD_Tab[13] & 0x20) >> 5;381 SD0_CardInfo->CSD.Reserved3 = 0;382 SD0_CardInfo->CSD.ContentProtectAppli = (CSD_Tab[13] & 0x01);383 /* Byte 14 */384 SD0_CardInfo->CSD.FileFormatGrouop = (CSD_Tab[14] & 0x80) >> 7;385 SD0_CardInfo->CSD.CopyFlag = (CSD_Tab[14] & 0x40) >> 6;386 SD0_CardInfo->CSD.PermWrProtect = (CSD_Tab[14] & 0x20) >> 5;387 SD0_CardInfo->CSD.TempWrProtect = (CSD_Tab[14] & 0x10) >> 4;388 SD0_CardInfo->CSD.FileFormat = (CSD_Tab[14] & 0x0C) >> 2;389 SD0_CardInfo->CSD.ECC = (CSD_Tab[14] & 0x03);390 /* Byte 15 */391 SD0_CardInfo->CSD.CSD_CRC = (CSD_Tab[15] & 0xFE) >> 1;392 SD0_CardInfo->CSD.Reserved4 = 1;393 394 if(SD0_CardInfo->CardType == V2HC)395 {396 /* Byte 7 */397 SD0_CardInfo->CSD.DeviceSize = (uint16_t)(CSD_Tab[8]) *256;398 /* Byte 8 */399 SD0_CardInfo->CSD.DeviceSize += CSD_Tab[9] ;400 }401 402 SD0_CardInfo->Capacity = SD0_CardInfo->CSD.DeviceSize * MSD_BLOCKSIZE * 1024;403 SD0_CardInfo->BlockSize = MSD_BLOCKSIZE;404 405 /* Byte 0 */406 SD0_CardInfo->CID.ManufacturerID = CID_Tab[0];407 /* Byte 1 */408 SD0_CardInfo->CID.OEM_AppliID = CID_Tab[1] << 8;409 /* Byte 2 */410 SD0_CardInfo->CID.OEM_AppliID |= CID_Tab[2];411 /* Byte 3 */412 SD0_CardInfo->CID.ProdName1 = CID_Tab[3] << 24;413 /* Byte 4 */414 SD0_CardInfo->CID.ProdName1 |= CID_Tab[4] << 16;415 /* Byte 5 */416 SD0_CardInfo->CID.ProdName1 |= CID_Tab[5] << 8;417 /* Byte 6 */418 SD0_CardInfo->CID.ProdName1 |= CID_Tab[6];419 /* Byte 7 */420 SD0_CardInfo->CID.ProdName2 = CID_Tab[7];421 /* Byte 8 */422 SD0_CardInfo->CID.ProdRev = CID_Tab[8];423 /* Byte 9 */424 SD0_CardInfo->CID.ProdSN = CID_Tab[9] << 24;425 /* Byte 10 */426 SD0_CardInfo->CID.ProdSN |= CID_Tab[10] << 16;427 /* Byte 11 */428 SD0_CardInfo->CID.ProdSN |= CID_Tab[11] << 8;429 /* Byte 12 */430 SD0_CardInfo->CID.ProdSN |= CID_Tab[12];431 /* Byte 13 */432 SD0_CardInfo->CID.Reserved1 |= (CID_Tab[13] & 0xF0) >> 4;433 /* Byte 14 */434 SD0_CardInfo->CID.ManufactDate = (CID_Tab[13] & 0x0F) << 8;435 /* Byte 15 */436 SD0_CardInfo->CID.ManufactDate |= CID_Tab[14];437 /* Byte 16 */438 SD0_CardInfo->CID.CID_CRC = (CID_Tab[15] & 0xFE) >> 1;439 SD0_CardInfo->CID.Reserved2 = 1;440 441 return 0; 442 }443 444 445 //写SD卡446 //buf:数据缓存区447 //sector:起始扇区448 //cnt:扇区数449 //返回值:0,ok;其他,失败.450 uint8_t SD_WriteDisk(uint8_t *buf, uint32_t sector, uint8_t cnt)451 {452 uint8_t r1;453 454 if(SD_TYPE != V2HC)455 sector *= 512; //转换为字节地址456 if(cnt == 1)457 {458 r1 = SD_SendCMD(CMD24, sector, 0x01); //读命令459 if(r1 == 0) //指令发送成功460 r1=SD_SendBlock(buf, 0xFE); //写512个字节 461 }else462 {463 if(SD_TYPE != MMC)464 {465 SD_SendCMD(CMD55, 0, 0x01); 466 SD_SendCMD(CMD23, cnt, 0x01); //发送指令 467 }468 r1 = SD_SendCMD(CMD25, sector, 0x01); //连续读命令469 if(r1 == 0)470 {471 do472 {473 r1 = SD_SendBlock(buf,0xFC); //接收512个字节 474 buf += 512; 475 }while(--cnt && r1==0);476 r1 = SD_SendBlock(0,0xFD); //接收512个字节 477 }478 } 479 SPISD_CS(0); //取消片选480 481 return r1;482 } 483 484 //读SD卡485 //buf:数据缓存区486 //sector:扇区487 //cnt:扇区数488 //返回值:0,ok;其他,失败.489 uint8_t SD_ReadDisk(uint8_t*buf,uint32_t sector,uint8_t cnt)490 {491 uint8_t r1;492 493 if(SD_TYPE != V2HC)494 sector <<= 9; //转换为字节地址495 if(cnt == 1)496 {497 r1 = SD_SendCMD(CMD17, sector, 0x01); //读命令498 if(r1 == 0) 499 r1 = SD_ReceiveData(buf, 512); //接收512个字节 500 }else501 {502 r1 = SD_SendCMD(CMD18, sector, 0x01); //连续读命令503 do504 {505 r1 = SD_ReceiveData(buf, 512); //接收512个字节 506 buf += 512; 507 }while(--cnt && r1==0); 508 SD_SendCMD(CMD12, 0, 0x01); //发送停止命令509 } 510 SPISD_CS(0); //取消片选511 512 return r1;513 }514 515 uint8_t SPI_ReadWrite(uint8_t Txdata)516 {517 uint8_t Rxdata; 518 HAL_SPI_TransmitReceive(&hspi1, &Txdata, &Rxdata, 1, 100);519 return Rxdata;520 }521 522 //SPI1波特率设置523 void SPI_SetSpeed(uint8_t speed)524 {525 hspi1.Init.BaudRatePrescaler = speed;526 if (HAL_SPI_Init(&hspi1) != HAL_OK)527 {528 Error_Handler();529 }530 }531 ///////////////////////////END//////////////////////////////////////
drive_spisd.h如下
1 /* Define to prevent recursive inclusion -------------------------------------*/ 2 #ifndef __DRIVE_SPISD_H 3 #define __DRIVE_SPISD_H 4 5 #ifdef __cplusplus 6 extern "C" { 7 #endif 8 9 /* Includes ------------------------------------------------------------------*/ 10 #include "main.h" 11 /* Private includes ----------------------------------------------------------*/ 12 #include "fatfs.h" 13 /* Exported types ------------------------------------------------------------*/ 14 enum _CD_HOLD 15 { 16 HOLD = 0, 17 RELEASE = 1, 18 }; 19 20 typedef struct /* Card Specific Data */ 21 { 22 uint8_t CSDStruct; /* CSD structure */ 23 uint8_t SysSpecVersion; /* System specification version */ 24 uint8_t Reserved1; /* Reserved */ 25 uint8_t TAAC; /* Data read access-time 1 */ 26 uint8_t NSAC; /* Data read access-time 2 in CLK cycles */ 27 uint8_t MaxBusClkFrec; /* Max. bus clock frequency */ 28 uint16_t CardComdClasses; /* Card command classes */ 29 uint8_t RdBlockLen; /* Max. read data block length */ 30 uint8_t PartBlockRead; /* Partial blocks for read allowed */ 31 uint8_t WrBlockMisalign; /* Write block misalignment */ 32 uint8_t RdBlockMisalign; /* Read block misalignment */ 33 uint8_t DSRImpl; /* DSR implemented */ 34 uint8_t Reserved2; /* Reserved */ 35 uint32_t DeviceSize; /* Device Size */ 36 uint8_t MaxRdCurrentVDDMin; /* Max. read current @ VDD min */ 37 uint8_t MaxRdCurrentVDDMax; /* Max. read current @ VDD max */ 38 uint8_t MaxWrCurrentVDDMin; /* Max. write current @ VDD min */ 39 uint8_t MaxWrCurrentVDDMax; /* Max. write current @ VDD max */ 40 uint8_t DeviceSizeMul; /* Device size multiplier */ 41 uint8_t EraseGrSize; /* Erase group size */ 42 uint8_t EraseGrMul; /* Erase group size multiplier */ 43 uint8_t WrProtectGrSize; /* Write protect group size */ 44 uint8_t WrProtectGrEnable; /* Write protect group enable */ 45 uint8_t ManDeflECC; /* Manufacturer default ECC */ 46 uint8_t WrSpeedFact; /* Write speed factor */ 47 uint8_t MaxWrBlockLen; /* Max. write data block length */ 48 uint8_t WriteBlockPaPartial; /* Partial blocks for write allowed */ 49 uint8_t Reserved3; /* Reserded */ 50 uint8_t ContentProtectAppli; /* Content protection application */ 51 uint8_t FileFormatGrouop; /* File format group */ 52 uint8_t CopyFlag; /* Copy flag (OTP) */ 53 uint8_t PermWrProtect; /* Permanent write protection */ 54 uint8_t TempWrProtect; /* Temporary write protection */ 55 uint8_t FileFormat; /* File Format */ 56 uint8_t ECC; /* ECC code */ 57 uint8_t CSD_CRC; /* CSD CRC */ 58 uint8_t Reserved4; /* always 1*/ 59 }MSD_CSD; 60 61 typedef struct /*Card Identification Data*/ 62 { 63 uint8_t ManufacturerID; /* ManufacturerID */ 64 uint16_t OEM_AppliID; /* OEM/Application ID */ 65 uint32_t ProdName1; /* Product Name part1 */ 66 uint8_t ProdName2; /* Product Name part2*/ 67 uint8_t ProdRev; /* Product Revision */ 68 uint32_t ProdSN; /* Product Serial Number */ 69 uint8_t Reserved1; /* Reserved1 */ 70 uint16_t ManufactDate; /* Manufacturing Date */ 71 uint8_t CID_CRC; /* CID CRC */ 72 uint8_t Reserved2; /* always 1 */ 73 }MSD_CID; 74 75 typedef struct 76 { 77 MSD_CSD CSD; 78 MSD_CID CID; 79 uint32_t Capacity; /* Card Capacity */ 80 uint32_t BlockSize; /* Card Block Size */ 81 uint16_t RCA; 82 uint8_t CardType; 83 uint32_t SpaceTotal; /* Total space size in file system */ 84 uint32_t SpaceFree; /* Free space size in file system */ 85 }MSD_CARDINFO, *PMSD_CARDINFO; 86 87 extern MSD_CARDINFO SD0_CardInfo; 88 /* Exported constants --------------------------------------------------------*/ 89 90 /* Exported macro ------------------------------------------------------------*/ 91 //SD卡类型 92 #define ERR 0x00 93 #define MMC 0x01 94 #define V1 0x02 95 #define V2 0x04 96 #define V2HC 0x06 97 98 #define DUMMY_BYTE 0xFF 99 #define MSD_BLOCKSIZE 512100 101 /*102 CMD定义103 bc:发送到所有卡,不返回响应104 bcr:发送到所有卡,同时接收所有卡的响应105 ac:发送到选定卡,无数据传输106 adtc:发送到选定卡,有数据传输107 */108 #define CMD0 0 //bc,卡复位到IDLE状态109 #define CMD1 1 110 #define CMD8 8 //bcr,读SD卡接口条件,包含主机支持的电压信息,并询问卡是否支持111 #define CMD9 9 //ac,读CSD数据112 #define CMD10 10 //ac,读CID数据113 #define CMD12 12 //ac,停止数据传输114 #define CMD16 16 //ac,设置块长度(对于SDHC卡块命令长度固定为512字节) 应返回0x00115 #define CMD17 17 //adtc,读单个块116 #define CMD18 18 //adtc,读多个块。连续读块,直到被CMD12中断。117 #define CMD23 23 //设置多sector写入前预先擦除N个block118 #define CMD24 24 //adtc,写单个块119 #define CMD25 25 //adtc,写多个块。连续写块,直到被CMD12中断。120 #define CMD41 41 //应返回0x00121 #define CMD55 55 //ac,指定下个命令为特定应用命令(ACMD),不是标准命令,应返回0x01122 #define CMD58 58 //读OCR信息123 #define CMD59 59 //使能/禁止CRC,应返回0x00124 125 //数据写入回应字意义126 #define MSD_DATA_OK 0x05127 #define MSD_DATA_CRC_ERROR 0x0B128 #define MSD_DATA_WRITE_ERROR 0x0D129 #define MSD_DATA_OTHER_ERROR 0xFF130 131 //SD卡回应标记字132 #define MSD_RESPONSE_NO_ERROR 0x00133 #define MSD_IN_IDLE_STATE 0x01134 #define MSD_ERASE_RESET 0x02135 #define MSD_ILLEGAL_COMMAND 0x04136 #define MSD_COM_CRC_ERROR 0x08137 #define MSD_ERASE_SEQUENCE_ERROR 0x10138 #define MSD_ADDRESS_ERROR 0x20139 #define MSD_PARAMETER_ERROR 0x40140 #define MSD_RESPONSE_FAILURE 0xFF141 142 /* Exported functions prototypes ---------------------------------------------*/143 void SPISD_CS(uint8_t p);144 uint8_t SD_Init(void);145 void WritetoSD(BYTE write_buff[],uint8_t bufSize);146 void FileSystem_Init(void);147 uint32_t SD_GetSectorCount(void);148 uint8_t SD_GetCID(uint8_t *cid_data);149 uint8_t SD_GetCSD(uint8_t *csd_data);150 int MSD0_GetCardInfo(PMSD_CARDINFO SD0_CardInfo);151 uint8_t SD_ReadDisk(uint8_t*buf, uint32_t sector, uint8_t cnt);152 uint8_t SD_WriteDisk(uint8_t*buf, uint32_t sector, uint8_t cnt);153 void SPI_SetSpeed(uint8_t speed);154 uint8_t SPI_ReadWrite(uint8_t Txdata);155 156 /* Private defines -----------------------------------------------------------*/157 extern uint8_t SD_TYPE;158 #ifdef __cplusplus159 }160 #endif161 162 #endif /* __DRIVE_SPISD_H */163 164 /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
然后在文件系统文件夹里修改内容。
user_diskio.c如下
1 /* USER CODE BEGIN Header */ 2 /** 3 ****************************************************************************** 4 * @file user_diskio.c 5 * @brief This file includes a diskio driver skeleton to be completed by the user. 6 ****************************************************************************** 7 * @attention 8 * 9 * Copyright (c) 2023 STMicroelectronics. 10 * All rights reserved. 11 * 12 * This software is licensed under terms that can be found in the LICENSE file 13 * in the root directory of this software component. 14 * If no LICENSE file comes with this software, it is provided AS-IS. 15 * 16 ****************************************************************************** 17 */ 18 /* USER CODE END Header */ 19 20 #ifdef USE_OBSOLETE_USER_CODE_SECTION_0 21 /* 22 * Warning: the user section 0 is no more in use (starting from CubeMx version 4.16.0) 23 * To be suppressed in the future. 24 * Kept to ensure backward compatibility with previous CubeMx versions when 25 * migrating projects. 26 * User code previously added there should be copied in the new user sections before 27 * the section contents can be deleted. 28 */ 29 /* USER CODE BEGIN 0 */ 30 /* USER CODE END 0 */ 31 #endif 32 33 /* USER CODE BEGIN DECL */ 34 35 /* Includes ------------------------------------------------------------------*/ 36 #include37 #include "ff_gen_drv.h" 38 39 /* Private typedef -----------------------------------------------------------*/ 40 #include "drive_spisd.h" 41 /* Private define ------------------------------------------------------------*/ 42 43 /* Private variables ---------------------------------------------------------*/ 44 /* Disk status */ 45 static volatile DSTATUS Stat = STA_NOINIT; 46 47 /* USER CODE END DECL */ 48 49 /* Private function prototypes -----------------------------------------------*/ 50 DSTATUS USER_initialize (BYTE pdrv); 51 DSTATUS USER_status (BYTE pdrv); 52 DRESULT USER_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count); 53 #if _USE_WRITE == 1 54 DRESULT USER_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count); 55 #endif /* _USE_WRITE == 1 */ 56 #if _USE_IOCTL == 1 57 DRESULT USER_ioctl (BYTE pdrv, BYTE cmd, void *buff); 58 #endif /* _USE_IOCTL == 1 */ 59 60 Diskio_drvTypeDef USER_Driver = 61 { 62 USER_initialize, 63 USER_status, 64 USER_read, 65 #if _USE_WRITE 66 USER_write, 67 #endif /* _USE_WRITE == 1 */ 68 #if _USE_IOCTL == 1 69 USER_ioctl, 70 #endif /* _USE_IOCTL == 1 */ 71 }; 72 73 /* Private functions ---------------------------------------------------------*/ 74 75 /** 76 * @brief Initializes a Drive 77 * @param pdrv: Physical drive number (0..) 78 * @retval DSTATUS: Operation status 79 */ 80 DSTATUS USER_initialize ( 81 BYTE pdrv /* Physical drive nmuber to identify the drive */ 82 ) 83 { 84 /* USER CODE BEGIN INIT */ 85 uint8_t res; 86 res = SD_Init(); 87 if(res) //STM32 SPI的bug,在sd卡操作失败的时候如果不执行下面的语句,可能导致SPI读写异常 88 { 89 SPI_SetSpeed(SPI_BAUDRATEPRESCALER_256); 90 SPI_ReadWrite(0xff); //提供额外的8个时钟 91 SPI_SetSpeed(SPI_BAUDRATEPRESCALER_4); 92 } 93 if(res) 94 return STA_NOINIT; 95 else 96 return RES_OK; 97 /* USER CODE END INIT */ 98 } 99 100 /**101 * @brief Gets Disk Status102 * @param pdrv: Physical drive number (0..)103 * @retval DSTATUS: Operation status104 */105 DSTATUS USER_status (106 BYTE pdrv /* Physical drive number to identify the drive */107 )108 {109 /* USER CODE BEGIN STATUS */110 switch (pdrv)111 {112 case 0 :113 return RES_OK;114 case 1 :115 return RES_OK;116 case 2 :117 return RES_OK;118 default:119 return STA_NOINIT;120 }121 /* USER CODE END STATUS */122 }123 124 /**125 * @brief Reads Sector(s)126 * @param pdrv: Physical drive number (0..)127 * @param *buff: Data buffer to store read data128 * @param sector: Sector address (LBA)129 * @param count: Number of sectors to read (1..128)130 * @retval DRESULT: Operation result131 */132 DRESULT USER_read (133 BYTE pdrv, /* Physical drive nmuber to identify the drive */134 BYTE *buff, /* Data buffer to store read data */135 DWORD sector, /* Sector address in LBA */136 UINT count /* Number of sectors to read */137 )138 {139 /* USER CODE BEGIN READ */140 uint8_t res;141 if( !count )142 { 143 return RES_PARERR; /* count不能等于0,否则返回参数错误 */144 }145 switch (pdrv)146 {147 case 0:148 res = SD_ReadDisk(buff,sector,count); 149 if(res == 0)150 return RES_OK;151 else152 return RES_ERROR; 153 default:154 return RES_ERROR;155 }156 /* USER CODE END READ */157 }158 159 /**160 * @brief Writes Sector(s)161 * @param pdrv: Physical drive number (0..)162 * @param *buff: Data to be written163 * @param sector: Sector address (LBA)164 * @param count: Number of sectors to write (1..128)165 * @retval DRESULT: Operation result166 */167 #if _USE_WRITE == 1168 DRESULT USER_write (169 BYTE pdrv, /* Physical drive nmuber to identify the drive */170 const BYTE *buff, /* Data to be written */171 DWORD sector, /* Sector address in LBA */172 UINT count /* Number of sectors to write */173 )174 {175 /* USER CODE BEGIN WRITE */176 /* USER CODE HERE */177 uint8_t res;178 if( !count )179 return RES_PARERR; /* count不能等于0,否则返回参数错误 */180 switch (pdrv)181 {182 case 0:183 res=SD_WriteDisk((uint8_t *)buff,sector,count);184 if(res == 0)185 return RES_OK;186 else187 return RES_ERROR; 188 default:189 return RES_ERROR;190 }191 /* USER CODE END WRITE */192 }193 #endif /* _USE_WRITE == 1 */194 195 /**196 * @brief I/O control operation197 * @param pdrv: Physical drive number (0..)198 * @param cmd: Control code199 * @param *buff: Buffer to send/receive control data200 * @retval DRESULT: Operation result201 */202 #if _USE_IOCTL == 1203 DRESULT USER_ioctl (204 BYTE pdrv, /* Physical drive nmuber (0..) */205 BYTE cmd, /* Control code */206 void *buff /* Buffer to send/receive control data */207 )208 {209 /* USER CODE BEGIN IOCTL */210 DRESULT res;211 switch(cmd)212 {213 case CTRL_SYNC:214 SPISD_CS(1);215 do{216 HAL_Delay(20);217 }while(SPI_ReadWrite(0xFF)!=0xFF);218 res=RES_OK;219 SPISD_CS(0);220 break; 221 case GET_SECTOR_SIZE:222 *(WORD*)buff = 512;223 res = RES_OK;224 break; 225 case GET_BLOCK_SIZE:226 *(WORD*)buff = 8;227 res = RES_OK;228 break; 229 case GET_SECTOR_COUNT:230 *(DWORD*)buff = SD_GetSectorCount();231 res = RES_OK;232 break;233 default:234 res = RES_PARERR;235 break;236 }237 return res;238 /* USER CODE END IOCTL */239 }240 #endif /* _USE_IOCTL == 1 */
main.c
1 /* USER CODE BEGIN Header */ 2 /** 3 ****************************************************************************** 4 * @file : main.c 5 * @brief : Main program body 6 ****************************************************************************** 7 * @attention 8 * 9 * Copyright (c) 2023 STMicroelectronics. 10 * All rights reserved. 11 * 12 * This software is licensed under terms that can be found in the LICENSE file 13 * in the root directory of this software component. 14 * If no LICENSE file comes with this software, it is provided AS-IS. 15 * 16 ****************************************************************************** 17 */ 18 /* USER CODE END Header */ 19 /* Includes ------------------------------------------------------------------*/ 20 #include "main.h" 21 #include "fatfs.h" 22 #include "spi.h" 23 #include "usart.h" 24 #include "gpio.h" 25 26 /* Private includes ----------------------------------------------------------*/ 27 /* USER CODE BEGIN Includes */ 28 #include "drive_spisd.h" 29 /* USER CODE END Includes */ 30 31 /* Private typedef -----------------------------------------------------------*/ 32 /* USER CODE BEGIN PTD */ 33 34 /* USER CODE END PTD */ 35 36 /* Private define ------------------------------------------------------------*/ 37 /* USER CODE BEGIN PD */ 38 /* USER CODE END PD */ 39 40 /* Private macro -------------------------------------------------------------*/ 41 /* USER CODE BEGIN PM */ 42 43 /* USER CODE END PM */ 44 45 /* Private variables ---------------------------------------------------------*/ 46 47 /* USER CODE BEGIN PV */ 48 uint8_t status = 0; 49 uint8_t writeBuf[] = "demo program forever no bug!!!\r\n"; 50 /* USER CODE END PV */ 51 52 /* Private function prototypes -----------------------------------------------*/ 53 void SystemClock_Config(void); 54 /* USER CODE BEGIN PFP */ 55 56 /* USER CODE END PFP */ 57 58 /* Private user code ---------------------------------------------------------*/ 59 /* USER CODE BEGIN 0 */ 60 61 /* USER CODE END 0 */ 62 63 /** 64 * @brief The application entry point. 65 * @retval int 66 */ 67 int main(void) 68 { 69 /* USER CODE BEGIN 1 */ 70 UINT Bw; 71 FIL file; 72 uint16_t cb_task = 0; 73 uint8_t res1 = 0, res2 = 0; 74 /* USER CODE END 1 */ 75 76 /* MCU Configuration--------------------------------------------------------*/ 77 78 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ 79 HAL_Init(); 80 81 /* USER CODE BEGIN Init */ 82 83 /* USER CODE END Init */ 84 85 /* Configure the system clock */ 86 SystemClock_Config(); 87 88 /* USER CODE BEGIN SysInit */ 89 90 /* USER CODE END SysInit */ 91 92 /* Initialize all configured peripherals */ 93 MX_GPIO_Init(); 94 MX_SPI1_Init(); 95 MX_USART1_UART_Init(); 96 MX_FATFS_Init(); 97 /* USER CODE BEGIN 2 */ 98 FileSystem_Init(); //初始化文件系统 99 100 status = 1;101 res1 = f_open(&file, "sdRW1.txt", FA_OPEN_ALWAYS | FA_WRITE);102 if((res1 & FR_DENIED) == FR_DENIED)103 printf("卡存储已满,写入失败! \r\n");104 /* USER CODE END 2 */105 106 /* Infinite loop */107 /* USER CODE BEGIN WHILE */108 while (1)109 {110 /* USER CODE END WHILE */111 112 /* USER CODE BEGIN 3 */113 if(status == 1)114 {115 if(res1 == FR_OK)116 {117 f_lseek(&file, f_size(&file)); //确保写入不会覆盖之前的数据118 res2 = f_write(&file, writeBuf, sizeof(writeBuf), &Bw); //写数据到SD卡 119 if(res2 != FR_OK)120 {121 printf("文件写入失败! \r\n");122 HAL_GPIO_WritePin(User_led_GPIO_Port, User_led_Pin, GPIO_PIN_RESET);123 }else124 {125 HAL_GPIO_WritePin(User_led_GPIO_Port, User_led_Pin, GPIO_PIN_SET);126 } 127 }128 else129 {130 printf("打开文件失败! %d\r\n",res1);131 } 132 if(++cb_task%4096==0)133 f_sync(&file); 134 }else if(status == 2)135 {136 f_close(&file);137 f_mount(NULL, USERPath, 1); //取消挂载138 HAL_GPIO_WritePin(User_led_GPIO_Port, User_led_Pin, GPIO_PIN_RESET);139 status = 0;140 }141 HAL_Delay(10);142 }143 /* USER CODE END 3 */144 }145 146 /**147 * @brief System Clock Configuration148 * @retval None149 */150 void SystemClock_Config(void)151 {152 RCC_OscInitTypeDef RCC_OscInitStruct = {0};153 RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};154 155 /** Configure the main internal regulator output voltage156 */157 __HAL_RCC_PWR_CLK_ENABLE();158 __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);159 /** Initializes the RCC Oscillators according to the specified parameters160 * in the RCC_OscInitTypeDef structure.161 */162 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;163 RCC_OscInitStruct.HSEState = RCC_HSE_ON;164 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;165 RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;166 RCC_OscInitStruct.PLL.PLLM = 4;167 RCC_OscInitStruct.PLL.PLLN = 168;168 RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;169 RCC_OscInitStruct.PLL.PLLQ = 4;170 if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)171 {172 Error_Handler();173 }174 /** Initializes the CPU, AHB and APB buses clocks175 */176 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK177 |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;178 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;179 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;180 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;181 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;182 183 if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)184 {185 Error_Handler();186 }187 }188 189 /* USER CODE BEGIN 4 */190 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)191 {192 if(GPIO_Pin == User_Key_Pin)193 {194 status = 2;195 }196 }197 /* USER CODE END 4 */198 199 /**200 * @brief This function is executed in case of error occurrence.201 * @retval None202 */203 void Error_Handler(void)204 {205 /* USER CODE BEGIN Error_Handler_Debug */206 /* User can add his own implementation to report the HAL error return state */207 __disable_irq();208 while (1)209 {210 }211 /* USER CODE END Error_Handler_Debug */212 }213 214 #ifdef USE_FULL_ASSERT215 /**216 * @brief Reports the name of the source file and the source line number217 * where the assert_param error has occurred.218 * @param file: pointer to the source file name219 * @param line: assert_param error line source number220 * @retval None221 */222 void assert_failed(uint8_t *file, uint32_t line)223 {224 /* USER CODE BEGIN 6 */225 /* User can add his own implementation to report the file name and line number,226 ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */227 /* USER CODE END 6 */228 }229 #endif /* USE_FULL_ASSERT */
实测是可以的,但是写速度并没有网上说的1M/s那么快,也有可能是我引线的问题。
标签: