1. 程式人生 > >STM32 多通道ADC採集詳解(DMA模式和非DMA模式)

STM32 多通道ADC採集詳解(DMA模式和非DMA模式)

ADC相關問題:

1.採集到的值如何轉化計算?

  在STM32系列晶片大都是12位只有少部分是16位的,如:F373晶片。

  12位解析度意味著我們採集電壓的精度可以達到:Vref / 4096。

   採集電壓= Vref * ADC_DR / 4096;

   VREF:參考電壓

   ADC_DR:讀取到ADC資料暫存器的值  

2.什麼是通道掃描模式?

掃描模式用於多通道採集時,一輪採集為根據通道設定的順序依次採集每個開啟的通道。但要注意的是每個通道採集到的值都是存放在暫存器ADCx-> DR中,而只有當一輪採集完即所有設定的通道都採集完後採集完成標誌才會被置位,所以不使用DMA傳輸方式的時候會出現採集到的資料被覆蓋的現象。

3.採集週期,轉換週期

轉換時間=取樣時間+12.5個時鐘週期;

取樣時間根據ADC_RegularChannelConfig()配置。

4.連續轉換模式

將所有開啟的通道採集轉換完一輪後自動開啟下一輪的採集轉換。

5.一次採集完成後會觸發中斷嗎?

ADC使能中斷的話當通道規則組都採集轉化完成一輪後會產生中斷ADC1_2_IRQHandler(void)

DMA相關問題:

1.什麼是DMA迴圈模式?

完成一輪DMA傳輸後自動開啟下一輪傳輸。

2.怎麼算一輪DMA傳輸完成?

3.ADC怎麼觸發DMA?

傳輸請求函式觸發

例如:

ADC_DMACmd(ADC1,Enable); 

4.DMA有哪些傳輸方向?

記憶體  到  記憶體

記憶體  到  外設

外設  到  記憶體

5.DMA傳輸會觸發哪些中斷?

以下為ADC多通道採集(非DMA模式和DMA模式)的示例

非MA模式(配置16個通道)

void adc_gpio_init(void)
{
    GPIO_InitTypeDef t_gpio;
   
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);//使能 GPIOA GPIOB GPIOC 時鐘
    t_gpio.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;//GPIOA
    t_gpio.GPIO_Mode=GPIO_Mode_AIN;//模擬輸入模式
    GPIO_Init(GPIOA,&t_gpio);//初始化GPIOA
    
    t_gpio.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1;//GPIOB 
    GPIO_Init(GPIOB,&t_gpio);//初始化GPIOB
    
    t_gpio.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;//GPIOC  
    GPIO_Init(GPIOC,&t_gpio);//初始化GPIOC      
}


void adc_init(void)
{
    ADC_InitTypeDef t_adc;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);//使能ADC1時鐘 
    
    t_adc.ADC_Mode = ADC_Mode_Independent;               //獨立模式:ADC1與ADC2獨立
    t_adc.ADC_ScanConvMode = DISABLE;                      //禁用通道掃描
    t_adc.ADC_ContinuousConvMode = DISABLE;                //禁用連續轉換
    t_adc.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//不使用外部觸發
    t_adc.ADC_DataAlign = ADC_DataAlign_Right;             //資料位右對齊
    t_adc.ADC_NbrOfChannel = 1;                            //轉換通道數為1
    ADC_Init(ADC1,&t_adc);                                 //初始化ADC1
    
    RCC_ADCCLKConfig(RCC_PCLK2_Div8);//配置ADC時鐘為PCLK2的8分頻
 
    ADC_Cmd(ADC1,ENABLE); //使能ADC1
        
    ADC_ResetCalibration(ADC1);          
    while(ADC_GetResetCalibrationStatus(ADC1));     
    ADC_StartCalibration(ADC1); //校準
    while(ADC_GetCalibrationStatus(ADC1)){}; 
}

uint16_t read_adc_value(uint8_t ch)
{
    uint16_t adc_value = 0;
    
    ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_7Cycles5 );   //設定ADC1通道ch的轉換週期為7.5個取樣週期,取樣次序為1

    ADC_SoftwareStartConvCmd(ADC1, ENABLE);//使能軟體觸發
    
    while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC )){};//等待轉換完成
        
    adc_value = ADC_GetConversionValue(ADC1); //獲取轉換值
  
    return adc_value;
}

DMA模式

ADC配置(配置4個通道)

void ADC_Multichannel_Config(void)
{
    ADC_InitTypeDef  ADC_InitStructure; 
    
    ADC_DeInit(ADC1);  //將外設 ADC1 的全部暫存器重設為預設值
    
    /* ADC1 configuration ------------------------------------------------------*/ 
    
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//ADC工作模式:ADC1和ADC2工作在獨立模式
    ADC_InitStructure.ADC_ScanConvMode =ENABLE; //ADC為掃描模式
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //ADC為連續轉換模式
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //外部觸發轉換關閉
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC資料右對齊 
    ADC_InitStructure.ADC_NbrOfChannel = 4; //順序進行規則轉換的ADC通道的數目
    ADC_Init(ADC1, &ADC_InitStructure); //根據ADC_InitStruct中指定的引數初始化外設ADCx的暫存器
    
    /* ADC1 regular channel11 configuration */ 
    //設定指定ADC的規則組通道,設定它們的轉化順序和取樣時間
    //ADC1,ADC通道x,規則取樣順序值為y,取樣時間為239.5週期
    
    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5 );
    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5 );
    ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 3, ADC_SampleTime_239Cycles5 );
    ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 4, ADC_SampleTime_239Cycles5);
    // 開啟ADC的DMA支援(要實現DMA功能,還需獨立配置DMA通道等引數)     
    ADC_DMACmd(ADC1, ENABLE); 
    /* Enable ADC1 */    
    ADC_Cmd(ADC1, ENABLE);    //使能指定的ADC1
     /* Enable ADC1 reset calibaration register */      
    ADC_ResetCalibration(ADC1);  //復位指定的ADC1的校準暫存器 
    
    /* Enable ADC1 reset calibaration register */      
    ADC_ResetCalibration(ADC1);  //復位指定的ADC1的校準暫存器 
    /* Start ADC1 calibaration */
    ADC_StartCalibration(ADC1);  //開始指定ADC1的校準狀態 
    /* Check the end of ADC1 calibration */
    while(ADC_GetCalibrationStatus(ADC1)); 
    //獲取指定ADC1的校準程式,設定狀態則等待
}

DMA配置(ADC1傳輸到記憶體變數ADC_Value)

void DMA_Configuration(void) 
{
    /* ADC1  DMA1 Channel Config */  
    DMA_InitTypeDef DMA_InitStructure;
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA時鐘

    DMA_DeInit(DMA1_Channel1);   //將DMA的通道1暫存器重設為預設值:ADC1連線DMA通道1
    DMA_InitStructure.DMA_PeripheralBaseAddr =  (u32)&ADC1->DR;  //DMA外設ADC基地址
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_Value;      //DMA記憶體基地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;  //記憶體作為資料傳輸的目的地
    DMA_InitStructure.DMA_BufferSize = N*4;  //此值為完整一輪DMA傳輸的次數
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外設地址不變
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  //記憶體地址遞增
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //外設資料位寬度16位,即DMA傳輸尺寸
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //資料寬度16位
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;  //工作在迴圈快取模式,一輪結束後自動開始下輪傳輸
    DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA通道 x擁有高優先順序
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMA通道x禁止記憶體到記憶體
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);  //根據DMA_InitStruct中引數DMA通道
}