一個因初始化順序而導致異常的話題 有STM32用戶反映,他使用STM32F4系列芯片進行開發,通過STM32CubeMX配置初始化代碼,使用了UART的DMA傳輸。但他發現DMA根本不工作。后來他無意中發現,是因為他在用戶代碼里不經意地調整過UART外設和DMA外設初始化代碼的前后順序,當他重新調整二者的先后順序后就一切正常了。他想知道這個順序是怎么影響DMA功能的。 筆者順手拿了塊STM32F334的NUCLEO板,開啟UART1/UART3的數據通信功能,使用DMA進行數據的循環傳輸。UART1 發送數據,UART3接收數據,均配置在異步雙工模式;STM32CubeMX配置后生成初始化代碼,添加用戶代碼。如下圖所示: file:///C:\Users\Administrator.WIN-STED6B9V5UI\AppData\Local\Temp\ksohtml14452\wps5.jpg 經測試驗證,發現基于UART1/3的DMA傳輸功能是正常的。 結合客戶的反饋,我將DMA與UART初始化順序前后調換下,如下圖: file:///C:\Users\Administrator.WIN-STED6B9V5UI\AppData\Local\Temp\ksohtml14452\wps6.jpg 果然發現,DMA真的不工作了,UART1/UART3之間沒有發生數據通信。通過調試發現,UART1/3的數據寄存器內容維持0值而沒有任何變化,尤其作為發送端的UART1的數據寄存器也毫無動靜。 看來,DMA和UART的初始化代碼的順序的確影響到了二者的功能。也就是說如果代碼是基于現有CubeMX生成的初始化代碼,二者的初始化順序不能隨意調整,那到底怎么回事呢? 首先自然是查看這兩個初始化代碼內容,試圖找到蛛絲馬跡。很遺憾,并未很快發現原因。當再次查看DMA初始化函數MX_DMA_Init();的具體內容時,發現其代碼其實很簡單,就兩個動作,幾行代碼而已: file:///C:\Users\Administrator.WIN-STED6B9V5UI\AppData\Local\Temp\ksohtml14452\wps7.png 一個開啟DMA外設的時鐘,另一個動作就是使能DMA相關的中斷矢量控制。 既然這樣,我嘗試將該DMA初始化函數體位置依然放在UART初始化代碼的后面,但將DMA初始化函數里的那句開啟DMA外設時鐘的代碼提取出來,并移到UART初始化代碼之前,據此進行驗證。這次,結果又一切正常了。 也就是說,基于現有初始化代碼,這個DMA時鐘的開啟要放在UART初始化代碼之前,那是為什么呢?感覺UART的配置跟DMA時鐘并沒有啥關系啊。 再回頭細看UART的初始化代碼,在UART初始化函數的一個子函數HAL_UART_MspInit()那里發現了端倪。 MX_USART1_UART_Init()==》 HAL_UART_Init()==》 HAL_UART_MspInit(); 因為我們開啟了跟UART傳輸事件相關的DMA功能,在UART_MspInit();函數里不僅有對與UART相關的GPIO的復用功能配置,還有跟UART事件有關的DMA配置?磥UART的初始化還是跟DMA有關聯的。【見如下截圖】 file:///C:\Users\Administrator.WIN-STED6B9V5UI\AppData\Local\Temp\ksohtml14452\wps8.png 結合前面提到的DMA初始化函數里的那句開啟DMA外設時鐘代碼,到這里基本明白怎么回事了。 因為我們在UART初始化代碼里要做跟DMA有關的配置,如果不事先將DMA外設的時鐘開啟,【UART初始化函數里也沒有開啟DMA外設時鐘的代碼】,那么,在UART初始化代碼進行有關DMA的配置操作就沒法保證有效。 到此,開篇中提到的因為DMA和UART初始化代碼順序影響DMA功能的原因應該說揭曉了。 在做嵌入式開發過程中,很多的初始化配置都是基于硬件本身的,有些初始化順序往往有硬件方面的配置時序要求,關于這些各個芯片手冊中一般都會有描述和說明。我們在編寫初始代碼時須遵循相關規定或約定。當然,有些配置順序可能還得結合具體應用,實際體會后而做靈活調整。 具體到文中案例,本來STM32CubeMX在生成初始化代碼時已經考慮到初始化時序這點了,只是用戶在整理代碼過程中無意調整了二者的初始化順序而不自知,再加上我們對CubeMX生成的初始化代碼本身往往缺乏足夠的熟悉度,在開發過程中可能會因此陷入一時的困擾。關于這點,相信未來版本的STM32CubeMX會做進一步的調整與完善。 |