寧波住房和城鄉(xiāng)建設(shè)委員會(huì)官方網(wǎng)站山西網(wǎng)頁(yè)制作
SD卡是一個(gè)嵌入式中非常常用的外設(shè),可以用于存儲(chǔ)一些大容量的數(shù)據(jù)。但用單片機(jī)讀寫(xiě)SD卡速度一般都有限(對(duì)于高速SD卡,主要是受限于單片機(jī)本身的接口速度),在高速、實(shí)時(shí)數(shù)據(jù)存儲(chǔ)時(shí)可能會(huì)有影響。但具體速度可以達(dá)到多少呢,今天就來(lái)實(shí)際測(cè)試一下。
SD卡一般有兩種常用的接口SPI和SDIO,SDIO又有1線和4線之分。很多單片機(jī)沒(méi)有SDIO接口,但SPI接口就比較常用,今天主要來(lái)測(cè)試一下SPI接口讀寫(xiě)SD卡的速度,主要是寫(xiě)入速度。
測(cè)試條件:
單片機(jī):STM32L433CCT6
編譯環(huán)境:MDK 5.30+HAL庫(kù)
SD卡:32Gbit SDNAND,型號(hào):米客方德MKDV32GCL-STH
文件系統(tǒng):FatFS R0.12c
1.單純SPI接口測(cè)試(非DMA)
我們知道,想SD卡之類的Flash存儲(chǔ)器,一般都是按扇區(qū)擦除整塊數(shù)據(jù)。因此每次寫(xiě)入字節(jié)數(shù)是扇區(qū)整數(shù)倍時(shí),效率會(huì)比較高。同時(shí),每次寫(xiě)入數(shù)據(jù)時(shí),都需要先發(fā)送一些SD卡的指令,所以單次寫(xiě)入數(shù)據(jù)量越大,平均速度也就越快。了解了這些,我們就知道如何進(jìn)行測(cè)試了。
首先,SD卡底層驅(qū)動(dòng)使用的是HAL庫(kù)函數(shù),單字節(jié)讀寫(xiě),沒(méi)有任何改動(dòng)和優(yōu)化:
uint8_tSPI_ReadWriteByte(uint8_t TxData)
{ uint8_t RxData = 0;HAL_SPI_TransmitReceive(&hspi3,&TxData,&RxData,1,100);return RxData;
}
接下來(lái),我們先確定SPI和時(shí)鐘頻率多少合適,經(jīng)過(guò)測(cè)試,發(fā)現(xiàn)20MHz的時(shí)鐘頻率比較合適,10MHz時(shí)讀寫(xiě)速度會(huì)降低,再高的時(shí)鐘頻率對(duì)速度的提升也很小。因此我們這里用20MHz的時(shí)鐘。
然后我們分別測(cè)試單次寫(xiě)入4KB、8KB、16KB時(shí)的速度為多少,測(cè)試結(jié)果如下:

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

我們進(jìn)行了兩種測(cè)試:只使能DMA寫(xiě)扇區(qū)數(shù)據(jù),以及使能DMA寫(xiě)扇區(qū)數(shù)據(jù)和發(fā)送指令。都是按照單次寫(xiě)入16KB進(jìn)行測(cè)試,測(cè)試結(jié)果如下:

可以看到,速度提升非常明顯。數(shù)據(jù)和指令都用DMA傳輸時(shí),速度最快。如果再進(jìn)行一些底層函數(shù)的優(yōu)化,速度還會(huì)有提升。
最后我們對(duì)讀取速度也進(jìn)行了測(cè)試,使用DMA方式,使能DMA讀扇區(qū)數(shù)據(jù)和發(fā)送指令,測(cè)試結(jié)果如下,讀取速度可以達(dá)到1.1MB~1.2MB/S。

3.總結(jié)
SPI+DMA的方式讀寫(xiě)SD卡速度優(yōu)勢(shì)明顯,推薦使用。當(dāng)然,這跟非DMA方式的底層函數(shù)效率低下有很大的關(guān)系。
但DMA的另一個(gè)更重要的優(yōu)勢(shì)在于,讀寫(xiě)數(shù)據(jù)時(shí)可以大部分釋放CPU資源。比如我之前的一個(gè)應(yīng)用,需要以1KHz的頻率在外部中斷中去讀取一些數(shù)據(jù),每次大約需要幾十uS。如果使用非DMA方式,頻繁的中斷,且?guī)资畊S時(shí)間也不短,會(huì)導(dǎo)致SD卡寫(xiě)入出錯(cuò)。而使用DMA方式則不會(huì)有這個(gè)問(wèn)題。
驅(qū)動(dòng)程序:
https://download.csdn.net/download/zhang062061/87554323