stm32 ADC模數轉換 ADC多通道 ADC DMA

thomas_blog發表於2017-08-14

1 2
通過調節電位器,改變AD轉換值和電壓值

STM32F1 ADC 配置步驟

1.使能GPIO時鐘和ADC時鐘
2.配置引腳模式為模擬輸入
3.配置ADC的分頻因子
4.初始化ADC引數,ADC_InitTypeDef
5.使能ADC
6.執行ADC校準
7.設定ADC軟體啟動
8.讀取ADC轉換值
9.設定ADC規則,取樣時間等
10.使能ADC的軟體轉換
11.讀取ADC轉換結果

舉例

u16 ADC_value(u8 time)
{
    u8 i = 0;
    u16 value;

    for(i = 0; i < time; i++)
    {
        ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能ADC的軟體轉換

        while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) != SET);

        value += ADC_GetConversionValue(ADC1); //讀取ADC轉換結果
    }

    return value/time;
}

typedef struct
{
    uint32_t ADC_Mode; //雙模式選擇
    FunctionalState ADC_ScanConvMode; //掃描模式 
    FunctionalState ADC_ContinuousConvMode; //連續轉換
    uint32_t ADC_ExternalTrigConv;  //注入通道的外部觸發轉換模式
    uint32_t ADC_DataAlign; //資料對齊
    uint8_t ADC_NbrOfChannel; //規則通道序列長度
}ADC_InitTypeDef;

void ADC_init()
{
    GPIO_InitTypeDef gpio = 
    {
        GPIO_Pin_1,
        GPIO_Speed_50MHz,
        GPIO_Mode_AIN //模擬輸入
    };

    ADC_InitTypeDef adc =
    {
        ADC_Mode_Independent, //獨立模式
        DISABLE, //關閉掃描模式
        DISABLE, //單次轉換模式
        ADC_ExternalTrigConv_None, //不用外部事件啟動轉換
        ADC_DataAlign_Right, //右對齊
        1, //通道數目1
    };

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE); //使能GPIO時鐘和ADC時鐘

    GPIO_Init(GPIOA, &gpio); //配置引腳模式

    RCC_ADCCLKConfig(RCC_PCLK2_Div6); //配置ADC的分頻因子 72/6=12(通常)

    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_55Cycles5); //設定ADC規則,取樣週期

    ADC_Init(ADC1, &adc); //初始化ADC引數

    ADC_Cmd(ADC1, ENABLE); //使能ADC

    ADC_ResetCalibration(ADC1); //執行ADC復位校準
    while(ADC_GetResetCalibrationStatus(ADC1) == SET); //等待校準完成

    ADC_StartCalibration(ADC1); //執行ADC校準
    while(ADC_GetCalibrationStatus(ADC1) == SET);

    ADC_SoftwareStartConvCmd(ADC1, ENABLE); //ADC軟體啟動
}

int main(void)
{
    ADC_init();

    while(1)
    {
        adc = ADC_value(15); //15次平均值
        printf("ADC_value is %d.\n", adc);
        printf("vol is %.3fV.\n", adc * (3.3 / 4096)); //電壓值
        delay_ms(500);
        led1 = ~led1;
    }
}

電壓計算

vol = ADC * (3.3 / 4096);

ADC的參考電壓VREF+為3.3V。ADC為12位轉換精度, 2^12為4096

DMA方式
ADC_DMACmd開啟DMA,在ADC初始化之後
ADC_RegularChannelConfig通道配置,在ADC初始化之後
DMA_MemoryInc要設定為DMA_MemoryInc_Enable,儲存器地址遞增

DMA_BufferSize大小,是定義DMA_MemoryBaseAddr記憶體的大小。根據DMA_MemoryDataSize儲存器資料寬度,HalfWord佔16bit

#define CHANNEL_NUM 4

volatile u16 AD_Bufer[4];
volatile u8 adc1_ok;

//多通道配置。4路輸入
void ADC_init()
{
    ...

    ADC_InitTypeDef adc =
    {
        ADC_Mode_Independent, //獨立模式
        ENABLE, //開啟掃描模式
        ENABLE, //開啟連續轉換模式
        ADC_ExternalTrigConv_None, //不用外部事件啟動轉換
        ADC_DataAlign_Right, //右對齊
        CHANNEL_NUM, //通道數目4
    };

    ADC_Init(ADC1, &adc);

    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_55Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 2, ADC_SampleTime_55Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 3, ADC_SampleTime_55Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 4, ADC_SampleTime_55Cycles5);

    DMA_adc_init(ADC1, (u32)AD_Bufer, CHANNEL_NUM);

    ...
}

void DMA_adc_init(ADC_TypeDef *ADCx, u32 mem_addr, u32 size)
{
   DMA_InitTypeDef DMA_InitStructure;
   NVIC_InitTypeDef NVIC_InitStructure;

   RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA控制器時鐘

   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
   NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
   NVIC_Init(&NVIC_InitStructure);

   DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&ADCx->DR; //外設地址
   DMA_InitStructure.DMA_MemoryBaseAddr = mem_addr; //記憶體地址
   DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
   DMA_InitStructure.DMA_BufferSize = size; //4個快取大小
   DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
   DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
   DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
   DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
   DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //迴圈模式
   DMA_InitStructure.DMA_Priority = DMA_Priority_High;
   DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
   DMA_Init(DMA1_Channel1, &DMA_InitStructure);

   DMA_ITConfig(DMA1_Channel1, DMA1_IT_TC1, ENABLE); //開啟中斷

   DMA_Cmd(DMA1_Channel1, ENABLE);

   ADC_DMACmd(ADCx, ENABLE); //開啟ADC1 DMA採集
}

void DMA1_Channel1_IRQHandler(void)
{
    if(DMA_GetITStatus(DMA1_IT_TC1) != RESET)
    {
        DMA_ClearITPendingBit(DMA1_IT_TC1);
        adc1_ok = 1;
    }
}

int main(void)
{
    ADC_init();

    while(1)
    {
        if(adc1_ok == 1)
        {
            v0 = AD_Bufer[0];
            v1 = AD_Bufer[1];
            v2 = AD_Bufer[2];
            v3 = AD_Bufer[3];

            printf("v0 is %.3fV.\n", v0 * (3.3 / 4096)); //電壓值
            printf("v1 is %.3fV.\n", v1 * (3.3 / 4096));
            printf("v2 is %.3fV.\n", v2 * (3.3 / 4096));
            printf("v3 is %.3fV.\n", v3 * (3.3 / 4096));

            led1 = ~led1;
            adc1_ok = 0;
        }

        delay_ms(500);
    }
}

相關文章