STM32F407 ADC+DMA+定时器 定时采样模拟量

STM32F407 ADC+DMA+定时器 定时采样模拟量

文章目录

  • STM32F407 ADC+DMA+定时器 定时采样模拟量
  • 前言
  • 一、硬件原理
    • 1.1 ADC
    • 1.2 定时器
    • 3. DMA
  • 二、代码实现
    • 2.1初始化
    • 2.1.1 PIN initial
    • 2.2 ADC 初始化 代码
    • 2.3 DMA 初始化 代码
    • 3.1 定时器初始化
    • 3.2 函数调用
  • 总结


前言

项目中需要对多个通道的电压进行一定频率的AD采样,由于采样过程贯穿整个任务,为了使采样过程尽可能不占用CPU资源,采用定时器触发的多通道ADC扫描采样,且采样数据由DMA传到RAM中的缓存。
这样做有以下几个好处:1、由定时器触发ADC采样,这样采样的频率可控,且定时器触发不会占用任何CPU资源;2、DMA进一步降低了任务对CPU的占有率。


提示:以下是本篇文章正文内容,下面案例可供参考

一、硬件原理

1.1 ADC

STM32F 407的ADC的规则通道扫描采样,配置好规则通道后,可以采用软件触发的方式开启AD转换,也可通过外部触发,如下图所示。可以通过定时器以及外部中断方式触发:
如下图为ADC 内部使用框图:

STM32F407 ADC+DMA+定时器 定时采样模拟量_第1张图片

1.2 定时器

STM32F407 ADC+DMA+定时器 定时采样模拟量_第2张图片
如上图所示是定时器的内部硬件原理框图,在此使用定时器的TIMER3 的TRGO信号作为ADC转换的触发信号,当接收到一次中断信号后,ADC通道进行转换一次.

3. DMA

STM32F407 ADC+DMA+定时器 定时采样模拟量_第3张图片
如上图所示,ADC1 使用的是DMA 的stream0 数据流,ADC2使用DMA2 的Stream2数据流.

二、代码实现

2.1初始化

global 数据

__IO uint16_t g_Adc2ConvertedValues[10][6];//6 个通道每个通道采样10次

2.1.1 PIN initial

	static void BspGpioConfig(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    GPIO_InitStructure.GPIO_Pin  =  GPIO_Pin_0    
                                   | GPIO_Pin_4   
                                   | GPIO_Pin_7;  
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0  
                                 | GPIO_Pin_1;  
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0    
                                 | GPIO_Pin_1 ; 

    GPIO_Init(GPIOC, &GPIO_InitStructure);

    return;
}

2.2 ADC 初始化 代码

static void BspAdcConfig(void)
{
    ADC_CommonInitTypeDef ADC_CommonInitStructure;
    ADC_InitTypeDef       ADC_InitStructure;

    ADC_CommonInitStructure.ADC_Mode             = ADC_Mode_Independent;
    ADC_CommonInitStructure.ADC_Prescaler        = ADC_Prescaler_Div8;
    ADC_CommonInitStructure.ADC_DMAAccessMode    = ADC_DMAAccessMode_Disabled;
    ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles;
    ADC_CommonInit(&ADC_CommonInitStructure);

    ADC_InitStructure.ADC_Resolution           = ADC_Resolution_12b;
    ADC_InitStructure.ADC_ScanConvMode         = DISABLE;
    ADC_InitStructure.ADC_ContinuousConvMode   = DISABLE;
    ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
    ADC_InitStructure.ADC_ExternalTrigConv     = ADC_ExternalTrigConv_T2_TRGO;
    ADC_InitStructure.ADC_DataAlign            = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_NbrOfConversion      = 1;
    ADC_Init(ADC1, &ADC_InitStructure);

    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_480Cycles);     // PA0_AD0_AC

    ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);
    ADC_DMACmd(ADC1, ENABLE);
    ADC_Cmd(ADC1, ENABLE);

    ADC_InitStructure.ADC_ScanConvMode         = ENABLE;
    ADC_InitStructure.ADC_ExternalTrigConv     = ADC_ExternalTrigConv_T3_TRGO;
    ADC_InitStructure.ADC_NbrOfConversion      = ADC2_NbrofChannel;
    ADC_Init(ADC2, &ADC_InitStructure);


    ADC_RegularChannelConfig(ADC2, ADC_Channel_4, 1, ADC_SampleTime_480Cycles);     // PA4_AD4_24V_2
    ADC_RegularChannelConfig(ADC2, ADC_Channel_7, 2, ADC_SampleTime_480Cycles);     // PA7_AD7_GNDF
    ADC_RegularChannelConfig(ADC2, ADC_Channel_8, 3, ADC_SampleTime_480Cycles);     // PB0_AD8_24V_OCP_C1
    ADC_RegularChannelConfig(ADC2, ADC_Channel_9, 4, ADC_SampleTime_480Cycles);     // PB1_AD9_24V_OCP_C2
    ADC_RegularChannelConfig(ADC2, ADC_Channel_10,5, ADC_SampleTime_480Cycles);     // PC0_AD10_HOUSING
    ADC_RegularChannelConfig(ADC2, ADC_Channel_11,6, ADC_SampleTime_480Cycles);     // PC1_AD11_24V_1

    ADC_DMARequestAfterLastTransferCmd(ADC2, ENABLE);
    ADC_DMACmd(ADC2, ENABLE);
    ADC_Cmd(ADC2, ENABLE);

    return;
}

