Alden 发表于 2025-7-22 18:02

APM32F407 定时器触发ADC+DMA采样测量3ADC采样速度

本帖最后由 Alden 于 2025-7-22 18:02 编辑

#技术资源# #申请原创#
上一篇帖子测了APM32F407使用ADC加DMA连续采样3ADC的12个通道,但进一步测试发现这样做虽然能一直用DMA采ADC的数据,但不好确定数据是在什么时刻采的,也不好测量ADC采样的时间,就再调整了下,增加用定时器触发ADC采样。
查看APM32F407的手册可以看到,ADC有多种外部触发的选择,可以根据需要进行选择。

我这里就选择TMR2_TRGO来进行触发。
定时器的配置如下:
void TMR_Init(void) {
      TMR_BaseConfig_T timBaseConfig;
                RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_TMR2);
      //系统工作主频为168M,由于APB1是4分频,所以tmr2时钟84M计算如下
      timBaseConfig.period = 83;                         //设置定时器时基为125us,(83+1)*(124+1)/(84m) = 125us      
      timBaseConfig.division = 124;               
      timBaseConfig.clockDivision = TMR_CLOCK_DIV_1;
      timBaseConfig.countMode = TMR_COUNTER_MODE_UP;
      TMR_ConfigTimeBase(TMR2,&timBaseConfig);
      
      //选择外部触发源
      TMR_SelectOutputTrigger(TMR2, TMR_TRGO_SOURCE_UPDATE);
      
      TMR_EnableInterrupt(TMR2,TMR_INT_UPDATE);
      NVIC_EnableIRQRequest(TMR2_IRQn,0,0);
      
      TMR_Enable(TMR2);
}系统工作主频为168M,由于APB1是4分频,所以tmr2时钟84M计算如下:(83+1)*(124+1)/(84m) = 125us
也就是在默认168M主频下TMR2触发ADC采样的周期是125us.
对应的,ADC的初始化中增加外部触发的配置:
void ADC_Init(void)
{
    GPIO_Config_T gpioConfig;
    ADC_Config_TadcConfig;
    ADC_CommonConfig_T adcCommonConfig;

    /* RCM Enable*/
    RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOA|RCM_AHB1_PERIPH_GPIOB|RCM_AHB1_PERIPH_GPIOC);

    /* GPIO Configuration */
    GPIO_ConfigStructInit(&gpioConfig);
    gpioConfig.pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6;
    gpioConfig.mode = GPIO_MODE_AN;
    gpioConfig.pupd = GPIO_PUPD_NOPULL;
    GPIO_Config(GPIOA, &gpioConfig);
               
          GPIO_ConfigStructInit(&gpioConfig);
    gpioConfig.pin = GPIO_PIN_0|GPIO_PIN_1;
    gpioConfig.mode = GPIO_MODE_AN;
    gpioConfig.pupd = GPIO_PUPD_NOPULL;
    GPIO_Config(GPIOB, &gpioConfig);
      
          GPIO_ConfigStructInit(&gpioConfig);
    gpioConfig.pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3;
    gpioConfig.mode = GPIO_MODE_AN;
    gpioConfig.pupd = GPIO_PUPD_NOPULL;
    GPIO_Config(GPIOC, &gpioConfig);

    RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_ADC1);
    RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_ADC2);
                RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_ADC3);
    ADC_Reset();

    /* ADC Common Configuration */
    adcCommonConfig.mode      = ADC_MODE_TRIPLE_REGSIMULT;//3ADC规则同步模式
    adcCommonConfig.prescaler   = ADC_PRESCALER_DIV4;
    adcCommonConfig.accessMode= ADC_ACCESS_MODE_1;
    adcCommonConfig.twoSampling = ADC_TWO_SAMPLING_10CYCLES;
    ADC_CommonConfig(&adcCommonConfig);

    /* ADC Configuration */
    ADC_ConfigStructInit(&adcConfig);

    /* Set resolution*/
    adcConfig.resolution = ADC_RESOLUTION_12BIT;
    /* Set dataAlign*/
    adcConfig.dataAlign = ADC_DATA_ALIGN_RIGHT;
    /* Set scanDir*/
    adcConfig.scanConvMode = ENABLE;
    /* Set convMode continous*/
    adcConfig.continuousConvMode = DISABLE;
    /* Set extTrigEdge*/
    adcConfig.extTrigEdge = ADC_EXT_TRIG_EDGE_RISING;
                adcConfig.extTrigConv = ADC_EXT_TRIG_CONV_TMR2_TRGO;      //设置ADC使用TMR2触发
    /* Set nbrOfConversion*/
    adcConfig.nbrOfChannel = 4;

    ADC_Config(ADC1, &adcConfig);
    ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_0, 1, ADC_SAMPLETIME_3CYCLES);
    ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_1, 2, ADC_SAMPLETIME_3CYCLES);
                ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_2, 3, ADC_SAMPLETIME_3CYCLES);
    ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_4, 4, ADC_SAMPLETIME_3CYCLES);

    ADC_Config(ADC2, &adcConfig);
    ADC_ConfigRegularChannel(ADC2, ADC_CHANNEL_5, 1, ADC_SAMPLETIME_3CYCLES);
    ADC_ConfigRegularChannel(ADC2, ADC_CHANNEL_6, 2, ADC_SAMPLETIME_3CYCLES);
                ADC_ConfigRegularChannel(ADC2, ADC_CHANNEL_8, 3, ADC_SAMPLETIME_3CYCLES);
    ADC_ConfigRegularChannel(ADC2, ADC_CHANNEL_9, 4, ADC_SAMPLETIME_3CYCLES);
               
                ADC_Config(ADC3, &adcConfig);
    ADC_ConfigRegularChannel(ADC3, ADC_CHANNEL_10, 1, ADC_SAMPLETIME_3CYCLES);
    ADC_ConfigRegularChannel(ADC3, ADC_CHANNEL_11, 2, ADC_SAMPLETIME_3CYCLES);
    ADC_ConfigRegularChannel(ADC3, ADC_CHANNEL_12, 3, ADC_SAMPLETIME_3CYCLES);
    ADC_ConfigRegularChannel(ADC3, ADC_CHANNEL_13, 4, ADC_SAMPLETIME_3CYCLES);
               

               
    ADC_EnableMultiModeDMARequest();
    /* Enable ADC*/
    ADC_Enable(ADC1);
    ADC_Enable(ADC2);
                ADC_Enable(ADC3);
                ADC_SoftwareStartConv(ADC1);

}这样的话ADC就会由定时器TMR2触发采样,采样完成后有DMA搬运到数组中,触发DMA完成中断进行数据处理。
void DMA2_STR0_IRQHandler(void)
{
      if(DMA_ReadIntFlag(DMA2_Stream0,DMA_INT_TCI**0) != RESET)
      {
                              APM_MINI_LEDToggle(LED2);
                                        APM_MINI_LEDOn(LED2);
                                        ADC1_CH0_ConvertedValue = (float)DMA_DualConvertedValue/4095*3.3f;
                                        ADC2_CH5_ConvertedValue = (float)DMA_DualConvertedValue/4095*3.3f;
                                        ADC3_CH10_ConvertedValue = (float)DMA_DualConvertedValue/4095*3.3f;
                                       
                                        ADC1_CH1_ConvertedValue = (float)DMA_DualConvertedValue/4095*3.3f;
                                        ADC2_CH6_ConvertedValue = (float)DMA_DualConvertedValue/4095*3.3f;
                                        ADC3_CH11_ConvertedValue = (float)DMA_DualConvertedValue/4095*3.3f;
                                       
                                        ADC1_CH2_ConvertedValue = (float)DMA_DualConvertedValue/4095*3.3f;
                                        ADC2_CH8_ConvertedValue = (float)DMA_DualConvertedValue/4095*3.3f;
                                        ADC3_CH12_ConvertedValue = (float)DMA_DualConvertedValue/4095*3.3f;
                                       
                                        ADC1_CH4_ConvertedValue = (float)DMA_DualConvertedValue/4095*3.3f;
                                        ADC2_CH9_ConvertedValue = (float)DMA_DualConvertedValue/4095*3.3f;
                                        ADC3_CH13_ConvertedValue = (float)DMA_DualConvertedValue/4095*3.3f;
      DMA_ClearIntFlag(DMA2_Stream0,DMA_INT_TCI**0);
      }
}

