STM32 通過USART+DMA收發,更深刻地理解DMA能有多省時間!!

閱讀全篇約花兩三分鐘, 可以先上杯茶, 慢慢細看~~~

玩STM32好幾年了,常用DMA傳輸數據,好給MCU省下資源去幹幹其它的事。

理念中,DMA = 又省又快!這是被書本灌輸的知識。到底有多快有多少省呢?今天手癢,寫了個測試代碼,結果還真有些驚喜!

將分別測試USART通過printf、DMA兩種方式,測試輸出數據(TX)時,在耗時、速度上的差別。

目錄

一、測試環境

二、測試用代碼

        1、代碼三處要點

        2、普通發送printf()函數,  fputc的重定向代碼

        3、DMA配置代碼,print()函數

三、運行耗時 - 測試結果

        1、直接數據

        2、理解思考

四、邏輯分析儀 - 測試結果

        1、計算分析

        2、疑問



一、測試環境

硬件:  F103C8 (還是這個大愛的魔女開發板,賊好用)

軟件:  Keil_5.27(5.27!!!)

上位機:  秉火串口調試助手(V1.0最好用)

邏輯分析軟件:  pulseview(開源,界面和設置都很簡潔)


二、測試用代碼

1、代碼三處要點

  • 紅點處:自定義的運行時長觀察函數,放置在3個地方,獲取期間兩段運行時長,單位是us ;
  • 黃色1: printf(), 就是平時l輸出至上位機的 printf(), 其底層fputc已重定向輸出至USART1,
  • 黃色2:print(), 注意名稱少了個f,自定義的函數, USART1通過DMA輸出參數傳遞的字符串;

 2、普通發送printf()函數,  fputc的重定向代碼

// 重定向fputc函數
// printf的輸出,指向fputc,由fputc輸出到串口
// 這裏使用串口1(USART1)輸出printf信息
int fputc(int ch, FILE *f)
{   
    // 重要判斷 
    if(xFlag.PrintfOK == 0) return 0;    // 判斷USART是否已配置,防止在配置前調用printf被卡死
	
    while((USARTx ->SR&0X40)==0);        // 等待上一次串口數據發送完成  
	USARTx ->DR = (u8) ch;      	     // 寫DR,串口1將發送數據
	return ch;
}

3、DMA配置代碼,print()函數

/******************************************************************************
 * 函  數: print
 * 功  能: UART使用DMA發送數據
 * 參  數: u8* charTemp  要發送的字符串首地址
 * 返回值:
 * 備  注: 魔女開發板團隊  資料存放Q羣:262901124        最後修改_2020年05月10日
 ******************************************************************************/  
void print(char* charTemp) 
{
    u32 num = 0;
    char* t=charTemp ;    
    while(*t++ !=0)  num++;
    
	RCC->AHBENR|=1<<0;			             // 開啓DMA1時鐘
	DMA1_Channel4->CPAR  = (u32)&USARTx->DR; // 外設地址 
	DMA1_Channel4->CMAR  = (u32)charTemp;    // 存儲器地址    
	DMA1_Channel4->CNDTR = num;    	         // 傳輸數據量
	DMA1_Channel4->CCR   = 1<<4;  		     // 數據傳輸方向   0:從外設讀   1:從存儲器讀
	DMA1_Channel4->CCR  |= 0<<5;  		     // 循環模式       0:不循環     1:循環
	DMA1_Channel4->CCR  |= 0<<6; 		     // 外設地址非增量模式
	DMA1_Channel4->CCR  |= 1<<7; 	 	     // 存儲器增量模式
	DMA1_Channel4->CCR  |= 0<<8; 	 	     // 外設數據寬度爲8位
	DMA1_Channel4->CCR  |= 0<<10; 		     // 存儲器數據寬度8位
	DMA1_Channel4->CCR  |= 0<<12; 		     // 中等優先級
	DMA1_Channel4->CCR  |= 0<<14; 		     // 非存儲器到存儲器模式	
	DMA1_Channel4->CCR  |= 1<<0;             // 開啓DMA傳輸    
} 

三、運行耗時 - 測試結果

使用printf()和print()通過USART1輸出27個字節, 然後把監察到的運行時長打印到上位機,結果如下:

1、直接數據

     數據處理的耗時相差230倍!!!

     使用DMA省99.5%的耗時!!!

2、理解思考

       printf的耗時,應該耗在每發一個字節,芯片都要while(), 一個字節挨一個字節地while()死等着發送, 計算可知115200波特率下,每一個字節要87us,  那27個字節, 它就要等待27次87us , 就是 2343us了, 加上命令處理時間, 打印中的結果2769us, 差不多是這個時間了.  注意, 這個在等人的主角是芯片, 這個時候它是不能幹其它的事情的, 除非有中斷突然出現. 特別特別地注意: 如果死等的這個時候有中斷出現, 而中斷處理時長超過87us, 那就會有另一個頭疼的問題:  USART的數據被打斷打亂!

        而print通過DMA發送,12us的耗時應該就是代碼指令操作的時間:主芯片給運輸隊伍下命令:你們去A地方,把num個紙箱,搬到B地方。幾句話, 用時12us. 

       我們再進一步: 12us是主芯片動動嘴皮下命令的時長,不是運輸隊伍工作的時長,那它需要工作多長時間呢?


四、邏輯分析儀 - 測試結果

直接上波形的運行截圖, 兩個截圖都是發送27個字節的波形時長:

 

可以看到,printf產生波型比DMA耗時, 大概多10%左右的時間, 但兩者的波型時長相差很小, 

說明儘管在輸出數據時處理方式不同, 芯片運行耗時兩者相差巨大, 但最終在硬件產生數據波型的工作中, 都得按協議走.

1、計算分析

說說printf產生波型要比DMA耗時多的理解:  上面計算過波特率115200時, 每個字節佔用10個時鐘, 計算數值是每個字節波形長86.8us,  27個字節的波型總長是2343us,  這個計算數值和在DMA傳輸時的實測數值2344us是一致的;但是使用printf的while死等輸出, 27字節的數據波形時長要2579us, 足足多了236us, 應該是每兩個字節間得處理死等的判斷代碼和命令, 造成了字節與字節間的空白浪費;

2、疑問

還有1個小問題, 暫時沒找到答案: 在使用自定義的print函數通過DMA發送數據, 200字節左右, 數據都是準確的, 但大約500字節以上時,出現頭尾的數據有失真的情況,不是偶然發生, 是每次都在同樣的位置出現小量數據錯誤, 多次排查均沒有發現問題所在, 先做個記錄, 後續有時間再放燈下挑挑.


如果有朋友需要測試中的完整代碼, 可在留言區留下郵箱.

歡迎拍磚頭!!


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章