整理:MilerShao 某日,一深圳客戶在用STM32F0芯片開(kāi)發(fā)新產(chǎn)品,其中用到7個(gè)ADC通道,并將AD轉(zhuǎn)換的值通過(guò)DMA傳輸?shù)揭粋(gè)內(nèi)存數(shù)組里。他反映如果單通道ADC并啟用DMA,數(shù)據(jù)傳輸都很正確,但當(dāng)啟用多通道ADC并啟用DMA傳輸時(shí),發(fā)現(xiàn)數(shù)據(jù)亂了套,結(jié)果是第一個(gè)數(shù)據(jù)特別大,后面的數(shù)據(jù)多數(shù)為0。后來(lái)通過(guò)網(wǎng)上了解到,貌似不少人栽在這里,這里盡力分享交流下。
在聊這個(gè)問(wèn)題前,不妨插入兩個(gè)小話題。 一、 記得有一次有個(gè)工程師在用STM8S芯片的ADC,跟我說(shuō)ADC的值當(dāng)輸入電壓較低時(shí)數(shù)據(jù)很準(zhǔn)很正確,可當(dāng)輸入電壓高到一定范圍時(shí),數(shù)據(jù)反而變小,似乎并無(wú)章法。查看其代碼,其AD使用的是10bit,可他ADC處理函數(shù)返回的數(shù)據(jù)卻8bit的。既然這樣,當(dāng)ADC數(shù)據(jù)大過(guò)255時(shí)要正確就怪了。 二、 還記得某工程師用STM32F1的芯片開(kāi)發(fā)產(chǎn)品時(shí)問(wèn)我,是不是用HSI的話,UART波特率就上不了115200。我告知他一般來(lái)說(shuō),輕松能上。后來(lái)細(xì)查其代碼,他不知何時(shí)把那個(gè)存放UART波特率的數(shù)據(jù)變量定義為16位寬度了,既然這樣最高波特率就過(guò)不了64K。
好,回到今天的多通道ADC的DMA傳輸話題。
其實(shí),關(guān)于stm32 多通道ADC的DMA傳輸,ST官方在其傳統(tǒng)外設(shè)固件庫(kù)或CUBE工程固件庫(kù)里都有現(xiàn)存的項(xiàng)目工程。兩個(gè)庫(kù)的例程我用基于STM32F072的牛客板【NUCLEO】做了測(cè)試,都可以正常使用。出現(xiàn)上面工程師提到的問(wèn)題,是因?yàn)槠溆嘘P(guān)數(shù)據(jù)寬度配置不一致導(dǎo)致的誤解和誤判。 因?yàn)榉从吃搯?wèn)題的客戶是基于ST官方的CUBE庫(kù)做的。這里就基于cube工程示例項(xiàng)目交流。相關(guān)工程位置如下: \STM32Cube\Repository\STM32Cube_FW_F0_V1.3.0\Projects\STM32F072RB-Nucleo\Examples\ADC\ADC_Sequencer
例程項(xiàng)目用到3個(gè)AD通道。在有關(guān)ADC配置的地方,可以看出其ADC轉(zhuǎn)換的數(shù)據(jù)分辨率為12位,數(shù)據(jù)右對(duì)齊,多通道ADC的掃描方向是從小往大,即FORWARD方向掃描。 
從有關(guān)DMA配置代碼可以看出,DMA拾取、送達(dá)兩端的數(shù)據(jù)對(duì)齊寬度均為半字即16bit;數(shù)據(jù)從外設(shè)搬到存儲(chǔ)器;內(nèi)存地址遞增方式; 
從官方例程里可以看到一個(gè)3元素的數(shù)組用來(lái)存儲(chǔ)3個(gè)AD通道轉(zhuǎn)換值,數(shù)組元素的數(shù)據(jù)寬度為16位,即半字uint16_t 。 #define ADCCONVERTEDVALUES_BUFFER_SIZE ((uint32_t) 3) /* Variable containing ADC conversions results */ __IO uint16_t aADCxConvertedValues[ADCCONVERTEDVALUES_BUFFER_SIZE];

按照上面的條件進(jìn)行編譯調(diào)試,查看數(shù)組里的轉(zhuǎn)換結(jié)果,并無(wú)發(fā)現(xiàn)異常。既沒(méi)有第一個(gè)數(shù)據(jù)特別大,也沒(méi)有后面數(shù)據(jù)為0的異象。分別是3個(gè)16位數(shù)據(jù)0x0803,0x06b2,0x05e5,每個(gè)數(shù)組變量對(duì)應(yīng)一個(gè)AD通道的轉(zhuǎn)換值。【后面也會(huì)用到這幾個(gè)數(shù)據(jù),因?yàn)槭菍?shí)時(shí)調(diào)試截圖,數(shù)據(jù)可能些差異,先行忽略】 如果把上面存放ADC數(shù)據(jù)的數(shù)組變量數(shù)據(jù)寬度由16位改為32位,即U16改為U32,其它不變,再來(lái)看看結(jié)果。如下圖所示:
 呵呵,貌似異象出現(xiàn)了。 數(shù)組里的數(shù)據(jù)出現(xiàn)跟第一種情況明顯不同的布局,數(shù)組第一個(gè)數(shù)據(jù)的確是特別大,數(shù)組第2個(gè)數(shù)據(jù)擺放的似乎并非程序猿所希望的。結(jié)合上面的測(cè)試結(jié)果,大致可以看出該數(shù)組的第2個(gè)數(shù)據(jù)是處在第3個(gè)轉(zhuǎn)換次序的AD通道的轉(zhuǎn)換值,第3個(gè)數(shù)組數(shù)據(jù)里空空如也,是0。通過(guò)兩次實(shí)驗(yàn)的比較不難發(fā)現(xiàn),第二種情形下的半字?jǐn)?shù)據(jù),除了0值外,跟第一種情形里的數(shù)據(jù)是一樣的,只是在數(shù)組元素的位置有變動(dòng)。
