ADC To DMA

ADC+DMA

下面來講一下STM32的ADC應用。

先閒扯一點其他事情,是我自己的理解。
STM32的優點在哪裏?
除去宣傳環節,細細分析。
STM32時鐘不算快,72MHZ,
也不能擴展大容量的RAM FLASH,
同樣沒有DSP那樣強大的指令集。
它的優勢在哪裏呢?
---就在快速採集數據,快速處理上。
ARM的特點就是方便。
這個快速採集,高性能的ADC就是一個很好的體現,
12位精度,最快1uS的轉換速度,通常具備2個以上獨立的ADC控制器,
這意味着,
STM32可以同時對多個模擬量進行快速採集,
這個特性不是一般的MCU具有的。
以上高性能的ADC,配合相對比較塊的指令集和一些特色的算法支持,
就構成了STM32在電機控制上的強大特性。

好了,正題,怎末做一個簡單的ADC,注意是簡單的,
ADC是個複雜的問題,涉及硬件設計,電源質量,參考電壓,信號預處理等等問題。
我們只就如何在MCU內完成一次ADC作討論。

談到ADC,我們還要第一次引入另外一個重要的設備DMA.
DMA是什麼東西呢。
通常在8位單片機時代,很少有這個概念。
在外置資源越來越多以後,
我們把一個MCU內部分爲 主處理器 和 外設兩個部分。
主處理器當然是執行我們指令的主要部分,
外設則是 串口 I2C ADC 等等用來實現特定功能的設備

回憶一下,8位時代,我們的主處理器最常乾的事情是什麼?
邏輯判斷?不是。那才幾個指令
計算算法?不是。大部分時候算法都很簡單。
事實上,主處理器就是作個搬運工,
把USART的數據接收下來,存起來
把ADC的數據接收下來,存起來
把要發送的數據,存起來,一個個的往USART裏放。
…………
爲了解決這個矛盾,
人們想到一個辦法,讓外設和內存間建立一個通道,
在主處理器允許下,
讓外設和內存直接 讀寫,這樣就釋放了主處理器,
這個東西就是DMA。

打個比方:
一個MCU是個公司。
老闆就是主處理器
員工是外設
倉庫就是內存
從前 倉庫的東西都是老闆管的。
員工需要原料工作,就一個個報給老闆,老闆去倉庫裏一個一個拿。
員工作好的東西,一個個給老闆,老闆一個個放進倉庫裏。
老闆很累,雖然老闆是超人,也受不了越來越多的員工和單子。
最後老闆僱了一個倉庫保管員,它就是DMA
他專門負責 入庫和出庫,
只需要把出庫 和入庫計劃給老闆過目
老闆說OK,就不管了。
後面的入庫和出庫過程,
員工只需要和這個倉庫保管員打交道就可以了。


--------閒話,馬七時常想,讓設備與設備之間開DMA,豈不更牛X

比喻完成。
ADC是個高速設備,前面提到。
而且ADC採集到的數據是不能直接用的。即使你再小心的設計外圍電路,測的離譜的數據總會出現。
那麼通常來說,是採集一批數據,然後進行處理,這個過程就是軟件濾波。
DMA用到這裏就很合適。讓ADC高速採集,把數據填充到RAM中,填充一定數量,比如32個,64個
MCU再來使用。
-----多一句,也可以說,單次ADC毫無意義。



下面我們來具體介紹,如何使用DMA來進行ADC操作。

初始化函數包括兩部分,DMA初始化和 ADC初始化

我們有多個管理員--DMA
一個管理員當然不止管一個DMA操作。所以DMA有多個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);    //開啓DMA1的第一通道
        DMA_InitStruct.DMA_PeripheralBaseAddr = ADC1_DR_Address;   //DMA對應的外設基地址,這個地址走Datasheet查
        DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //轉換結果的數據大小
        DMA_InitStruct.DMA_MemoryBaseAddr = (unsigned long)&ADC_ConvertedValue; //
        DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;   //DMA的轉換模式是SRC模式,就是從外設向內存中搬運,
        DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;         //M2M模式禁止,memory to memory,這裏暫時用不上,以後介


        DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;  //DMA搬運的數據尺寸,注意ADC是12位的,

HalfWord就是16位
        DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Disable;          //接收一次數據後,目標內存地址是否後移--重

要概念,用來採集多個數據的
        DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //接收一次數據後,設備地址是否後移
        DMA_InitStruct.DMA_Mode  = DMA_Mode_Circular;  //轉換模式,循環緩存模式,常用,M2M果果開啓了,這個模式失效


        DMA_InitStruct.DMA_Priority = DMA_Priority_High; //DMA優先級,高
        DMA_InitStruct.DMA_BufferSize = 1;  //DMA緩存大小,1個
        DMA_Init(DMA1_Channel1,&DMA_InitStruct);

        // Enable DMA1
        DMA_Cmd(DMA1_Channel1, ENABLE);
}


void ADCx_Init(unsigned char ADC_Channel)
{
        ADC_DeInit(ADC1);     //開啓ADC1
        ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;  //轉換模式,爲獨立轉換。轉換模式太多了,以後深究
        ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; //對齊方式,ADC結果是12位的,顯然有個對齊左邊還是右邊

的問題。一般是右對齊
        ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;   //連續轉換模式開啓
        ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;  //ADC外部出發開關,關閉
        ADC_InitStruct.ADC_NbrOfChannel = 2;  //開啓通道數,2個
        ADC_InitStruct.ADC_ScanConvMode = ENABLE;  //掃描轉換模式開啓
        ADC_Init(ADC1, &ADC_InitStruct);

        ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_239Cycles5);  //規則組通道設置,關鍵函數 轉

換器ADC1,選擇哪個通道channel,規則採樣順序,1到16,以後解釋詳細含義,最後一個參數是轉換時間,越長越準越穩定

        // ADC1 to DMA, Enable
        ADC_DMACmd(ADC1, ENABLE); //ADC命令,和DMA關聯。

        //ADC1 Enable
        ADC_Cmd(ADC1,ENABLE); //開啓ADC1

        //Reset the Calibration of ADC1
        ADC_ResetCalibration(ADC1);  //重置校準

        //wait until the Calibration's finish
        while(ADC_GetResetCalibrationStatus(ADC1))  //等待重置校準完成
                ;

        ADC_StartCalibration(ADC1);  //開始校準

        while(ADC_GetCalibrationStatus(ADC1))  //等待校準完成
                ;

        ADC_SoftwareStartConvCmd(ADC1, ENABLE);  //連續轉換開始,從選擇開始,MCU可以不用管了,ADC將通過DMA不斷刷新

制定RAM區
        // Attach them;
}


最後講講濾波算法
濾波的方法以後會開個專題。
特別提一下---沒有完美的濾波算法,只有合適的濾波算法。
需要綜合考慮信號特點,噪聲特點,控制對象等等,
這裏用個最簡單的濾波算法,均值濾波。
採樣16次,取平均值,吼吼,在豆皮上跳動還是蠻小的,合適,吼吼

//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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章