先展示最終實現的功能效果如下:
1.目的與意義
為什么選用SD卡?
使用Nor-flash(W25Q系列)進行圖片的存取,需要先把圖片通過對應軟件批量處理為二進制bin文件,再通過SPI等通訊方式將 bin文件燒寫進Nor-flash才能進行使用,使用時還要記住每張圖片的首地址和對應字節數,MCU才能準確的讀出來并顯示,所以在要更換圖片或者讀取顯示圖片上會顯得十分繁瑣麻煩;相對來說,SD(TF)卡雖然價格較貴,但通過SD(TF)卡和讀卡器直接連接電腦可以將SD(TF)卡虛擬為U盤,直接往里面拷貝圖片即可,更換圖片就顯得方便簡單;
為什么要使用FATFS文件系統?
MCU要跟SD卡之間進行通訊,可以使用SPI和SDIO通訊方式,不移植FATFS文件系統的話也可以像Nor-flash一樣通過對地址及扇區字節進行讀取,但是此方法較麻煩,為了讓MCU可以直接對SD卡內的各類文件格式進行讀取識別,所以需要一個相同的文件系統,又由于fatfs系統在現階段最廣泛兼容,且STM32CUBEMX支持移植,所以就選用了該文件系統;
為什么使用Tinyjpeg解碼庫?
STM32F4系列具有較大的flash和ram,所以可以直接移植LVGL或Emwin圖形庫對圖片格式進行解碼,同時STM32F4及以上系列的MCU,STM32CubeMX也已經支持Tinyjpeg解碼庫的直接移植:
所以證明Tinyjpeg解碼庫還是挺受歡迎的;
而STM32H系列價格昂貴,但具有JEPG硬件解碼,所以不需要軟件解碼庫;
STM32F1系列作為STM32家族中的廉價產品,其外設及內存肯定也較少,即沒有硬件JPEG解碼,flash和ram又較小,所以在使用顯示屏顯示圖片時,移植Tinyjpeg庫就是比較好的選擇了,通過軟件多寫一點,就能節省MCU的價格,相信大部分人還是愿意做的。
2.使用STM32CubeMX建立工程
這里先給出我使用的TF卡的硬件原理圖:
這里我設計成了只要TF卡插入卡槽,LED燈就會被點亮,同時這個CD腳也是后面配置FATFS文件系統要用到的,所以才在這里給出原理圖。
通過STM32CubeMX我們要完成創建對SD卡的SDIO通訊,FATFS文件系統的移植,Tinyjpeg則只需要拷貝幾個C文件和h文件即可:
1.使能SYS的serial Wire,選擇晶振及配置時鐘樹,這些創建基本工程也都要進行配置,這里我就不具體給出設置參數了,我這邊使用的是外部晶振配置為72MHz時鐘:
2.配置SD卡的SDIO通訊方式(因為MCU又要從SD卡讀取文件,又要將數據發送到TFTLCD進行顯示,所以SDIO這里使用DMA方式,減少對MCU線程的占用):
這里配置了SDIO的基本參數,開啟4線通訊(對應4個IO口才會使能),然后使能硬件流(看過很多博主都說使能了硬件流之后SD卡初始化成功概率高很多,我自己測試也確實是),最后設置工作頻率1Mhz(計算方式為:SDIO的時鐘頻率/(SDIOCLK clock divide factor+2),通過時鐘樹可以看到SDIO的時鐘頻率為36MHz ,如果SD卡通訊失敗率很高,則可以再調小頻率進行嘗試,若改小后效果仍然很差,需檢查硬件布線是否存在較大線長差異或線路干擾等);
接下來開啟SDIO的DMA通道及中斷使能:
由于是從SD卡讀取數據到MCU,所以方向選擇外設到內存;
這里設置好后要到中斷優先級NVIC里面,將DMA的中斷優先級改低,一般DMA的中斷優先級都調到比其他重要中斷低,防止大規模傳輸數據時打斷其他重要中斷:
這樣SDIO就配置完成了。
3.移植FATFS文件系統:
這里也沒啥需要進行配置修改的,由于大家都是中國人,難免會用到中文給文件夾起名,所以這里將CODE_PAGE修改為simplified Chinese即可,同時為了避免長文件名出錯,所以也使能了USE_LFN使用棧的方式。由于這里我們只用了一個外部存儲器(TF卡),所以VOLUMES默認為1即可,操作塊(MAX_SS及MIN_SS)為512字節也是默認即可。
然后配置其設備檢測IO口(即上面原理圖跟CD腳相連的MCU的IO口,低電平觸發,所以配置為上拉輸入即可):
至此SDIO(DMA)跟FATFS文件系統也配置完成。
接下來就是Keil生成工程,這里把堆棧可申請空間都稍微調大至4KB,確保FATFS和Tinyjpeg操作時有足夠的空間。
3.對工程進行修改,并測試MCU跟SD卡正常通訊及掛載FATFS系統
打開KEIL工程的main.c文件,找到SDIO初始化的位置將其數據總線改為1位,這里僅是做初始化用(初始化用1位數據總線,400KHz以下頻率),初始化完成后程序會切換到4位數據總線:
接下來編寫SD卡的測試函數:
1.配置uart對接printf函數:
#include "stdio.h"#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */PUTCHAR_PROTOTYPE //重定義usart1,之后使用printf()函數將自動通過串口1輸出
{HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF); //改變&huart1為&huart2可以選擇串口2return ch;
}
然后勾選USE MicroLIB庫:
2.讀取SD卡的