返回

ST H7 ADC应用之定时器触发配合DMA双缓冲的原理解析与编程实现

后端

前言

ST H7系列微控制器集成了高性能的模拟数字转换器(ADC),可实现高速、高精度的数据采集。在某些应用中,需要对模拟信号进行连续采集,并以一定速率传输到处理器进行处理。此时,可以使用定时器触发配合DMA双缓冲技术来提高数据采集的效率和准确性。

原理解析

定时器触发配合DMA双缓冲技术的基本原理如下图所示:

[原理解析示意图]

  1. 定时器触发:

    • 配置定时器以产生周期性中断。
    • 在定时器中断服务程序中,启动ADC转换。
  2. DMA双缓冲:

    • 配置DMA控制器,将ADC转换结果存储到两个缓冲区中。
    • 当一个缓冲区已满时,DMA控制器会自动切换到另一个缓冲区,确保数据采集不会中断。
  3. 处理器数据处理:

    • 处理器从缓冲区中读取ADC转换结果,进行数据处理和分析。

编程实现

以下以STM32H743ZI为例,介绍如何使用定时器触发配合DMA双缓冲技术进行ADC数据采集。

  1. 配置ADC:

    • 启用ADC时钟。
    • 配置ADC通道、采样率和分辨率。
    • 配置ADC触发源为定时器。
  2. 配置定时器:

    • 启用定时器时钟。
    • 配置定时器周期和中断。
  3. 配置DMA:

    • 启用DMA控制器时钟。
    • 配置DMA传输模式、数据宽度、源地址、目标地址和传输数量。
    • 配置DMA中断。
  4. 编写中断服务程序:

    • 在定时器中断服务程序中,启动ADC转换。
    • 在DMA中断服务程序中,处理ADC转换结果。
  5. 主函数:

    • 初始化ADC、定时器和DMA。
    • 开启ADC转换和DMA传输。
    • 从缓冲区中读取ADC转换结果,进行数据处理和分析。

示例代码

以下为STM32H743ZI使用定时器触发配合DMA双缓冲技术进行ADC数据采集的示例代码:

#include "stm32h7xx_hal.h"

/* ADC handler */
ADC_HandleTypeDef hadc1;

/* DMA handler */
DMA_HandleTypeDef hdma_adc1;

/* Timer handler */
TIM_HandleTypeDef htim2;

/* Buffers for ADC data */
uint16_t adc_buffer1[1024];
uint16_t adc_buffer2[1024];

/* Index of current buffer */
uint8_t current_buffer = 0;

/* Flag to indicate that ADC conversion is complete */
volatile uint8_t adc_conversion_complete = 0;

/* Initialize ADC, DMA, and Timer */
void System_Init(void)
{
    /* Enable ADC, DMA, and Timer clocks */
    __HAL_RCC_ADC12_CLK_ENABLE();
    __HAL_RCC_DMA2_CLK_ENABLE();
    __HAL_RCC_TIM2_CLK_ENABLE();

    /* Configure ADC */
    hadc1.Instance = ADC1;
    hadc1.Init.DataResolution = ADC_RESOLUTION_12B;
    hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
    hadc1.Init.ContinuousConvMode = ENABLE;
    hadc1.Init.NbrOfConversion = 16;
    hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
    hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
    hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T2_TRGO;
    HAL_ADC_Init(&hadc1);

    /* Configure DMA */
    hdma_adc1.Instance = DMA2_Stream0;
    hdma_adc1.Init.Channel = DMA_CHANNEL_0;
    hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
    hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_adc1.Init.Mode = DMA_CIRCULAR;
    hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;
    hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    HAL_DMA_Init(&hdma_adc1);

    /* Link DMA to ADC */
    __HAL_LINKDMA(&hadc1, DMA_Handle, hdma_adc1);

    /* Configure Timer */
    htim2.Instance = TIM2;
    htim2.Init.Prescaler = 80 - 1;
    htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim2.Init.Period = 1000 - 1;
    htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_Base_Init(&htim2);
}

/* Start ADC conversion and DMA transfer */
void Start_ADC_Conversion(void)
{
    /* Start ADC conversion */
    HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_buffer1, 1024);

    /* Start DMA transfer */
    HAL_DMA_Start(&hdma_adc1);

    /* Start Timer */
    HAL_TIM_Base_Start_IT(&htim2);
}

/* ADC interrupt handler */
void ADC_IRQHandler(void)
{
    /* Clear ADC interrupt flag */
    __HAL_ADC_CLEAR_FLAG(&hadc1, ADC_FLAG_EOC);

    /* Set conversion complete flag */
    adc_conversion_complete = 1;
}

/* Timer interrupt handler */
void TIM2_IRQHandler(void)
{
    /* Clear Timer interrupt flag */
    __HAL_TIM_CLEAR_IT(&htim2, TIM_IT_UPDATE);

    /* Start ADC conversion */
    HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_buffer2, 1024);
}

/* DMA interrupt handler */
void DMA2_Stream0_IRQHandler(void)
{
    /* Clear DMA interrupt flag */
    __HAL_DMA_CLEAR_FLAG(&hdma_adc1, DMA_FLAG_TCIF0);

    /* Switch to another buffer */
    current_buffer = (current_buffer + 1) % 2;

    /* Check if ADC conversion is complete */
    if (adc_conversion_complete)
    {
        /* Stop ADC conversion and DMA transfer */
        HAL_ADC_Stop_DMA(&hadc1);
        HAL_DMA_Abort(&hdma_adc1);

        /* Process ADC data */
        // ...

        /* Reset conversion complete flag */
        adc_conversion_complete = 0;
    }
}

/* Main function */
int main(void)
{
    /* Initialize system */
    System_Init();

    /* Start ADC conversion and DMA transfer */
    Start_ADC_Conversion();

    /* Infinite loop */
    while (1)
    {
        /* Check if ADC conversion is complete */
        if (adc_conversion_complete)
        {
            /* Process ADC data */
            // ...

            /* Reset conversion complete flag */
            adc_conversion_complete = 0;
        }
    }
}

结语

ST H7 ADC应用之定时器触发配合DMA双缓冲技术是一种高效的数据采集方法,可以提高数据采集的速率和准确性。本文详细介绍了该技术的原理解析和编程实现,并提供了示例代码。读者可以根据自己的需要,灵活运用该技术来满足不同的应用需求。