当前位置:   article > 正文

【STM32】- 定时器+DMA+ADC 双重模式_stm32 adc dma

stm32 adc dma

目录

 

1 前言

2 ADC介绍

2.1 多重工作模式

2.2 多重ADC框图

2.3 规则同时模式

3 程序设计

3.1 时序图

3.2 初始化流程图

3.3 初始化代码

4 结论


 

1 前言

     关于ADC,相信大家都比较了解,关于STM32的学习教程都会有所讲解,但以查询方式、单通道讲解的较多,主要告诉大家基本的原理。关于ADC多重模式讲解的较少。本文主要通过讲解ADC转换器的双重工作模式,让大家更好的理解ADC的多重模式。

参考资料《STM32F4参考手册》。

2 ADC介绍

    STM32单片机内部集成了12位ADC转换器,是逐次趋近型模数转换器。具有多达19个复用通道,可测量来自16个外部源、两个内部源和VBAT通道的信号。这些通道的AD转换可在单次、连续、扫描和不连续采样模式下进行。

2.1 多重工作模式

    在具有两个或多个ADC的器件中,可以使用两重(具有2个ADC)或三重(具有3个)ADC模式。在多重 ADC 模式下,通过 ADC1 主器件到 ADC2 和 ADC3 从器件的交替触发或同时触发来启动转换,具体取决于 ADC_CCR 寄存器中的 MULTI[4:0] 位所选的模式。在多重 ADC 模式下,配置外部事件触发转换时,应用必须设置为仅主器件触发而禁止从器件触发,以防止出现意外触发而启动不需要的从转换。

    可实现以下四种模式:
    ● 注入同时模式
    ● 规则同时模式
    ● 交替模式
    ● 交替触发模式
     也可按以下方式组合使用上述模式:
    ● 注入同时模式 + 规则同时模式
    ● 规则同时模式 + 交替触发模式

2.2 多重ADC框图

0cde10e1fecd433c908e1d44fb67521e.bmp

 

图 1 多重ADC框图

    从图中可以看出:

  1. 尽管 ADC2 和 ADC3 上存在外部触发,但它们并未显示在此图中。
  2. 在双重 ADC 模式下,不存在 ADC3 从器件部分。
  3. 在三重 ADC 模式下, ADC 通用数据寄存器 (ADC_CDR) 包含 ADC1、 ADC2 和 ADC3 的规则转换数据。按照所选的存储顺序使用全部 32 个寄存器位。在双重 ADC 模式下, ADC 通用数据寄存器 (ADC_CDR) 包含 ADC1 和 ADC2 的规则转换数据。使用全部32 个寄存器位。

2.3 规则同时模式

    此模式可用于规则通道组。外部触发源来自 ADC1 的规则组多路复用器。在规则同时模式下,必须使用同一长度来转换序列,或必须确保触发之间的间隔长于 2 个序列(双重 ADC 模式)中的较长转换时间。否则,当序列较长的ADC 完成上一次转换时,序列较短的 ADC 可能重新开始转换。必须禁止注入转换。

     双重 ADC 模式,在 ADC1 或 ADC2 转换事件结束时: 会生成一个 32 位 DMA 传输请求(如果 ADC_CCR 寄存器中的 DMA[1:0] 位等于0b10)。此请求会将存储在 ADC_CDR 32 位寄存器高位半字中的 ADC2 转换数据传输到 SRAM,然后将存储在 ADC_CCR 低位半字中的 ADC1 转换数据传输到 SRAM。

     当 ADC1/ADC2 的规则通道全部完成转换后,会生成一个 EOC 中断(如果已在两个 ADC接口中的一个接口上使能)。

    16通道规则同时模式如下图所示。

5e8d14028eb045ad99d181344c26d6f5.bmp

 

图 2 双重ADC模式

3 程序设计

3.1 时序图

ffa602975e634286bda46e4ed85fcfde.png

 