看到這里估計(jì)有人已經(jīng)明白怎么回事了。DMA傳輸?shù)臄?shù)據(jù)寬度跟數(shù)組定義的存儲(chǔ)寬度不一致導(dǎo)致誤會(huì)。其實(shí)各通道ADC的值并沒(méi)有錯(cuò)【從上面實(shí)驗(yàn)也可以看出】,第2種情況只是把兩個(gè)16位寬的ADC值放到一個(gè)32位寬的數(shù)組元素里。如果此時(shí)簡(jiǎn)單地把每個(gè)數(shù)組變量里的數(shù)據(jù)當(dāng)做單個(gè)通道轉(zhuǎn)換過(guò)來(lái)的值就是天大的誤解了,因?yàn)?/span>每個(gè)數(shù)組變量存放的數(shù)據(jù)跟單個(gè)通道的ADC值不存在一一對(duì)應(yīng)關(guān)系了。
上面客戶的問(wèn)題就是出在這里。為了避免類似誤解和麻煩,DMA配置過(guò)程中在定義內(nèi)存數(shù)據(jù)對(duì)齊寬度時(shí)最好與存放AD轉(zhuǎn)換值的存儲(chǔ)變量用同類型的數(shù)據(jù)寬度。 細(xì)心的人還可以發(fā)現(xiàn),在ST CUBE庫(kù)例程代碼里的DMA相關(guān)配置代碼后面還特意跟了一句注釋:

/* Transfer to memory by half-word to match with buffer variable type: half-word */ 這樣做的目的主要是方便后面對(duì)ADC轉(zhuǎn)換數(shù)據(jù)的讀取及后續(xù)計(jì)算,并不是說(shuō)數(shù)組存儲(chǔ)變量的數(shù)據(jù)寬度定義跟DMA傳輸數(shù)據(jù)寬度不一致就一定錯(cuò)了。 對(duì)于DMA而言,它只關(guān)心數(shù)據(jù)要存放的起始地址、自己每次搬運(yùn)數(shù)據(jù)的寬度、單輪循環(huán)搬運(yùn)的次數(shù)就行。至于緩沖區(qū)存放數(shù)據(jù)的變量類型怎么樣它并不關(guān)心。 下面是我將存放數(shù)據(jù)的數(shù)組變量的數(shù)據(jù)寬度改為8位、數(shù)組元素改為6,其它不動(dòng)的測(cè)試結(jié)果。【高位地址對(duì)應(yīng)高位數(shù)據(jù)字節(jié)】 
顯然,當(dāng)把數(shù)組變量類型定義為U8時(shí),DMA將來(lái)自AD轉(zhuǎn)換來(lái)的每個(gè)16位源數(shù)據(jù)分別放在2個(gè)8位數(shù)組變量空間。跟上面第二種情形一樣,每個(gè)數(shù)組變量并不對(duì)應(yīng)一個(gè)AD轉(zhuǎn)換值,而是每個(gè)AD值分兩個(gè)數(shù)組元素?cái)[放。但從AD變換或DMA傳輸而言,并沒(méi)有出錯(cuò),結(jié)果都是正確的。 下圖是直接給DMA一個(gè)目標(biāo)地址,然后在內(nèi)存區(qū)查看ADC轉(zhuǎn)換出來(lái)的數(shù)據(jù)。【高位地址對(duì)應(yīng)高位數(shù)據(jù)字節(jié)】結(jié)果也是正常的。

最后順便提下,STM32F0的多通道AD掃描有兩個(gè)方向,一般默認(rèn)為FORWARD方向,也可以設(shè)置為BACKWARD方向。有時(shí)忽略了也可能給開(kāi)發(fā)者帶來(lái)混亂或困惑,因?yàn)橛袝r(shí)多通道,換個(gè)方向后AD值可能跟預(yù)估的大相徑庭而又無(wú)規(guī)律。
本文中的話題,包括開(kāi)頭中插入的兩個(gè)話題,看似跟AD/DMA等有關(guān),實(shí)質(zhì)上感覺(jué)跟C語(yǔ)言或其它基礎(chǔ)更為密切。
【拋磚引玉 旨在交流,如有錯(cuò)疏 歡迎賜教】 |