发新帖本帖赏金 30.00元(功能说明)我要提问
返回列表
打印
[APM32F4]

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

[复制链接]
682|3
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
Alden|  楼主 | 2025-7-22 18:02 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 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_T  adcConfig;
    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[0]/4095*3.3f;
                                        ADC2_CH5_ConvertedValue = (float)DMA_DualConvertedValue[1]/4095*3.3f;
                                        ADC3_CH10_ConvertedValue = (float)DMA_DualConvertedValue[2]/4095*3.3f;
                                       
                                        ADC1_CH1_ConvertedValue = (float)DMA_DualConvertedValue[3]/4095*3.3f;
                                        ADC2_CH6_ConvertedValue = (float)DMA_DualConvertedValue[4]/4095*3.3f;
                                        ADC3_CH11_ConvertedValue = (float)DMA_DualConvertedValue[5]/4095*3.3f;
                                       
                                        ADC1_CH2_ConvertedValue = (float)DMA_DualConvertedValue[6]/4095*3.3f;
                                        ADC2_CH8_ConvertedValue = (float)DMA_DualConvertedValue[7]/4095*3.3f;
                                        ADC3_CH12_ConvertedValue = (float)DMA_DualConvertedValue[8]/4095*3.3f;
                                       
                                        ADC1_CH4_ConvertedValue = (float)DMA_DualConvertedValue[9]/4095*3.3f;
                                        ADC2_CH9_ConvertedValue = (float)DMA_DualConvertedValue[10]/4095*3.3f;
                                        ADC3_CH13_ConvertedValue = (float)DMA_DualConvertedValue[11]/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[2:0]位控制,最小采样周期为 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的采样时间与理论是相符的。

使用特权

评论回复

打赏榜单

21小跑堂 打赏了 30.00 元 2025-07-28
理由:恭喜通过原创审核!期待您更多的原创作品~~

评论
21小跑堂 2025-7-28 17:48 回复TA
书接上文,增加定时器触发ADC采样,通过定时器的辅助,使多个ADC多通通加DMA采样更为可控,效果更佳。 
Alden 2025-7-22 18:03 回复TA
@21小跑堂 
沙发
绝影孤狼| | 2025-7-29 20:53 | 只看该作者
非常详细的配置过程,学习了!不过我有个疑问,DMA搬运数据时的延迟对采样时间的影响大吗?

使用特权

评论回复
发新帖 本帖赏金 30.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

49

主题

112

帖子

2

粉丝