某STM32用戶開發產品,用到ADC模塊,通過定時器更新事件觸發AD轉換,轉換結果由DMA搬運到指定的內存區域。DMA工作在正常模式(即非循環模式),每當傳輸完畢一批數據后在傳輸完成中斷里設置傳輸結束標志,應用代碼對該標志進行監視。
當檢查到該有效標志時,說明采集到了預定的轉換數據。將數據處理后,軟件產生TIMER更新事件,以保證計數器從0開始計數【注:這里選用的向上計數模式】。然后清除更新事件標志、ADC轉換完成標志位EOC ,關閉DMA后對DMA進行再配置,然后重新使能DMA進行第二次傳輸。
調試中發現,對于第二次DMA傳輸,每次一使能DMA 就立即搬運一個數據。按理說應該延時一個定時器更新周期后才會搬運首次數據才對。因為軟件置位UG位后,用來觸發ADC的TIMer是從0開始計數的,需要計數到溢出才會觸發AD轉換。他想不明白的是TIM已經復位從0開始計數了,該清的標志位都清除了,還有什么原因導致DMA不等TIMER觸發就立即先行搬運一個數據呢。
該問題源于某STM32論壇,但用戶沒有貼出任何代碼。這里模擬他的應用場景做個測試驗證,并試圖找出相關原因。
我這里也設計了兩輪DMA傳輸,照樣使用TIMER更新事件觸發ADC轉換。第一輪DMA傳輸傳輸3個AD轉換結果到某內存地址,第二輪傳輸5個轉換結果到另一內存位置。
先使用Stm32CubeMx基于STM32F411Discovery板進行基本的初始化配置。配置都很簡單。
ADC配置,這里只選擇1個常規通道用于測試,選擇TIM2的觸發輸出啟動AD轉換,并開啟ADC的DMA傳輸功能,DMA工作在Normal模式。【硬件上ADC輸入通道我直接連VDD了】
TIMER配置,這里選擇TIM2,其更新事件做為觸發輸出用來啟動ADC。
配置完畢后生成初始化代碼,然后添加用戶代碼。
這里準備了幾個內存變量.
我在第一次DMA傳輸完成后立即關閉定時器,在開啟第二輪DMA傳輸前,不讓定時器有機會再次觸發ADC產生EOC事件。看看有無他說到的情形發生。
我把用戶代碼分成兩部分,分別用紅框、綠框區分。
第一部分由基本的初始化函數、開啟ADC外設及其DMA功能、對第一次DMA傳輸做配置并使能DMA、等待3次ADC轉換結束。
第二部分代碼的功能主要關閉定時器、關閉DMA,第二次對DMA進行配置,再開啟DMA功能并啟動定時器。【我把斷點打在箭頭所指的地方,即待啟動計數器的那句代碼處】
基于上述代碼測試,沒有發現一使能第二次DMA傳輸就先傳一個數據的現象。這時定時器也沒被啟動,DMA處于就緒待命狀態。【結果如下圖】
那客戶反饋的情況到底是怎么回事呢?
因為沒見到用戶具體的代碼,他說過在DMA做完第一次傳輸后,還對定時器做了復位。那我們不妨在第一次DMA傳輸結束后,增加對定時器的復位操作,看看結果會怎么樣。
我將第二部分代碼稍作修改如下【見下圖中A處代碼】:
基于調整過的代碼進行測試,還真發現了一使能第二次DMA傳輸時就先傳一個數據的現象。可是此時定時器仍未啟動,DMA怎么就開始傳輸數據了呢。【結果如下圖所示】
當然,單純從DMA傳輸功能來講,它跟定時器是否啟動并沒有必然聯系。對于被使能了的DMA,只要有合適的DMA請求出現,它就行使職能。具體到這里,應該是有EOC事件出現了才會發生DMA傳輸的。那這個EOC事件從哪里來的呢?
我們不妨先理一理:
第一次DMA傳輸完成后不可能還有待處理EOC事件存在。在第一次DMA傳輸過程中,每次DMA讀取ADC數據就保證EOC被清零了,DMA傳輸完成后又立即關閉了定時器,本案例里也沒有別的事情影響定時器的迅速關閉。按理說在兩次DMA傳輸之間不會有定時器更新事件觸發AD轉換,更何況在使能第二次DMA前還專門做了EOC的清除操作。
看起來的確有點奇怪,怎么感覺有個DMA請求,用客戶的話說,好像潛伏在哪里一樣?
目前的代碼跟剛開始的比,多了個定時器的復位操作。難道這個復位操作會導致ADC轉換而生成EOC事件?說到這,它還真有這本事。
因為軟件方式對定時器進行復位也可以產生更新事件,它正好能啟動AD轉換【AD轉換功能一直都沒關閉過】從而產生EOC事件。如果EOC標志沒有及時清除的話,就可以在下次DMA傳輸剛被使能,即使計數器還沒被啟動的條件下觸發一次DMA傳輸。
分析到這里,感覺找到問題原因了。但是,似乎還是有點不對勁。因為即使定時器復位動作產生更新事件而觸發ADC轉換,進而產生EOC事件, 但我們在定時器復位動作之后還特意做過對EOC標志的清除。【下圖中的第二個紅圈內的代碼】
難道說這個清除EOC標志的操作有問題?
先確認代碼寫法本身,沒有問題。再看邏輯和時序上問題。
通過進一步的調試,在下圖所示代碼處放了3個斷點單步運行,的確發現定時器復位事件觸發了ADC轉換,EOC被置位。在后續代碼中也發現EOC被清零了。有意思的是,當開著下圖所示3個斷點來運行時,那個奇怪的現象就消失了,那潛伏的DMA請求似乎遁形了。
如果取消上面的第1、第2個斷點后運行代碼,那個現象立即又重現,潛伏的又激活了。
反復驗證到這里,基本上明白是怎么回事了。
毫無疑問,定時器的復位操作導致AD轉換而產生了EOC事件。代碼里雖然有對EOC的清除操作,但該操作相對ADC而言,太早了點。即在針對EOC做刪除操作時,ADC可能還在忙著轉換,離產生EOC事件還早呢。這正好可以解釋為什么在復位操作代碼后放個斷點再刪除EOC就有效的情形。
既然這樣,我在清除EOC操作代碼的前面加一句EOC標志查詢等待,以保證后續的清除操作可靠有效。我將代碼再次做了調整。見下圖中方框內代碼。
就修改過的代碼進行驗證,那個現象徹底消失。后續的第二輪DMA傳輸也規規矩矩了。
到此,本應用案例分享結束。最后,稍作小結并做些提醒:
1、針對STM32定時器的軟件復位操作可以產生更新事件,其效果等同于定時器溢出導致的更新事件。
2、我們編寫代碼,尤其這種嵌入式代碼時,除了保證代碼基本的正常邏輯外,各個硬件本身操作時序、響應時間參數等也須多加關注。
3、結合本案例,在第一次DMA傳輸完成后為第二次DMA做準備時,建議先關閉計數器,否則可能會給我們的應用帶來些隱患,本案例中探討的問題,就是其中隱患之一。限于篇幅和主題,這里就不啰嗦了,后面若有合適案例再行交流。
-
adc
+關注
關注
97文章
6302瀏覽量
542488 -
定時器
+關注
關注
23文章
3218瀏覽量
113711 -
dma
+關注
關注
3文章
552瀏覽量
99954
原文標題:DMA觸發請求異常之案例分享
文章出處:【微信號:stmcu832,微信公眾號:茶話MCU】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論