STM32 多通道ADC連續采集之數據到內存 DMA傳輸


      DMA的英文Direct memory access,意思就是傳輸將數據從一個地址空間復制到另一個地址空間,設置好后

自動傳輸而不需要處理器參與。STM32F030F4只有DMA1,DMA1有5個通道,要想使用必須進行一些設置。

    下面以ADC多通道采集為例,大致分3步:

     1. GPIO的配置。該配置主要設置ADC采集涉及的IO口,本例設置ADC連接9個IO口進行模擬量采集。

     2. ADC的配置。設置多通道采集速率、連續轉換模式、使能和DMA通道的連接等。注意該芯片只有ADC1。

     3. DMA的配置。設置DMA的開啟、源地址、目標地址等。該芯片只有DMA1,但有5個通道,本例用通道1。

下面是main.c代碼:

#include "stm32f0xx.h"
#include "stm32f0xx_rcc.h"
#include "stm32f0xx_gpio.h"
#include "stm32f0xx_adc.h"
#include "stm32f0xx_dma.h"

// 原版創作,引用請注明出處:https://www.cnblogs.com/beiyhs/p/12061278.html              北有寒山

// 6 腳PA0 ADC_IN0
// 7 腳PA1 ADC_IN1
// 8 腳PA2 ADC_IN2
// 9 腳PA3 ADC_IN3
// 10腳PA4 ADC_IN4
// 11腳PA5 ADC_IN5
// 12腳PA6 ADC_IN6
// 13腳PA7 ADC_IN7
// 14腳PB1 ADC_IN9   注意:STM32F030F4芯片沒有ADC_IN8通道哦!

#define N 50                         //預定義每通道采50次
#define M 9                           //預定義為9個通道

uint16_t ad_value[N][M];       //定義二維數組,用來存放ADC轉換結果,也是DMA的目標地址
uint16_t ad_avg[M];              //9個ADC通道采集50次后平均值結果,M從0-8
float adc_data[M];                 //9個ADC通道平均值轉換電壓值結果,M從0-8

void GPIO_cfg(void);            // 1. GPIO的配置函數聲明
void ADC1_cfg(void);           // 2. ADC的配置函數聲明
void DMA_cfg(void);             // 3. DMA的配置函數聲明
void delay_ms(void);            // 延時函數
void filter(void);                     // 9通道,每通道50次求平均函數

//*************************************************************************************************
void GPIO_cfg(void)
{
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); //打開A口時鍾
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|
GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;     //PA0/1/2/3/4/5/6/7/ 作為模擬通道輸入引腳
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;      //模擬輸入引腳
GPIO_Init(GPIOA, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;                //PB1 作為模擬通道輸入引腳
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;     //模擬輸入引腳
GPIO_Init(GPIOB, &GPIO_InitStructure);
}

//***************************************************************************************************************
void ADC1_cfg(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE); //使能AHB預分頻器到外設ADC1的開關
ADC_DeInit(ADC1);                                                                          //ADC復位
RCC_ADCCLKConfig(RCC_ADCCLK_PCLK_Div4) ;                      //時鍾分頻48M/4=12M 最大時鍾不超過14M

ADC_InitTypeDef ADC_InitStruct;                                                    //聲明ADC結構變量,在stm32f0xx_adc.c中
ADC_InitStruct.ADC_Resolution=ADC_Resolution_12b;                                           //采集設為12位精度即4095
ADC_InitStruct.ADC_ContinuousConvMode=ENABLE;                                             //轉換工作在連續轉換模式
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;                                          //ADC數據右對齊
ADC_InitStruct.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; //外部觸發轉換關閉 
ADC_InitStruct.ADC_ScanDirection = ADC_ScanDirection_Upward;                         //1-9升序,Backward降序
ADC_Init(ADC1,&ADC_InitStruct);                         //根據ADC_InitStruct中指定的參數初始化外設ADCx的寄存器

//設置指定ADC的規則組通道,設置它們的轉化順序和采樣時間
ADC_ChannelConfig(ADC1,ADC_Channel_0,ADC_SampleTime_55_5Cycles);
ADC_ChannelConfig(ADC1,ADC_Channel_1,ADC_SampleTime_55_5Cycles);
ADC_ChannelConfig(ADC1,ADC_Channel_2,ADC_SampleTime_55_5Cycles);
ADC_ChannelConfig(ADC1,ADC_Channel_3,ADC_SampleTime_55_5Cycles);
ADC_ChannelConfig(ADC1,ADC_Channel_4,ADC_SampleTime_55_5Cycles);
ADC_ChannelConfig(ADC1,ADC_Channel_5,ADC_SampleTime_55_5Cycles);
ADC_ChannelConfig(ADC1,ADC_Channel_6,ADC_SampleTime_55_5Cycles);
ADC_ChannelConfig(ADC1,ADC_Channel_7,ADC_SampleTime_55_5Cycles);
ADC_ChannelConfig(ADC1,ADC_Channel_9,ADC_SampleTime_55_5Cycles);
//ADC_ChannelConfig(ADC1,ADC_Channel_0, ADC_SampleTime_239Cycles5 ); //等待更長采集周期

ADC_GetCalibrationFactor(ADC1);              //校准ADC
ADC_DMACmd(ADC1, ENABLE);               //使能ADC1的DMA通道,還需獨立配置DMA通道等參數
ADC_Cmd(ADC1,ENABLE);                        //使能的ADC1
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_ADEN));   //等待ADC准備好
ADC_StartOfConversion(ADC1);                 //啟動轉換

}