图 3定时器+DMA+ADC工作时序图

        如图 3所示,完成DMA、定时器、ADC初始化后,先使能DMA,再使能ADC,最后启动定时器Timer。当定时器发生更新事件时,触发ADC开始采样、转换,转换结束后触发DMA保存数据到缓冲区,完成一次变换。定时器的周期设定要确保完成ADC转换及DMA数据存储,否则将溢出错误。当DMA检测到转换次数达到其预设值时,触发DMA接收完成中断,关闭定时器。一个大的AD采集循环完成。

3.2 初始化流程图

        初始化过程包括ADC初始化、DMA初始化、定时器初始化。ADC初始化和大部分MCU外设初始化流程一样,先使能时钟,包括GPIO时钟、ADC时钟、ADC配置,这里的ADC配置包含ADC1和ADC2两个外设的配置。DMA初始化包含DMA时钟(本文用DMA将ADC转换结果拷贝到RAM中)使能、DMA配置,在后面的实现代码中,将DMA初始化合并在ADC_MspInit函数中完成。因为要用定时器实现定期触发ADC转化,所以在初始化时要对定时器初始化,完成时钟使能、定时器配置。

d9c2e0956e0e40b0b28de0be7378e02c.png

 

图 4 初始化流程图

3.3 初始化代码

        ADC初始化相关函数如表 1所示

表 1 ADC初始化相关函数

序号

函数名称

函数功能说明

1

HAL_ADC_MspInit ()

时钟使能、IO、DMA初始化

2

MX_ADC1_Init()

配置ADC1

3

MX_ADC2_Init()

配置ADC2

4

ADC_Trigger_Timer_Start()

启动定时器

5

ADC_Trigger_Timer_Stop()

停止定时器

6

DMA2_Stream0_IRQHandler()

DMA中断处理函数

7

HAL_ADC_ConvCpltCallback()

ADC转换完成回调函数

8

MX_Timer2_Init()