2.3 DMA 初始化 代码


static void BspDmaConfig(void)
{
    DMA_InitTypeDef DMA_InitStructure;

    DMA_InitStructure.DMA_Channel            = DMA_Channel_0;
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
    DMA_InitStructure.DMA_Memory0BaseAddr    = (uint32_t)&g_Adc1ConvertedValues;
    DMA_InitStructure.DMA_DIR                = DMA_DIR_PeripheralToMemory;
    DMA_InitStructure.DMA_BufferSize         = 1024 * 2;
    DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode               = DMA_Mode_Circular;
    DMA_InitStructure.DMA_Priority           = DMA_Priority_High;
    DMA_InitStructure.DMA_FIFOMode           = DMA_FIFOMode_Disable;
    DMA_InitStructure.DMA_FIFOThreshold      = DMA_FIFOThreshold_HalfFull;
    DMA_InitStructure.DMA_MemoryBurst        = DMA_MemoryBurst_Single;
    DMA_InitStructure.DMA_PeripheralBurst    = DMA_PeripheralBurst_Single;
    DMA_Init(DMA2_Stream0, &DMA_InitStructure);

    DMA_ITConfig(DMA2_Stream0, DMA_IT_HT | DMA_IT_TC, ENABLE);
    DMA_Cmd(DMA2_Stream0, ENABLE);

    DMA_InitStructure.DMA_Channel            = DMA_Channel_1;
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC2->DR;
    DMA_InitStructure.DMA_Memory0BaseAddr    = (uint32_t)&g_Adc2ConvertedValues;
    DMA_InitStructure.DMA_BufferSize         = ADC2_NbrofChannel * SamplingNumber;//N*M N: channel number M:sampling number
    DMA_Init(DMA2_Stream2, &DMA_InitStructure);

    DMA_Cmd(DMA2_Stream2, ENABLE);

    return;
}

static void BspNvicConfig(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;

    NVIC_InitStructure.NVIC_IRQChannel                   = DMA2_Stream0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    return;
}

在调用这几个函数之前必须设置RCC时钟如下代码设置RCC时钟:

static void BspRccConfig(void)
{
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA
                         | RCC_AHB1Periph_GPIOB
                         | RCC_AHB1Periph_GPIOC
                         | RCC_AHB1Periph_DMA2, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2
                         | RCC_APB1Periph_TIM3, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1
                         | RCC_APB2Periph_ADC2, ENABLE);

    return;
}

3.1 定时器初始化

static void BspTimConfig(void)
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

    // TIM2设置ADC1  1024Hz
    TIM_TimeBaseStructure.TIM_Prescaler     = 0;
    TIM_TimeBaseStructure.TIM_CounterMode   = TIM_CounterMode_Up;
    TIM_TimeBaseStructure.TIM_Period        = 84000000 / 1024 - 1;
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

    TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);

    // TIM3设置ADC2 1000Hz
    TIM_TimeBaseStructure.TIM_Prescaler     = 84000000 / 1000000 - 1;
    TIM_TimeBaseStructure.TIM_Period        = 1000000 / 1000 - 1;
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

    TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update);

    TIM_Cmd(TIM2, ENABLE);
    TIM_Cmd(TIM3, ENABLE);

    return;
}

3.2 函数调用

void BspAdcInit(void)
{
    BspRccConfig();
    BspGpioConfig();
    BspAdcConfig();
    BspDmaConfig();
    BspNvicConfig();
    BspTimConfig();
    return;
}

在调用BspAdcInit()函数后,就配置完成了ADC+DMA+TIMER的配置.会定时完成采集,且不占用CPU资源.采集的数据将会自动存储在定义的g_Adc2ConvertedValues 全局变量中.

总结

以上流程就是使用STM32的ADC+DMA+timer实现自动定时采样模拟电压的配置使用流程,若读者发先任何疑问,妄指出问题…

你可能感兴趣的:(stm32,单片机,arm)