当前位置:   article > 正文

STM32L071使用TIM+DMA+ADC数据采集_数据采集adc+dma+tim

数据采集adc+dma+tim

 简介:使用TIM+DMA+ADC可实现高频、大量的数据采集,节省CPU资源,本文将讲解如何结合三者进行高频、大量的ADC采集。

概述:本文大致介绍使用定时器触发ADC采集转换,转换数据通过DMA通道传输存入内存空间。可在DMA中断中处理相关数据。

优点:ADC采用频率可调范围广,可一次性大量采集,DMA传输完成后及时处理,提升数据的实时性。

一、硬件配置

        所有硬件配置均通过STM32cubeMX自动生成,再根据自己需求对各寄存器参数进行微调,故本文不再对各硬件功能、性能参数做介绍,相关知识可查看数据手册。

(一)、TIM配置

  本列中,定时器用于事件更新触发ADC转换。此处使用TIM3,在TIM3中断函数中更新事件,达到触发ADC转换的目的(下文ADC配置中,外部触发源需要对应选择TIM3触发)。

       使用STM32cubeMX生成代码如下:(通过调整定时器更新中断频率调整ADC采集周期)

  1. tiemr3_init()
  2. {
  3. TIM_SlaveConfigTypeDef sSlaveConfig = {0};
  4. TIM_MasterConfigTypeDef sMasterConfig = {0};
  5. /* USER CODE BEGIN TIM3_Init 1 */
  6. /* USER CODE END TIM3_Init 1 */
  7. timer3.Instance = TIM3;
  8. timer3.Init.Prescaler = 8; //预分频值
  9. timer3.Init.CounterMode = TIM_COUNTERMODE_UP;//向上计数
  10. timer3.Init.Period = 100;//自动重装载值
  11. timer3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;//时钟分频系数
  12. timer3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;//自动重装载使能
  13. if (HAL_TIM_Base_Init(&timer3) != HAL_OK)
  14. {
  15. Error_Handler();
  16. }
  17. sSlaveConfig.SlaveMode = TIM_SLAVEMODE_TRIGGER;//触发模式
  18. sSlaveConfig.InputTrigger = TIM_TS_ITR0;//内部触发器0为触发源
  19. if (HAL_TIM_SlaveConfigSynchro(&timer3, &sSlaveConfig) != HAL_OK)
  20. {
  21. Error_Handler();
  22. }
  23. sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;//更新事件作为触发输出
  24. sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  25. if (HAL_TIMEx_MasterConfigSynchronization(&timer3, &sMasterConfig) != HAL_OK)
  26. {
  27. Error_Handler();
  28. }
  29. __HAL_RCC_TIM3_CLK_ENABLE();
  30. /* TIM3 interrupt Init */
  31. HAL_NVIC_SetPriority(TIM3_IRQn, 3, 0);//中断配置
  32. HAL_NVIC_EnableIRQ(TIM3_IRQn);
  33. }

(二)、DMA配置

  DMA工作原理不做过多讲解,简单说来就是可以将一个地址的数据内容搬运至另一个地址内存中,而在这个过程中不需要CPU的介入,两个地址可以分别是:外设寄存器地址与存储器地址、存储器地址与存储器地址、外设寄存器地址与外设寄存器地址。

  这里推荐一篇文章:https://blog.csdn.net/as480133937/article/details/104927922 作者详细讲解了DMA的工作原理,内容通俗易懂。

  DMA配置代码如下:

 

  1. dma_init()
  2. {
  3. hdma_adc.Instance = DMA1_Channel1;
  4. hdma_adc.Init.Request = DMA_REQUEST_0;//指定通道选择请求
  5. hdma_adc.Init.Direction = DMA_PERIPH_TO_MEMORY;//传输方向
  6. hdma_adc.Init.PeriphInc = DMA_PINC_DISABLE;//外围寄存器地址不自增
  7. hdma_adc.Init.MemInc = DMA_MINC_ENABLE;//内存自增
  8. hdma_adc.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;//数据宽度——半字-16位
  9. hdma_adc.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
  10. hdma_adc.Init.Mode = DMA_CIRCULAR;//DMA_NORMAL;
  11. hdma_adc.Init.Priority = DMA_PRIORITY_HIGH;
  12. if (HAL_DMA_Init(&hdma_adc) != HAL_OK)
  13. {
  14. Error_Handler();
  15. }
  16. __HAL_LINKDMA(hadc,DMA_Handle,hdma_adc);//连接DMA与ADC句柄
  17. /* DMA controller clock enable */
  18. __HAL_RCC_DMA1_CLK_ENABLE();
  19. /* DMA interrupt init */
  20. /* DMA1_Channel1_IRQn interrupt configuration */
  21. HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 3, 0);
  22. HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
  23. }

