ZBLOG

实验源码“31-ADC-电压采集:5-ADC-芯片温度”采用 DMA 的方式实现 MCU 温度的采集,比较 DMA 方式与单通道(中断读取)方式程序的异同。

以下是实验源码中使用 DMA 的部分代码:

#define ADC_CONVERTED_DATA_BUFFER_SIZE  ((uint32_t)  2)   /* Size of array aADCxConvertedData[] */
#define ADC_TEMPERATURE_REF_VOLTAGE     ((uint32_t) 33)
#define ADC_TEMPERATURE_V25             ((uint32_t)1100)

__IO uint16_t uhADCxConvertedData[ADC_CONVERTED_DATA_BUFFER_SIZE]; /* ADC group regular conversion data */

int main(void)
{
    /* Configure the system clock to 168 MHz */
    SystemClock_Config();

    /* Initialize LED */
    BSP_LED_Init(LED2);

    /* Configure EXTI Line for interrupt */
    UserButton_Init();

    /* Configure DMA controller to transfer ADC converted data to destination buffer */
    if (DMA_Config() != HAL_OK)
    {
        Error_Handler();
    }

    /* Configure ADC1 and its regular group with DMA mode enabled */
    if (ADC_Config_DMA() != HAL_OK)
    {
        Error_Handler();
    }

    while (1)
    {
        /* Turn-on/off LED2 in function of ADC conversion result */ 
        if (uhADCxConvertedData[0] > 2048)
        {
            BSP_LED_On(LED2);
        }
        else
        {
            BSP_LED_Off(LED2);
        }
        
        HAL_Delay(100);
	}
}

static HAL_StatusTypeDef DMA_Config(void)
{
	static DMA_HandleTypeDef hdma_adc;
	
	/*## -1- Enable peripherals and GPIO Clocks #################################*/
	/* Enable DMA clock */
	__HAL_RCC_DMA2_CLK_ENABLE();

	/*##-2- Select the DMA functional Parameters ###############################*/
	hdma_adc.Instance                 = DMA2_Stream0;
	hdma_adc.Init.Channel             = DMA_CHANNEL_0;
	hdma_adc.Init.Direction           = DMA_PERIPH_TO_MEMORY;
	hdma_adc.Init.PeriphInc           = DMA_PINC_DISABLE;
	hdma_adc.Init.MemInc              = DMA_MINC_ENABLE;
	hdma_adc.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
	hdma_adc.Init.MemDataAlignment    = DMA_MDATAALIGN_HALFWORD;
	hdma_adc.Init.Mode                = DMA_CIRCULAR;
	hdma_adc.Init.Priority            = DMA_PRIORITY_HIGH;
	hdma_adc.Init.FIFOMode            = DMA_FIFOMODE_DISABLE;         
	if (HAL_DMA_Init(&hdma_adc) != HAL_OK)
	{
	    return HAL_ERROR_DMA_INIT;
	}

	/* Associate the initialized DMA handle to the the ADC handle */
	__HAL_LINKDMA(&hadc1, DMA_Handle, hdma_adc);

	return HAL_OK;
}

static HAL_StatusTypeDef ADC_Config_DMA(void)
{
	static ADC_ChannelConfTypeDef sConfig;

	/*##-1- Configure the ADC peripheral #######################################*/
   hadc1.Instance                   = ADC1;

   hadc1.Init.ClockPrescaler        = ADC_CLOCK_SYNC_PCLK_DIV4; /* Synchronous clock mode, input ADC clock divided by 4*/
   hadc1.Init.Resolution            = ADC_RESOLUTION_12B;       /* 12-bit resolution for converted data */
   hadc1.Init.ScanConvMode          = DISABLE;                  /* Sequencer disabled (ADC conversion on only 1 channel: channel set on rank 1) */
   hadc1.Init.ContinuousConvMode    = ENABLE;                   /* Continuous mode enabled to have continuous conversion */
   hadc1.Init.DiscontinuousConvMode = DISABLE;                  /* Parameter discarded because sequencer is disabled */
   hadc1.Init.NbrOfDiscConversion   = 0;
   hadc1.Init.ExternalTrigConvEdge  = ADC_EXTERNALTRIGCONVEDGE_NONE;        /* Conversion start trigged at each external event */
   hadc1.Init.ExternalTrigConv      = ADC_SOFTWARE_START;                     /* External trigger disabled (because using software start) */
   hadc1.Init.DataAlign             = ADC_DATAALIGN_RIGHT;
   hadc1.Init.NbrOfConversion       = 1;

    if (HAL_ADC_Init(&hadc1) != HAL_OK)
    {
        return HAL_ERROR_ADC_INIT;
    }

	/*##-2- Configure the ADC regular channel ######################################*/
	sConfig.Channel      = ADC_CHANNEL_TEMPSENSOR;
	sConfig.Rank         = 1;
	sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES;
	if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
	{
	    return HAL_ERROR_ADC_CONFIG_CHANEL;
	}

	/*##-3- Start the conversion process and enable interrupt ####################*/
	if(HAL_ADC_Start_DMA(&hadc1,
	                    (uint32_t *)uhADCxConvertedData,
	                    ADC_CONVERTED_DATA_BUFFER_SIZE
	                    ) != HAL_OK)
	{
	    return HAL_ERROR_ADC_START_DMA;
	}
	
	return HAL_OK;
}

而单通道(中断读取)方式的代码可以参考以下示例:

#define VREF 3.3      // 参考电压
#define ADC_RES 4096  // ADC 分辨率

int main() {
    float value;

    // 初始化 ADC
    adc_init();

    while(1) {
        // 开始一次采样并等待采样完成
        adc_start_conversion();
        while(!adc_conversion_complete());

        // 获取采样结果并转换为实际数值
        uint16_t result = adc_get_result();
        float voltage = result * VREF / ADC_RES;     // 转换为电压值
        value = voltage * 10000;   // 根据芯片的温度传感器特性转换为温度值(单位为摄氏度)

        printf("Temperature: %.2f C\n", value);
    }
}

可以看到,使用 DMA 的方式相比于单通道中断读取方式,需要先进行一些配置工作,例如初始化 DMA 控制器、配置 ADC 的通道和分辨率等。同时,DMA 方式还可以设置循环模式,自动重复采样并将结果存储在指定的数组中。这种方式减少了 CPU 的负担,提高了系统的响应速度。

但是,在小型项目或者只需要采集一个信号时,并不需要使用 DMA 方式。因为 DMA 方式需要更多的代码来初始化和配置硬件,并且对于只有一个通道的 ADC 来说,在大多数情况下也不会带来显著的性能提升。此外,DMA 方式也可能增加系统延迟和功耗。

本站部分文章来源于网络,版权归原作者所有,如有侵权请联系站长删除。
转载请注明出处:https://sdn.0voice.com/?id=742

分享:
扫描分享到社交APP
上一篇
下一篇
发表列表
游客 游客
此处应有掌声~
评论列表

还没有评论,快来说点什么吧~

联系我们

在线咨询: 点击这里给我发消息

微信号:3007537140

上班时间: 10:30-22:30

关注我们
x

注册

已经有帐号?