stm32f407 ADC采集速度深度分析

本文使用源码来自整点原子例程【实验19-3 多通道ADC采集(DMA)实验】
本例程通过DMA自动采集6个通道的ADC数值,采集数据放在g_adc_dma_buf中,其声明如下:

#define ADC_DMA_BUF_SIZE        50 * 6      /* ADC DMA采集 BUF大小, 应等于ADC通道数的整数倍 */
uint16_t g_adc_dma_buf[ADC_DMA_BUF_SIZE];   /* ADC DMA BUF */

ADC_DMA_BUF_SIZE是6的几倍,表示一次采集几轮。
本文主要分析ADC的采集速度,以及如何控制ADC的采集速度。

adc.c文件中代码263-275行内容如下:

g_adc_nch_dma_handle.Instance = ADC_ADCX;
g_adc_nch_dma_handle.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;            /* 4分频,ADCCLK = PCLK2/4 = 84/4 = 21Mhz */
g_adc_nch_dma_handle.Init.Resolution = ADC_RESOLUTION_12B;                      /* 12位模式 */
g_adc_nch_dma_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;                      /* 右对齐 */
g_adc_nch_dma_handle.Init.ScanConvMode = ENABLE;                                /* 扫描模式 */
g_adc_nch_dma_handle.Init.ContinuousConvMode = ENABLE;                          /* 连续转换模式,转换完成之后接着继续转换 */
g_adc_nch_dma_handle.Init.DiscontinuousConvMode = DISABLE;                      /* 禁止不连续采样模式 */
g_adc_nch_dma_handle.Init.NbrOfConversion = ADC_CH_NUM;                         /* 使用转换通道数,需根据实际转换通道去设置 */
g_adc_nch_dma_handle.Init.NbrOfDiscConversion = 0;                              /* 不连续采样通道数为0 */
g_adc_nch_dma_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;                /* 软件触发 */
g_adc_nch_dma_handle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; /* 使用软件触发, 此位忽略 */
g_adc_nch_dma_handle.Init.DMAContinuousRequests = ENABLE;                       /* 开启DMA连续转换 */
HAL_ADC_Init(&g_adc_nch_dma_handle);  

其中的

g_adc_nch_dma_handle.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; 

表示设置ADC的时钟频谱为APB2的4分频,APB2频率为84MHz,所以设置的ADC时钟频率为84/4 = 21MHz。

g_adc_nch_dma_handle.Init.Resolution = ADC_RESOLUTION_12B;

表示设置ADC的分辨率为12比特。
adc.c文件中279-284行代码如下:

    adc_channel_set(&g_adc_nch_dma_handle, ADC_CHANNEL_0, 1, ADC_SAMPLETIME_480CYCLES);  /* 设置采样规则序列1~6 */
    adc_channel_set(&g_adc_nch_dma_handle, ADC_CHANNEL_1, 2, ADC_SAMPLETIME_480CYCLES);
    adc_channel_set(&g_adc_nch_dma_handle, ADC_CHANNEL_2, 3, ADC_SAMPLETIME_480CYCLES);
    adc_channel_set(&g_adc_nch_dma_handle, ADC_CHANNEL_3, 4, ADC_SAMPLETIME_480CYCLES);
    adc_channel_set(&g_adc_nch_dma_handle, ADC_CHANNEL_4, 5, ADC_SAMPLETIME_480CYCLES);
    adc_channel_set(&g_adc_nch_dma_handle, ADC_CHANNEL_5, 6, ADC_SAMPLETIME_480CYCLES);

这部分用于设置DMA每一轮的采集顺序,ADC_SAMPLETIME_480CYCLES表示采样周期为480个ADC时钟周期。
计算MDA采集一次ADC数据的时间公式如下:

转换时间 = 单次采样时间 + 转换时间
单次采样时间 = 采样周期 × ADC一个时钟周期时间
转换时间 = ADC分辨率 × ADC一个时钟周期时间

本实例中设置的采样周期为480,ADC分辨率为12,一个ADC时钟周期时间为1秒 / 21M = 0.047619微秒。
所以一个ADC数据的采集时间 = 480 × 0.047619 + 12 × 0.047619= 23.4285微妙。

为了验证以上计算采集时间公式的的准确性,做如下实验:
首先让DMA一轮采集6个通道的ADC数据,一次采集10000轮,测试一次的采集时间,发现采集时间为1405毫秒。
1405毫秒 / (60000) = 23.416666,与计算得到的23.4285微秒基本相符,