(三)、ADC配置

  ADC配置与使用软件触发时配置相同,仅需注意外部触发源需要选择定时器3的触发器0作为触发源,其余参数根据自己需求修改。

  ADC代码配置如下:

  1. adc_init()
  2. {
  3. ADC_ChannelConfTypeDef sConfig = {0};
  4. /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
  5. */
  6. hadc.Instance = ADC1;
  7. hadc.Init.OversamplingMode = DISABLE;
  8. hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV1;
  9. hadc.Init.Resolution = ADC_RESOLUTION_12B;//12位
  10. hadc.Init.SamplingTime = ADC_SAMPLETIME_39CYCLES_5;//采样时间
  11. hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
  12. hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  13. hadc.Init.ContinuousConvMode = ENABLE;//DISABLE;
  14. hadc.Init.DiscontinuousConvMode = DISABLE;
  15. hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
  16. hadc.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T3_TRGO;
  17. hadc.Init.DMAContinuousRequests = DISABLE;//DMA转换为连续模式
  18. hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  19. hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
  20. hadc.Init.LowPowerAutoWait = DISABLE;
  21. hadc.Init.LowPowerFrequencyMode = DISABLE;
  22. hadc.Init.LowPowerAutoPowerOff = DISABLE;
  23. if (HAL_ADC_Init(&hadc) != HAL_OK)
  24. {
  25. Error_Handler();
  26. }
  27. /** Configure for the selected ADC regular channel to be converted.
  28. */
  29. sConfig.Channel = ADC_CHANNEL_0;
  30. sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
  31. if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
  32. {
  33. Error_Handler();
  34. }
  35. }

二、软件结合

(一)、中断函数

1、定时器中断函数:

     在中断中给出更新事件,触发ADC转换。

  1. void TIM3_IRQHandler()
  2. {
  3. HAL_TIM_GenerateEvent(&timer3,TIM_EVENTSOURCE_UPDATE);
  4. HAL_TIM_IRQHandler(&timer3);
  5. }

此处生成软件事件直接放在中断函数里面,在cubeMX自动生成的文件中,有中断回调函数,可在回调函数里生成软件事件。个人不习惯,故放在外面。

2、DMA传输结束中断函数:

  DMA在每次传输完成后会进入DMA中断,可在该中断中处理数据或根据需求进行相应动作。

  1. void DMA1_Channel1_IRQHandler(void)
  2. {
  3. /* USER CODE BEGIN DMA1_Channel1_IRQn 0 */
  4. /* USER CODE END DMA1_Channel1_IRQn 0 */
  5. HAL_DMA_IRQHandler(&hdma_adc);
  6. /* USER CODE BEGIN DMA1_Channel1_IRQn 1 */
  7. /* USER CODE END DMA1_Channel1_IRQn 1 */
  8. }

 (二)、数据转换

数据转换与传输不是一次触发就永远执行的,定时器生成的触发事件仅触发ADC进行数据转换,但并没有将数据传输至存储器中,需要使用下面函数启动转换传输数据。在一组数据传输完成后需要重新启动方可转换。(*pData为缓存空间,length为数据个数,可动态调整通道传输个数)

HAL_ADC_Start_DMA(ADC_HandleTypeDef *hadc, uint32_t *pData, uint32_t Length)

三、总结

  第一次使用定时器+DMA进行ADC采集,单个模块的使用很简单,但是要将三个联系在一起,且不习惯使用cubeMX的情况下,还是有许多疑问。在此记录一下,文中不尽之处欢迎指正,欢迎探讨。

  最后,DMA真的很实用,且方便。

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

闽ICP备14008679号