void TMR2_IRQHandler(void){
      if (TMR_ReadIntFlag(TMR2, TMR_INT_UPDATE) != RESET) {
                              APM_MINI_LEDOff(LED2);
                TMR_ClearIntFlag(TMR2, TMR_INT_UPDATE);
                }
}同时,这样也可以使用一个IO在TMR2中断中置高,在DMA完成中断中置低,IO的高电平时间就相当于ADC采样转化的时间。
我们也可以先看看理论上的ADC采样时间改如何计算:
ADC时钟在APB2总线上,最大是主频的二分频也就是84Mhz。
ADC的时钟通过    adcCommonConfig.prescaler   = ADC_PRESCALER_DIV4;再分频下来。
这里测试是4分频也就是21Mhz
ADC的转换时间公司在用户手册中可以看到:
TCONV=采样时间+12 个周期;采样时间由 SMPCYCCFGx位控制,最小采样周期为 3 个
也就是我配置的:    ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_0, 1, ADC_SAMPLETIME_3CYCLES);
所以我目前配置的采样频率是21M÷(3+12)=1.4M,对应的采样时间为0.714us。
我使用三ADC采12个通道需要采4次也就是我目前配置的理论采样时间就是4*0.713us=2.856us。
而实际的采样时间我们通过示波器输出来看:

实际采样时间是3.333us

将采样周期改成15后: ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_0, 1, ADC_SAMPLETIME_15CYCLES);
理论采样时间就是5.143us
实际采样时间是5.529us

可以看到实际值比理论值略大零点几us,这主要是DMA传输以及进出中断的指令占的时间.
所以APM32F407ADC的采样时间与理论是相符的。

绝影孤狼 发表于 2025-7-29 20:53

非常详细的配置过程,学习了!不过我有个疑问,DMA搬运数据时的延迟对采样时间的影响大吗?

wangwu1976@ 发表于 2025-9-10 08:18

学习了

观星者宁静 发表于 2025-9-10 14:22

3个ADC均由Timer2触发,是不是 意味着这3个通道是同步采样了。

暗夜幽灵骑士 发表于 2025-9-28 17:06

如果我想调整采样频率,应该如何修改配置?
页: [1]
查看完整版本: APM32F407 定时器触发ADC+DMA采样测量3ADC采样速度