//****************************************************************************************************
void DMA_cfg(void)
{
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //1 使能DMA傳輸,開啟DMA時鍾
DMA_InitTypeDef DMA_InitStructu;                                                //2 聲明DMA結構變量
DMA_DeInit(DMA1_Channel1);                                                      //3 將DMA的通道1寄存器復位
DMA_InitStructu.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; //4 DMA外設ADC基地址 源地址
DMA_InitStructu.DMA_MemoryBaseAddr = (uint32_t)&ad_value; //5 DMA目標地址,內存AD_Value首地址  
DMA_InitStructu.DMA_DIR = DMA_DIR_PeripheralSRC;              //6 內存作為數據傳輸的目的地 傳輸方向
DMA_InitStructu.DMA_BufferSize = N*M;                                       //7 DMA通道的DMA緩存的大小50*9 每次
DMA_InitStructu.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //8 外設地址寄存器不變 源地址不變
DMA_InitStructu.DMA_MemoryInc = DMA_MemoryInc_Enable;         //9 內存地址寄存器遞增 目標地址加1
DMA_InitStructu.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord; //10 ADC 數據寬度為16位
DMA_InitStructu.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;        //11 內存 數據寬度為16位
DMA_InitStructu.DMA_Mode = DMA_Mode_Circular;                         //12 工作在循環緩存模式/Normal正常轉換
DMA_InitStructu.DMA_Priority = DMA_Priority_High;                          //13 DMA通道 高優先級 Medium 中優先
DMA_InitStructu.DMA_M2M = DMA_M2M_Disable;                            //14 DMA通道 關閉內存到內存傳輸
DMA_Init(DMA1_Channel1, &DMA_InitStructu);                       //15 據DMA_InitStruct指定參數初始化DMA通道1

DMA_Cmd(DMA1_Channel1, ENABLE);                                              //16 啟動DMA通道

}

//***********************************************************************************************
void filter(void)
{
int sum = 0;                                              //每通道累加和變量
uint8_t count_M=0;                                  //通道數變量
uint8_t count_N=0;                                   //次數變量

for( count_M=0;count_M<M;count_M++)                                                   //M為9列,就是通道數,從0-9
                                   {
                                     for(count_N=0;count_N<N;count_N++)                 //N為50行,就是次數,從0-50
                                                      {
                                                       sum += ad_value[count_N][count_M]; //通道50次ADC值求和
                                                       }
                                     ad_avg[count_M]=sum/N;                                      //通道50次ADC值求平均
                                     sum=0;                                                               //完成通道后清空和值,進入下一輪通道
                                     }
}
//*******************************************************************************************
void delay_ms(void)
{
unsigned char i,j;
for(i=255;i>0;i--)
    {
     for(j=200;j>0;j--);
     }
}

//**********************************************************************************************
//**********************************************************************************************
int main(void)
{
GPIO_cfg();                                              //初始化9個ADC的IO口
ADC1_cfg();                                              //初始ADC
DMA_cfg();                                               //初始DMA

while(1)                                                     //主循環

{
delay_ms();                                               //延時等待9通道50次采集結束
filter();                                                        //ADC0-9通道50次采集求平均

adc_data[0]=(ad_avg[0]*3.263)/4095;       //PA0 ADC通道0平均后轉換電壓值
adc_data[1]=(ad_avg[1]*3.263)/4095;       //PA1 ADC通道1平均后轉換電壓值
adc_data[2]=(ad_avg[2]*3.263)/4095;       //PA2 ADC通道2平均后轉換電壓值
adc_data[3]=(ad_avg[3]*3.263)/4095;       //PA3 ADC通道3平均后轉換電壓值
adc_data[4]=(ad_avg[4]*3.263)/4095;       //PA4 ADC通道4平均后轉換電壓值
adc_data[5]=(ad_avg[5]*3.263)/4095;       //PA5 ADC通道5平均后轉換電壓值
adc_data[6]=(ad_avg[6]*3.245)/4095;       //PA6 ADC通道6平均后轉換電壓值
adc_data[7]=(ad_avg[7]*3.263)/4095;       //PA7 ADC通道7平均后轉換電壓值
adc_data[8]=(ad_avg[8]*3.263)/4095;       //PB1 ADC通道9平均后轉換電壓值
}

  說明:

            1.  程序在uVision開發環境仿真運行,掛載的STM32F030F4實驗板的PA0、PA1等9個通道給入模

                 擬電壓。在程序adc_data[8]=(ad_avg[8]*3.263)/4095; 等處設斷點,則可看到采集電壓值。

            2.  程序在GPIO_cfg()、ADC1_cfg()、DMA_cfg() 函數執行完后,ADC的采集已經開始並且DMA

                通道已經打開。這時ADC執行通道0-9的連續循環采集,但采集值存放的地址不變只有一個,每

                次采集完由DMA傳送到內存的二維數組ad_value[N][M],每采集DMA傳輸一次,數組從首地址

                開始加1,直到N*M=9*50個地址用完,DMA重新從ad_value[N][M]首地址再傳,周而復始。

            3.  ADC通過DMA傳輸采集值到內存過程,不需要處理器干預,可以騰出資源處理別的事物。上例

                 只是在主環里面需要轉換電壓值時,從數組讀取數據、求平均並轉換,需要處理器計算。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM