STM32F429 Discovery开发板应用:实现SPI-SD Card文件写入(搭载FatFS文件系统)
阅读原文时间:2023年08月14日阅读:1

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); //发送CMD55 101 r1 = SD_SendCMD(CMD41, 0x40000000, 0X01); //发送CMD41 102 }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 }else 111 { 112 SD_SendCMD(CMD55, 0, 0X01); //发送CMD55 113 r1 = SD_SendCMD(CMD41, 0, 0X01); //发送CMD41 114 if(r1<=1) 115 { 116 SD_TYPE = V1; 117 retry = 0XFFFE; 118 do //等待退出IDLE模式 119 { 120 SD_SendCMD(CMD55, 0, 0X01); //发送CMD55 121 r1 = SD_SendCMD(CMD41, 0, 0X01); //发送CMD41 122 }while(r1&&retry--); 123 }else //MMC卡不支持CMD55+CMD41识别 124 { 125 SD_TYPE = MMC; //MMC V3 126 retry = 0XFFFE; 127 do //等待退出IDLE模式 128 { 129 r1 = SD_SendCMD(CMD1, 0, 0X01); //发送CMD1 130 }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 else 173 { 174 printf("格式化失败! \r\n"); 175 } 176 }else if(res == FR_OK) 177 { 178 printf("挂载成功! \r\n"); 179 }else 180 { 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 None
203 * @retval None
204 */
205 static uint8_t SD_ReceiveData(uint8_t *data, uint16_t len)
206 {
207 uint8_t r1;
208
209 SPISD_CS(1);
210 do
211 {
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 None
230 * @retval None
231 */
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); //忽略crc 247 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,卡的操作条件信息,128bit 258 * @note None 259 * @retval None 260 */ 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,卡识别号,128bit 275 * @note None 276 * @retval None 277 */ 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信息,如果期间出错,返回0 299 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 }else
462 {
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 do
472 {
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 }else
501 {
502 r1 = SD_SendCMD(CMD18, sector, 0x01); //连续读命令
503 do
504 {
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 512
100
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字节) 应返回0x00
115 #define CMD17 17 //adtc,读单个块
116 #define CMD18 18 //adtc,读多个块。连续读块,直到被CMD12中断。
117 #define CMD23 23 //设置多sector写入前预先擦除N个block
118 #define CMD24 24 //adtc,写单个块
119 #define CMD25 25 //adtc,写多个块。连续写块,直到被CMD12中断。
120 #define CMD41 41 //应返回0x00
121 #define CMD55 55 //ac,指定下个命令为特定应用命令(ACMD),不是标准命令,应返回0x01
122 #define CMD58 58 //读OCR信息
123 #define CMD59 59 //使能/禁止CRC,应返回0x00
124
125 //数据写入回应字意义
126 #define MSD_DATA_OK 0x05
127 #define MSD_DATA_CRC_ERROR 0x0B
128 #define MSD_DATA_WRITE_ERROR 0x0D
129 #define MSD_DATA_OTHER_ERROR 0xFF
130
131 //SD卡回应标记字
132 #define MSD_RESPONSE_NO_ERROR 0x00
133 #define MSD_IN_IDLE_STATE 0x01
134 #define MSD_ERASE_RESET 0x02
135 #define MSD_ILLEGAL_COMMAND 0x04
136 #define MSD_COM_CRC_ERROR 0x08
137 #define MSD_ERASE_SEQUENCE_ERROR 0x10
138 #define MSD_ADDRESS_ERROR 0x20
139 #define MSD_PARAMETER_ERROR 0x40
140 #define MSD_RESPONSE_FAILURE 0xFF
141
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 __cplusplus
159 }
160 #endif
161
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 #include
37 #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 Status
102 * @param pdrv: Physical drive number (0..)
103 * @retval DSTATUS: Operation status
104 */
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 data
128 * @param sector: Sector address (LBA)
129 * @param count: Number of sectors to read (1..128)
130 * @retval DRESULT: Operation result
131 */
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 else
152 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 written
163 * @param sector: Sector address (LBA)
164 * @param count: Number of sectors to write (1..128)
165 * @retval DRESULT: Operation result
166 */
167 #if _USE_WRITE == 1
168 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 else
187 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 operation
197 * @param pdrv: Physical drive number (0..)
198 * @param cmd: Control code
199 * @param *buff: Buffer to send/receive control data
200 * @retval DRESULT: Operation result
201 */
202 #if _USE_IOCTL == 1
203 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 }else
124 {
125 HAL_GPIO_WritePin(User_led_GPIO_Port, User_led_Pin, GPIO_PIN_SET);
126 }
127 }
128 else
129 {
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 Configuration
148 * @retval None
149 */
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 voltage
156 */
157 __HAL_RCC_PWR_CLK_ENABLE();
158 __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
159 /** Initializes the RCC Oscillators according to the specified parameters
160 * 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 clocks
175 */
176 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
177 |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 None
202 */
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_ASSERT
215 /**
216 * @brief Reports the name of the source file and the source line number
217 * where the assert_param error has occurred.
218 * @param file: pointer to the source file name
219 * @param line: assert_param error line source number
220 * @retval None
221 */
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那么快,也有可能是我引线的问题。

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章