配置Timer2配置为TIM_TRGO_UPDATE输出触发模式

        1)HAL_ADC_MspInit ()代码

        在ADC1底层初始化时,使能了ADC、DMA、GPIO时钟,引脚配置为模拟输入模式。对DMA2初始化,使能DMA中断。具体如下:

  1. void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
  2. {
  3. GPIO_InitTypeDef GPIO_InitStruct;
  4. if(adcHandle->Instance==ADC1)
  5. {
  6. __HAL_RCC_ADC1_CLK_ENABLE(); //使能ADC0时钟
  7. __HAL_RCC_DMA2_CLK_ENABLE();
  8. __HAL_RCC_GPIOA_CLK_ENABLE();
  9. __HAL_RCC_GPIOC_CLK_ENABLE();
  10. GPIO_InitStruct.Pin = GPIO_PIN_3;
  11. GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
  12. GPIO_InitStruct.Pull = GPIO_NOPULL;
  13. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  14. GPIO_InitStruct.Pin = GPIO_PIN_0;
  15. GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
  16. GPIO_InitStruct.Pull = GPIO_NOPULL;
  17. HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
  18. hdma_adc1.Instance = DMA2_Stream0;
  19. hdma_adc1.Init.Channel = DMA_CHANNEL_0;
  20. hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
  21. hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
  22. hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
  23. hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
  24. hdma_adc1.Init.MemDataAlignment = DMA_PDATAALIGN_WORD;
  25. hdma_adc1.Init.Mode = DMA_NORMAL;
  26. hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;
  27. hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
  28. HAL_DMA_Init(&hdma_adc1);
  29. __HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1);
  30. HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 1, 1); //DMA中断配置
  31. HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
  32. }
  33. else if(adcHandle->Instance==ADC2)
  34. {
  35. __HAL_RCC_ADC2_CLK_ENABLE();
  36. }
  37. }

        2)MX_ADC1_Init()代码

  1. void MX_ADC1_Init(void)
  2. {
  3. ADC_MultiModeTypeDef multimode;
  4. ADC_ChannelConfTypeDef sConfig;
  5. /**配置 ADC通用参数*/
  6. hadc1.Instance = ADC1;
  7. hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
  8. hadc1.Init.Resolution = ADC_RESOLUTION_12B; //设置分辨率
  9. hadc1.Init.ScanConvMode = ENABLE; //扫描方式
  10. hadc1.Init.ContinuousConvMode = DISABLE; //关闭连续扫描
  11. hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING; //触发模式
  12. hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T2_TRGO; //触发源
  13. hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; //对齐方式
  14. hadc1.Init.NbrOfConversion = 1;
  15. hadc1.Init.DMAContinuousRequests = ENABLE;
  16. hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
  17. HAL_ADC_Init(&hadc1);
  18. /**配置 ADC工作模式 */
  19. multimode.Mode = ADC_DUALMODE_REGSIMULT; //规则同时模式
  20. multimode.DMAAccessMode =ADC_DMAACCESSMODE_2; //ADC同步DMA模式2
  21. multimode.TwoSamplingDelay = ADC_TWOSAMPLINGDELAY_5CYCLES; //
  22. HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode);
  23. /**配置 ADC采集通道*/
  24. sConfig.Channel = ADC_CHANNEL_VREFINT;
  25. sConfig.Rank = 1;
  26. sConfig.SamplingTime = ADC_SAMPLETIME_56CYCLES;
  27. HAL_ADC_ConfigChannel(&hadc1, &sConfig);
  28. }

        3)MX_ADC2_Init()代码

  1. void MX_ADC2_Init(void)
  2. {
  3. ADC_ChannelConfTypeDef sConfig;
  4. /**配置 ADC通用参数*/
  5. hadc2.Instance = ADC2;
  6. hadc2.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
  7. hadc2.Init.Resolution = ADC_RESOLUTION_12B;
  8. hadc2.Init.ScanConvMode = ENABLE;
  9. hadc2.Init.ContinuousConvMode = DISABLE;
  10. hadc2.Init.DiscontinuousConvMode = DISABLE;
  11. hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  12. hadc2.Init.NbrOfConversion = 1;
  13. hadc2.Init.DMAContinuousRequests = DISABLE;
  14. hadc2.Init.EOCSelection = ADC_EOC_SEQ_CONV;
  15. HAL_ADC_Init(&hadc2);
  16. /**配置 ADC采集通道*/
  17. sConfig.Channel = ADC_CHANNEL_13;
  18. sConfig.Rank = 1;
  19. sConfig.SamplingTime = ADC_SAMPLETIME_56CYCLES;
  20. HAL_ADC_ConfigChannel(&hadc2, &sConfig);
  21. HAL_ADC_Start(&hadc2);
  22. }

        4)ADC_Trigger_Timer_Start()代码

  1. static void ADC_Trigger_Timer_Start(void)
  2. {
  3. __HAL_TIM_ENABLE(&htim2);
  4. }

        5)ADC_Trigger_Timer_Stop()代码

  1. static void ADC_Trigger_Timer_Stop(void)
  2. {
  3. __HAL_TIM_DISABLE(&htim2);
  4. }

 

6)HAL_ADC_ConvCpltCallback()代码

  1. void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
  2. {
  3. ADC_Trigger_Timer_Stop();
  4. HAL_ADCEx_MultiModeStop_DMA(&hadc1);
  5. ADC_ConvCpltCallback();
  6. }
  1. MX_Timer2_Init()代码
  1. void MX_Timer2_Init(void)
  2. {
  3. TIM_MasterConfigTypeDef sMasterConfig;
  4. uint32_t uwPrescalerValue = 0;
  5. uwPrescalerValue = (uint32_t) (SystemCoreClock /2/1000000) - 1; //timer2挂在APB2上,倍频后最大时钟为系统时钟/2,
  6. htim2.Instance = TIM2;
  7. htim2.Init.Prescaler = uwPrescalerValue; //定时器时钟频率为1M
  8. htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  9. htim2.Init.Period = 200-1; //定时器溢出时间,1uS*100 = 200uS
  10. htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  11. HAL_TIM_Base_Init(&htim2);
  12. sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  13. sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  14. HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig);
  15. }

4 结论

        本文介绍了定时器+DMA+ADC双重工作模式下ADC驱动设计思路,介绍了ADC初始化、配置的主要函数。文中代码截取自实际工程中部分代码,旨在为读者提供设计思路,可根据自己的程序做简单修改即可。

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/566568
推荐阅读
相关标签
  

闽ICP备14008679号