找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開(kāi)始

搜索
查看: 18729|回復(fù): 3
收起左側(cè)

關(guān)于STM32F0 MCU 多通道ADC的DMA傳輸話題

[復(fù)制鏈接]
ID:98618 發(fā)表于 2015-12-7 03:17 | 顯示全部樓層 |閱讀模式
整理: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ò)疏 歡迎賜教】

回復(fù)

使用道具 舉報(bào)

ID:263487 發(fā)表于 2017-12-19 09:22 | 顯示全部樓層
發(fā)現(xiàn)STM32L0的HAl庫(kù)(STM32Cube_FW_L0_V1.10.0)中出現(xiàn)了一個(gè)問(wèn)題:
如果將以下的DMA配置過(guò)程中在定義內(nèi)存數(shù)據(jù)對(duì)齊寬度改了,然后編譯,燒錄后發(fā)現(xiàn)出錯(cuò)。那以后就再改不回來(lái)了。
DmaHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
DmaHandle.Init.MemDataAlignment    = DMA_MDATAALIGN_WORD;
比如講上述的DMA_PDATAALIGN_WORD改為DMA_PDATAALIGN_HALFWORD,然后即使再改回DMA_PDATAALIGN_WORD,也是出錯(cuò)。
回復(fù)

使用道具 舉報(bào)

ID:192996 發(fā)表于 2019-11-29 13:35 | 顯示全部樓層
哇 好人啊 遇到這個(gè)問(wèn)題搞一天,現(xiàn)在豁然開(kāi)朗,終歸還是基礎(chǔ)不扎實(shí)!
回復(fù)

使用道具 舉報(bào)

ID:85740 發(fā)表于 2021-11-24 16:02 | 顯示全部樓層
LZQ0056 發(fā)表于 2017-12-19 09:22
發(fā)現(xiàn)STM32L0的HAl庫(kù)(STM32Cube_FW_L0_V1.10.0)中出現(xiàn)了一個(gè)問(wèn)題:
如果將以下的DMA配置過(guò)程中在定義內(nèi)存 ...


現(xiàn)在最新版是1.7
這個(gè)問(wèn)題很正常,cubeide經(jīng)常會(huì)出現(xiàn)修改了代碼無(wú)法生效的情況
clean無(wú)效
rebuild無(wú)效
最后我摸索出來(lái)的辦法是,在資源管理器中 ,把鎖有和工程配置有關(guān)的都刪除
包含不限于 Debug文件夾
.settings文件夾
.mxoroject文件
.launch文件
然后重新運(yùn)行仿真,你會(huì)發(fā)現(xiàn),問(wèn)題解決了
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表