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

来源:https://www.cnblogs.com/couvrir/archive/2023/06/07/17462810.html
-Advertisement-
Play Games

MCU:STM32F429ZIT6 開發環境:STM32CubeMX+MDK5 外購了一個SPI介面的SD Card模塊,想要實現SD卡存儲數據的功能。 首先需要打開STM32CubeMX工具。輸入開發板MCU對應型號,找到開發板對應封裝的MCU型號,雙擊打開(圖中第三)。 此時,雙擊完後會關閉此界 ...


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     	   

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ## 教程簡介 Ajax即Asynchronous Javascript And XML(非同步JavaScript和XML)在 2005年被Jesse James Garrett提出的新術語,用來描述一種使用現有技術集合的‘新’方法,包括: HTML 或 XHTML, CSS, JavaScript ...
  • ## 前言 本人之前開發了一個叫[電子腦殼](https://github.com/maker-community/ElectronBot.DotNet)的上位機應用,給稚暉君[ElectronBot](https://github.com/peng-zhihui/ElectronBot)開源機器人 ...
  • # SignalR+Hangfire 實現後臺任務隊列和實時通訊 1.簡介: SignalR是一個.NET的開源框架,SignalR可使用Web Socket, Server Sent Events 和 Long Polling作為底層傳輸方式實現服務端和客戶端的實時數據交互。 Hangfire是一 ...
  • > > 傳統桌面客戶端的遠程調試相比`UWP`,`ASP`等項目來說,配置比較麻煩,因為它是非部署的應用程式,原理是複製編譯的文件到遠程電腦,通過網路來連接和`VS`的通信,本文主要講述`WPF`,`WinForm`應用程式的遠程調試。 ![](https://learn.microsoft.co ...
  • ## 前言 使用 C# 作為開發語言已經 15 個年頭了,受惠於 C# 的不斷更新,伴隨著大量的新特性與大量語法糖,讓我更加容易寫出簡潔、高效的代碼。日常中大量特性早已信手拈來,當然從未嘗試過的特性更是難以盡數,但是每每回憶代碼中的特性究竟是哪個版本引入的,卻頗為含糊。索性簡單整理記錄下來,用以備忘 ...
  • 大家好,我是god23bin。歡迎來到《**一分鐘學一個 Linux 命令**》系列,今天需要你花兩分鐘時間來學習下,因為今天要講的是兩個命令,`mv` 和 `cp` 命令。 ...
  • # CentOS7 本地光碟鏡像rpm包 ## 一、前言 > rpm包的下載方式 > > - 通過本地光碟鏡像下載rpm,centos7.iso鏡像文件,內置了絕大多數軟體的rpm包(本文章即演示如何配置本地rpm) > > - 線上下載rpm包,有很多軟體的官網,以及第三方軟體倉庫,會提供下載功能 ...
  • 基本語法格式: Location block 的基本語法形式是: location [=|~|~*|^~|@] pattern { ... } [=|~|~*|^~|@] 被稱作 location modifier ,這會定義 Nginx 如何去匹配其後的 pattern ,以及該 pattern ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...