以下是实验源码中使用 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 方式也可能增加系统延迟和功耗。