STM32F429 Discovery開發板應用:實現SPI-SD Card檔案寫入(搭載FatFS檔案系統)

2023-06-07 12:00:53
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 <string.h>
 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那麼快,也有可能是我引線的問題。