本帖最后由 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的采样时间与理论是相符的。
|
此文章已获得独家原创/原创奖标签,著作权归21ic所有,未经允许禁止转载。
打赏榜单
21小跑堂 打赏了 30.00 元 2025-07-28 理由:恭喜通过原创审核!期待您更多的原创作品~~
  共2人点赞
|
书接上文,增加定时器触发ADC采样,通过定时器的辅助,使多个ADC多通通加DMA采样更为可控,效果更佳。
@21小跑堂