接下来把ADC的分辨率设置为8比特,代码如下:

g_adc_nch_dma_handle.Init.Resolution = ADC_RESOLUTION_8B;

测试采集时间为1394毫秒。
通过公式计算采集时间 (480 × 0.047619 + 8 × 0.047619) × 60000 = 1394毫秒,与实验结果相符。

所以从以上分析可以看出,ADC的采集速度,与ADC时钟周期、ADC采样周期、ADC分辨率有关。

接下来分析ADC时钟周期的设置方法。
前面提到过,本实例中设置的ADC时钟频谱是将APB2时钟频率进行4分频,可以分频的倍数如下:

#define ADC_CLOCK_SYNC_PCLK_DIV2    0x00000000U
#define ADC_CLOCK_SYNC_PCLK_DIV4    ((uint32_t)ADC_CCR_ADCPRE_0)
#define ADC_CLOCK_SYNC_PCLK_DIV6    ((uint32_t)ADC_CCR_ADCPRE_1)
#define ADC_CLOCK_SYNC_PCLK_DIV8    ((uint32_t)ADC_CCR_ADCPRE)

ADC时钟的源头是APB2,所以接下来讨论APB2的设置方式,比较直观的方式是看STM32CubeMX,如图:
在这里插入图片描述
本实例中,APB2的源头是AHB,AHB的源头SYSCLK,SYSCLK的源头是PLLCLK,PLLCLK的源头是HSE,
所以从晶振到ADC时钟频率的变换过程如下:

HSE(晶振)-> PLLCLK -> SYSCLK -> AHB -> APB2 -> ADC时钟

HSE表示高速外部振荡器(High Speed Extend Clock signal),也就是晶振,本实例用的开发板为8MHz。
LSE表示低速外部振荡器(Low Speed Extend Clock signal)。
HSI表示高速内部振荡器(High Speed Extend Internal signal),由内部RC振荡器产生,频谱为16Mhz,一般来说很少用,因为精度没有外部高速时钟那么高。

整个过程中比较重要的是通过PLL锁相环把8MHz的外表晶振频率转换成168MHz的系统时钟SYSCLK。其中重要的参数为M、N、P ,则:

SYSCLK = ((HSE / M) × N ) / P

程序中体现这3个参数为正点原子例程main函数中设置时钟部分:

sys_stm32_clock_init(336, 8, 2, 7);     /* 设置时钟,168Mhz */

其中336为N值、8为M值、2为P值。
所以通过通过PLL锁相环处理之后的系统时钟SYSCLK = ((8MHz / 8) × 336 ) / 2 = 168MHz
代码为sys.c文件中sys_stm32_clock_init() 函数141-149行

    /* 使能HSE,并选择HSE作为PLL时钟源,配置PLL1,开启USB时钟 */
    rcc_osc_init.OscillatorType = RCC_OSCILLATORTYPE_HSE;        /* 时钟源为HSE */
    rcc_osc_init.HSEState = RCC_HSE_ON;                          /* 打开HSE */
    rcc_osc_init.PLL.PLLState = RCC_PLL_ON;                      /* 打开PLL */
    rcc_osc_init.PLL.PLLSource = RCC_PLLSOURCE_HSE;              /* PLL时钟源选择HSE */
    rcc_osc_init.PLL.PLLN = plln;
    rcc_osc_init.PLL.PLLM = pllm;
    rcc_osc_init.PLL.PLLP = pllp;
    rcc_osc_init.PLL.PLLQ = pllq;
    ret = HAL_RCC_OscConfig(&rcc_osc_init);                      /* 初始化RCC */

从SYSCLK到AHB设置的代码为162行:

rcc_clk_init.AHBCLKDivider = RCC_SYSCLK_DIV1;                /* AHB分频系数为1 */

表示AHB与SYSCLK频率相同,同样是168MHz。

从AHB到APB2设置的代码为164行:

rcc_clk_init.APB2CLKDivider = RCC_HCLK_DIV2;                 /* APB2分频系数为2 */

表示APB为AHB的2分频,也就是84MHz。

最后从APB2到ADC频率的设置代码为:

g_adc_nch_dma_handle.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;            /* 4分频,ADCCLK = PCLK2/4 = 84/4 = 21Mhz */

表示ADC频率是APB2频率的4分频,也及时21MHz。

所以文档【STM32F4xx参考手册】中写的
在这里插入图片描述
在这里插入图片描述
其中12个周期貌似只是在ADC分辨率为12的时候。欢迎大家批评指正。

  • 5
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值