STM32_ADC+DMA
下面來(lái)講一下STM32的ADC應(yīng)用。
本文引用地址:http://www.ex-cimer.com/article/201612/325009.htm先閑扯一點(diǎn)其他事情,是我自己的理解。
STM32的優(yōu)點(diǎn)在哪里?
除去宣傳環(huán)節(jié),細(xì)細(xì)分析。
STM32時(shí)鐘不算快,72MHZ,
也不能擴(kuò)展大容量的RAM FLASH,
同樣沒(méi)有DSP那樣強(qiáng)大的指令集。
它的優(yōu)勢(shì)在哪里呢?
---就在快速采集數(shù)據(jù),快速處理上。
ARM的特點(diǎn)就是方便。
這個(gè)快速采集,高性能的ADC就是一個(gè)很好的體現(xiàn),
12位精度,最快1uS的轉(zhuǎn)換速度,通常具備2個(gè)以上獨(dú)立的ADC控制器,
這意味著,
STM32可以同時(shí)對(duì)多個(gè)模擬量進(jìn)行快速采集,
這個(gè)特性不是一般的MCU具有的。
以上高性能的ADC,配合相對(duì)比較塊的指令集和一些特色的算法支持,
就構(gòu)成了STM32在電機(jī)控制上的強(qiáng)大特性。
好了,正題,怎末做一個(gè)簡(jiǎn)單的ADC,注意是簡(jiǎn)單的,
ADC是個(gè)復(fù)雜的問(wèn)題,涉及硬件設(shè)計(jì),電源質(zhì)量,參考電壓,信號(hào)adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=17&is_app=0&jk=86c1906558a285a7&k=%D4%A4%B4%A6%C0%ED&k0=%D4%A4%B4%A6%C0%ED&kdi0=0&luki=3&n=10&p=baidu&q=98059059_cpr&rb=0&rs=1&seller_id=1&sid=a785a2586590c186&ssp2=1&stid=0&t=tpclicked3_hc&tu=u1831118&u=http%3A%2F%2Fwww%2E51hei%2Ecom%2Fmcu%2F1958%2Ehtml&urlid=0" id="6_nwl" mpid="6" target="_blank">預(yù)處理等等問(wèn)題。
我們只就如何在MCU內(nèi)完成一次ADC作討論。
談到ADC,我們還要第一次引入另外一個(gè)重要的設(shè)備DMA.
DMA是什么東西呢。
通常在8位單片機(jī)時(shí)代,很少有這個(gè)概念。
在外置資源越來(lái)越多以后,
我們把一個(gè)MCU內(nèi)部分為 主處理器 和 外設(shè)兩個(gè)部分。
主處理器當(dāng)然是執(zhí)行我們指令的主要部分,
外設(shè)則是 串口 I2C ADC 等等用來(lái)實(shí)現(xiàn)特定功能的設(shè)備
回憶一下,8位時(shí)代,我們的主處理器最常干的事情是什么?
邏輯判斷?不是。那才幾個(gè)指令
計(jì)算算法?不是。大部分時(shí)候算法都很簡(jiǎn)單。
事實(shí)上,主處理器就是作個(gè)搬運(yùn)工,
把USART的數(shù)據(jù)接收下來(lái),存起來(lái)
把ADC的數(shù)據(jù)接收下來(lái),存起來(lái)
把要發(fā)送的數(shù)據(jù),存起來(lái),一個(gè)個(gè)的往USART里放。
…………
為了解決這個(gè)矛盾,
人們想到一個(gè)辦法,讓外設(shè)和內(nèi)存間建立一個(gè)通道,
在主處理器允許下,
讓外設(shè)和內(nèi)存直接 讀寫,這樣就釋放了主處理器,
這個(gè)東西就是DMA。
打個(gè)比方:
一個(gè)MCU是個(gè)公司。
老板就是主處理器
員工是外設(shè)
倉(cāng)庫(kù)就是內(nèi)存
從前 倉(cāng)庫(kù)的東西都是老板管的。
員工需要原料工作,就一個(gè)個(gè)報(bào)給老板,老板去倉(cāng)庫(kù)里一個(gè)一個(gè)拿。
員工作好的東西,一個(gè)個(gè)給老板,老板一個(gè)個(gè)放進(jìn)倉(cāng)庫(kù)里。
老板很累,雖然老板是超人,也受不了越來(lái)越多的員工和單子。
最后老板雇了一個(gè)倉(cāng)庫(kù)保管員,它就是DMA
他專門負(fù)責(zé) 入庫(kù)和出庫(kù),
只需要把出庫(kù) 和入庫(kù)計(jì)劃給老板過(guò)目
老板說(shuō)OK,就不管了。
后面的入庫(kù)和出庫(kù)過(guò)程,
員工只需要和這個(gè)倉(cāng)庫(kù)保管員打交道就可以了。
--------閑話,馬七時(shí)常想,讓設(shè)備與設(shè)備之間開(kāi)DMA,豈不更牛X
比喻完成。
ADC是個(gè)高速設(shè)備,前面提到。
而且ADC采集到的數(shù)據(jù)是不能直接用的。即使你再小心的設(shè)計(jì)外圍電路,測(cè)的離譜的數(shù)據(jù)總會(huì)出現(xiàn)。
那么通常來(lái)說(shuō),是采集一批數(shù)據(jù),然后進(jìn)行處理,這個(gè)過(guò)程就是軟件濾波。
DMA用到這里就很合適。讓ADC高速采集,把數(shù)據(jù)填充到RAM中,填充一定數(shù)量,比如32個(gè),64個(gè)
MCU再來(lái)使用。
-----多一句,也可以說(shuō),單次ADC毫無(wú)意義。
下面我們來(lái)具體介紹,如何使用DMA來(lái)進(jìn)行ADC操作。
初始化函數(shù)包括兩部分,DMA初始化和 ADC初始化
我們有多個(gè)管理員--DMA
一個(gè)管理員當(dāng)然不止管一個(gè)DMA操作。所以DMA有多個(gè)Channel
//ADC with DMA Init
#define ADC_Channel ADC_Channel0
#define ADC1_DR_Address ((u32)0x4001244C)
void ADCWithDMAInit()
{
//DMA init; Using DMA channel 1
DMA_DeInit(DMA1_Channel1); //開(kāi)啟DMA1的第一通道
DMA_InitStruct.DMA_PeripheralBaseAddr = ADC1_DR_Address; //DMA對(duì)應(yīng)的外設(shè)基地址,這個(gè)地址走Datasheet查
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //轉(zhuǎn)換結(jié)果的數(shù)據(jù)大小
DMA_InitStruct.DMA_MemoryBaseAddr = (unsigned long)&ADC_ConvertedValue; //
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC; //DMA的轉(zhuǎn)換模式是SRC模式,就是從外設(shè)向內(nèi)存中搬運(yùn),
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; //M2M模式禁止,memory to memory,這里暫時(shí)用不上,以后介
紹
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //DMA搬運(yùn)的數(shù)據(jù)尺寸,注意ADC是12位的,
HalfWord就是16位
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Disable; //接收一次數(shù)據(jù)后,目標(biāo)內(nèi)存地址是否后移--重
要概念,用來(lái)采集多個(gè)數(shù)據(jù)的
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //接收一次數(shù)據(jù)后,設(shè)備地址是否后移
DMA_InitStruct.DMA_Mode = DMA_Mode_Circular; //轉(zhuǎn)換模式,循環(huán)緩存模式,常用,M2M果果開(kāi)啟了,這個(gè)模式失效
。
DMA_InitStruct.DMA_Priority = DMA_Priority_High; //DMA優(yōu)先級(jí),高
DMA_InitStruct.DMA_BufferSize = 1; //DMA緩存大小,1個(gè)
DMA_Init(DMA1_Channel1,&DMA_InitStruct);
// Enable DMA1
DMA_Cmd(DMA1_Channel1, ENABLE);
}
void ADCx_Init(unsigned char ADC_Channel)
{
ADC_DeInit(ADC1); //開(kāi)啟ADC1
ADC_InitStruct.ADC_Mode = ADC_Mode_Independent; //轉(zhuǎn)換模式,為獨(dú)立轉(zhuǎn)換。轉(zhuǎn)換模式太多了,以后深究
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; //對(duì)齊方式,ADC結(jié)果是12位的,顯然有個(gè)對(duì)齊左邊還是右邊
的問(wèn)題。一般是右對(duì)齊
ADC_InitStruct.ADC_ContinuousConvMode = ENABLE; //連續(xù)轉(zhuǎn)換模式開(kāi)啟
ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //ADC外部出發(fā)開(kāi)關(guān),關(guān)閉
ADC_InitStruct.ADC_NbrOfChannel = 2; //開(kāi)啟通道數(shù),2個(gè)
ADC_InitStruct.ADC_ScanConvMode = ENABLE; //掃描轉(zhuǎn)換模式開(kāi)啟
ADC_Init(ADC1, &ADC_InitStruct);
ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_239Cycles5); //規(guī)則組通道設(shè)置,關(guān)鍵函數(shù) 轉(zhuǎn)
換器ADC1,選擇哪個(gè)通道channel,規(guī)則采樣順序,1到16,以后解釋詳細(xì)含義,最后一個(gè)參數(shù)是轉(zhuǎn)換時(shí)間,越長(zhǎng)越準(zhǔn)越穩(wěn)定
// ADC1 to DMA, Enable
ADC_DMACmd(ADC1, ENABLE); //ADC命令,和DMA關(guān)聯(lián)。
//ADC1 Enable
ADC_Cmd(ADC1,ENABLE); //開(kāi)啟ADC1
//Reset the Calibration of ADC1
ADC_ResetCalibration(ADC1); //重置校準(zhǔn)
//wait until the Calibrations finish
while(ADC_GetResetCalibrationStatus(ADC1)) //等待重置校準(zhǔn)完成
;
ADC_StartCalibration(ADC1); //開(kāi)始校準(zhǔn)
while(ADC_GetCalibrationStatus(ADC1)) //等待校準(zhǔn)完成
;
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //連續(xù)轉(zhuǎn)換開(kāi)始,從選擇開(kāi)始,MCU可以不用管了,ADC將通過(guò)DMA不斷刷新
制定RAM區(qū)
// Attach them;
}
最后講講濾波算法
濾波的方法以后會(huì)開(kāi)個(gè)專題。
特別提一下---沒(méi)有完美的濾波算法,只有合適的濾波算法。
需要綜合考慮信號(hào)特點(diǎn),噪聲特點(diǎn),控制對(duì)象等等,
這里用個(gè)最簡(jiǎn)單的濾波算法,均值濾波。
采樣16次,取平均值,吼吼,在豆皮上跳動(dòng)還是蠻小的,合適,吼吼
//16ms finish a ADC detection
// return mv
unsigned int ADC_filter(void)
{
unsigned int result="0";
unsigned char i;
for(i=16;i>0;i--)
{
Delay_xms(1);
result += ADC_ConvertedValue;
}
return (unsigned int)(((unsigned long)(result>>4))*3300>>12);
}
評(píng)論