很久沒有寫公眾號了,一方面忙,另一方面也不知道寫些什么內容,大家如果有想了解的(前提是我也懂),可以后臺發送給我。
今天主要來測試一下SPI讀寫SD卡的速度。SD卡是一個嵌入式中非常常用的外設,可以用于存儲一些大容量的數據。但用單片機讀寫SD卡速度一般都有限(對于高速SD卡,主要是受限于單片機本身的接口速度),在高速、實時數據存儲時可能會有影響。但具體速度可以達到多少呢,今天就來實際測試一下。
SD卡一般有兩種常用的接口SPI和SDIO,SDIO又有1線和4線之分。很多單片機沒有SDIO接口,但SPI接口就比較常用,今天主要來測試一下SPI接口讀寫SD卡的速度,主要是寫入速度。? ? ? ? ?
1.單純SPI接口測試(非DMA)
我們知道,像SD卡之類的Flash存儲器,一般都是按扇區擦除整塊數據。因此每次寫入字節數是扇區整數倍時,效率會比較高。同時,每次寫入數據時,都需要先發送一些SD卡的指令,所以單次寫入數據量越大,平均速度也就越快。了解了這些,我們就知道如何進行測試了。
首先,SD卡底層驅動使用的是HAL庫函數,單字節讀寫,沒有任何改動和優化:
uint8_t SPI_ReadWriteByte(uint8_t TxData){uint8_t RxData = 0;HAL_SPI_TransmitReceive(&hspi3,&TxData,&RxData,1,100);return RxData;}
接下來,我們先確定SPI和時鐘頻率多少合適,經過測試,發現20MHz的時鐘頻率比較合適,10MHz時讀寫速度會降低,再高的時鐘頻率對速度的提升也很小。因此我們這里用20MHz的時鐘。

可以看到,單次寫入數據量越大,平均速度就越快。當單次寫入數據達到32KB時,速度提升不明顯。而且一般單片機內部RAM緩存也有限,單次寫入16KB是一個比較合適的選擇。
看到這個不到100KB/S速度,我還是有的不敢相信的,畢竟20MHz的時鐘,理論上速度可以達到2MB/S左右,考慮到一些文件系統等協議的消耗,能到1/3差不多,那也得600多KB,現在的速度差距有點大。
當然,這個使用的HAL庫函數有關,HAL_SPI_TransmitReceive函數效率比較低,內部做了大量的判斷等操作,而且單字節傳輸也嚴重影響效率。如果自己優化一下,相信效率會有很大的提升。有興趣的小伙伴可以試試。我們這次其實主要是測試SPI+DMA的速度,所以就不在這里糾結了。
2.SPI+DMA接口測試
DMA可以在外設和內存之間搬運數據,而不需要CPU的參與。其優勢在于大量數據傳輸時,比如SD卡讀寫、SPI接口的液晶屏刷屏等。如果只是讀寫幾個字節的數據,比如一些SPI接口的AD、DA等,DMA的優勢就不明顯。
因為SPI接口的設備一般都不是純數據傳輸,都要配合一些指令等。所以即使使用DMA,也是要等待DMA傳輸完成再進行其它操作。當然這期間CPU可以通過中斷方式去處理一些其它事情。
SPI+DMA寫數據函數如下,使用的也是HAL庫,沒有進行優化。? ? ? ? ?
int8_t SD_WriteBuffer_DMA(const uint8_t *TxData, uint16_t Size){uint32_t i = 0; // 循環變量SPI3_DMA_Flag = 0;SPI_TransmitReceive_DMA(&HSPI_TF, (uint8_t*)TxData, txrxdata, Size);/* 等待DMA傳輸完成 */while (1){if(SPI3_DMA_Flag == 1)break;i++;if (i > 0xFFFFFF){return 1; /* 超時退出 */}}return 0;}
以向SD卡寫數據為例,需要改為DMA的地方有2處:寫命令和寫扇區數據,因為這兩處發送的字節數比較多。一些SD卡的起始、結束、應答等單字節的數據傳輸使用的還是非DMA方式傳輸。下面是部分程序:


可以看到,速度提升非常明顯。數據和指令都用DMA傳輸時,速度最快。如果再進行一些底層函數的優化,速度還會有提升。
最后我們對讀取速度也進行了測試,使用DMA方式,使能DMA讀扇區數據和發送指令,測試結果如下,讀取速度可以達到1.1MB~1.2MB/S。

3.總結
SPI+DMA的方式讀寫SD卡速度優勢明顯,推薦使用。當然,這跟非DMA方式的底層函數效率低下有很大的關系。
但DMA的另一個更重要的優勢在于,讀寫數據時可以大部分釋放CPU資源。比如我之前的一個應用,需要以1KHz的頻率在外部中斷中去讀取一些數據,每次大約需要幾十uS。如果使用非DMA方式,頻繁的中斷,且幾十uS時間也不短,會導致SD卡寫入出錯。而使用DMA方式則不會有這個問題。
STM32CubeMX系列教程
單片機通過WIFI模塊(ESP8266)獲取網絡時間與天氣預報
串口接收不定長數據的幾種方法
不會寫Bootloader?看這里,現成的!
UART波特率對時鐘精度的要求有多高?
Keil調試時設置斷點的高級用法