STM32-HAL库配置定时器溢出中断实现ADC DMA采样

目录

1.配置ADC+DMA

2.配置定时器

3.代码实践

4.实践

1.配置ADC+DMA

        我们配置ADC+DMA是为了ADC采样完成之后通过DMA来传输避免占用CPU。贴出配置图片:

需要注意的是,我此次只配置了一个通道,如果要用多个通道,有些配置是需要更改的。

在DMA中需要将Mode配置成Circular。接下来回到Parameter Settings中将Scan Conversion Mode需要配置成Enable,若想要实现自己控制采样的话,需要将Cotinues Coversion Mode成Disable,将Discotinuous Coversion Mode改成Disable,DMA Continuous Requests配置成Enable。我配置的是完成所有通道转换完成之后才能有中断触发,将End of Coversion Selection配置成Eoc flag at the end of all conversions。

多个通道话,需要将Number of Conversion增加到自己需要采样的通道数,同时将Channel中改变通道。Sampling Time需要根据采样结果来更改,为了方便我将其设置成3 Cycles。此时ADC的配置基本完成。

2.配置定时器

首先要计算好自己需要的频率,我这里为了测试方便,将Psc配置成180-1,将定时器1的时钟进行180分频,最后定时器1的时钟是1MHz。

解释一下如何计算中心对齐模式的频率:我们先回到Up模式(向上计算)计算频率的方法:f=定时器时钟频率/((ARR+1)X(PSC+1))=最终配置的频率。而在中心对齐模式我们也可以如此计算只是分母有点变化(因为中心对齐模式是先向上计算到ARR-1,然后再从ARR-1向下减),所以它的频率为Up或Down Mode模式下的一半即f=(定时器时钟频率/(ARR*(PSC+1)*2))。以我上图的配置来计算:f=(180MHz)/((500-1)*(180-1+1)*2)=1.004KHz。贴上示波器图片:

为了实现两次溢出中断,我将Counter Mode配置成Center Aligned mode3(上溢出和下溢出中断触发),下图有三种不同中心对齐模式的差距:

最后特别要注意的是Repetition Counter(RCR-8 bits value),如果此时将其配置成1,则只会触发一次,因为它要满足在一个PWM周期中上溢中断次数等于下溢中断次数。若是配置成0,处于中心对齐模式3,则会有两次中断触发,分别是上溢和下溢。

注意:我这里为了演示方便,只选取了一个通道,而且该通道为Channel1,各位要根据自己的实际需要来配置,目的是为了在示波器中看波形来对比。

接下来配置PWM Generation Channel 1:

Mode配置成PWM Mode2,Pulse一开始可设置为0,CH Polarity设置为高电平。在PWM2模式中先是无效电平然后是有效电平,而PWM1中先是有效电平然后是无效电平,两者的区别只是在于一开始是高电平还是低电平,而PWM2是一开始低电平而已。

接下来要配置NVIC Settings:

开启定时器的更新中断。

3.代码实践

#include "main.h"
#include "adc.h"
#include "dma.h"
#include "tim.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
uint16_t adc_buffer=0;
uint16_t ConvFinshed=0;

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    if(hadc->Instance==ADC1)
        {

            ConvFinshed=1;
            HAL_GPIO_TogglePin(TEST_GPIO_Port,TEST_Pin);
        }
}

/* USER CODE BEGIN 1 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == htim1.Instance)
        {
            HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&adc_buffer,sizeof(adc_buffer)/sizeof(uint16_t));
        }
}
/* USER CODE END 1 */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_ADC1_Init();
  MX_TIM1_Init();
  /* USER CODE BEGIN 2 */
    HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);
    HAL_TIM_PWM_Start_IT(&htim1,TIM_IT_UPDATE);
    HAL_TIM_Base_Start_IT(&htim1);//开启定时器1


    __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_1,100);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
    while (1)
        {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */

        }
  /* USER CODE END 3 */
}

接下来解释一下代码:

在进入main函数之前需要添加以下代码:

uint16_t adc_buffer=0;
uint16_t ConvFinshed=0;

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    if(hadc->Instance==ADC1)
        {

            ConvFinshed=1;
            HAL_GPIO_TogglePin(TEST_GPIO_Port,TEST_Pin);
        }
}

该函数是ADC转换完成回调函数,我们在里面设置一个输出电平反转为方便在示波器中看波形变化。

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == htim1.Instance)
        {
            HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&adc_buffer,sizeof(adc_buffer)/sizeof(uint16_t));
        }
}

该函数是定时器1溢出中断回调函数,接下来我们这里开启开启ADC的DMA传输模式。因为我们之前配置是手动触发,所以是每一次触发时ADC才开始采样。

在int main(void)中需要添加以下代码:

    HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);//开启定时器1通道1输出PWM
    HAL_TIM_PWM_Start_IT(&htim1,TIM_IT_UPDATE);//开启定时器的更新中断
    HAL_TIM_Base_Start_IT(&htim1);//开启定时器1中断


    __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_1,100);//设置占空比。(500-100)/500=80%

4.实践

由于是鼠标画的,所以直线不太直

5.补充

第二日本人尝试将定时器设置中的Repetition Counter(RCR)设置为0,并尝试不同的中心对齐模式,发现只要将RCR设置为0,不论是哪种中心对齐模式似乎都能触发两个中断,贴上示波器图和程序图。

以上是设置为中心对齐模式1,RCR设置为0,发现向下计数的时候触发一次,向上计数触发一次。

以上是中心对齐模式2,RCR设置为0,一样是触发两次。

以上为中心对齐模式3,RCR设置为0,触发两次

以上是设置RCR为1时,不论是哪种中心对齐模式,都只会触发一次。

6.总结

若想在一个PWM周期中触发两次中断,无论设置哪个中心对齐模式,设置RCR为0都会触发两次中断。若想在一个PWM周期中只触发一次中断,无论设置哪个中心对齐模式,设置RCR为1都只会触发一次。

注意:本人是在PWM2模式下测试的。

作者:悠悠子衿12138

物联沃分享整理
物联沃-IOTWORD物联网 » STM32-HAL库配置定时器溢出中断实现ADC DMA采样

发表回复