一、ADC+DMA读取配置函数
1. HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length)
函数功能:
启动ADC的DMA传输,实现自动将ADC转换结果传输到指定内存地址。
参数说明:
hadc:ADC句柄指针,指向ADC配置结构体
pData:目标内存地址指针,用于存储ADC转换结果
Length:DMA传输的数据长度(转换次数)
工作特点:
自动数据传输: ADC每次转换完成后,DMA自动将结果从ADC数据寄存器搬运到指定内存
连续/单次模式: 根据ADC配置支持连续转换或单次转换
多通道支持: 配合ADC扫描模式,可实现多通道自动转换和存储
**非阻塞操作:**启动后立即返回,转换在后台进行
配置要求:
ADC必须配置为DMA模式
DMA通道必须正确配置为外设到内存模式
在多通道扫描模式下,Length通常是通道数量的整数倍
2. HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
函数功能:
ADC转换完成回调函数,当DMA传输完成指定数量的转换结果时自动调用。
参数说明:
hadc:触发回调的ADC句柄指针
工作特点:
自动调用: 由HAL库在DMA传输完成时自动调用
用户重写: 需要用户在代码中实现这个函数
安全处理: 在中断上下文中执行,适合处理转换结果
**多ADC支持:**通过hadc参数区分不同的ADC实例
二、示例实验
1. 实验现象
每隔500毫秒启动一次ADC的DMA转换,自动采集两个通道的模拟信号。当DMA传输完成两个通道的转换结果后,系统会自动调用回调函数,将原始ADC值转换为0.000V-3.300V的电压值,并通过串口以"输出1"和"输出2"的形式分别显示两个通道的电压测量值。串口上会看到每500毫秒输出一组两个电压值,实时反映两个ADC输入通道的电压变化情况。
2. 示例代码
ADC初始化
/**
* @brief ADC1初始化函数
*/
void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0}; // ADC通道配置结构体
/** ADC通用配置 */
hadc1.Instance = ADC1; // 设置ADC实例为ADC1
hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE; // 启用扫描模式(多通道)
hadc1.Init.ContinuousConvMode = DISABLE; // 禁用连续转换模式(单次转换)
hadc1.Init.DiscontinuousConvMode = DISABLE; // 禁用间断模式
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; // 使用软件触发转换
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; // 数据右对齐
hadc1.Init.NbrOfConversion = 2; // 转换序列数为2(2个通道)
// 初始化ADC,如果失败则调用错误处理函数
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/** 配置规则通道1 */
sConfig.Channel = ADC_CHANNEL_4; // 设置ADC通道4
sConfig.Rank = ADC_REGULAR_RANK_1; // 设置通道在规则组中的排名为第1个
sConfig.SamplingTime = ADC_SAMPLETIME_55CYCLES_5; // 设置采样时间为55.5个ADC时钟周期
// 配置ADC通道4,如果失败则调用错误处理函数
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** 配置规则通道2 */
sConfig.Channel = ADC_CHANNEL_5; // 设置ADC通道5
sConfig.Rank = ADC_REGULAR_RANK_2; // 设置通道在规则组中的排名为第2个
// 采样时间保持与通道4相同(55.5个ADC时钟周期)
// 配置ADC通道5,如果失败则调用错误处理函数
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
}
DMA初始化
/**
* @brief DMA初始化函数
*/
void MX_DMA_Init(void)
{
// 使能DMA1时钟
__HAL_RCC_DMA1_CLK_ENABLE();
// DMA中断配置
// 配置DMA1通道1中断(ADC1使用的DMA通道)
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0); // 设置中断优先级
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn); // 使能DMA中断
}
主函数
uint16_t get[2] = {0}; // ADC原始值数组,用于存储两个通道的转换结果
float aaa[2] = {0.0f}; // 电压值数组,用于存储计算后的电压值
// 系统时钟配置函数声明
void SystemClock_Config(void);
/**
* @brief 应用程序主函数
* @retval int
*/
int main(void)
{
// HAL库初始化
HAL_Init();
// 系统时钟配置
SystemClock_Config();
// 初始化所有配置的外设
MX_GPIO_Init(); // GPIO初始化
MX_DMA_Init(); // DMA初始化
MX_ADC1_Init(); // ADC1初始化(配置为双通道扫描模式)
MX_USART1_UART_Init(); // USART1串口初始化
// 执行ADC校准
HAL_ADCEx_Calibration_Start(&hadc1);
// 主循环
while (1)
{
// 启动ADC DMA传输,转换2个通道的数据到get数组
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)get, 2);
// 延时500ms
HAL_Delay(500);
}
}
中断回调函数
/**
* @brief ADC转换完成回调函数(DMA模式)
* @param hadc: ADC句柄指针
*/
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
if(hadc->Instance == ADC1)
{
// 将ADC原始值转换为电压值(12位分辨率,参考电压3.3V)
aaa[0] = (float)get[0] / 4095 * 3.3f; // 通道4的电压值
aaa[1] = (float)get[1] / 4095 * 3.3f; // 通道5的电压值
// 通过串口输出两个通道的电压值
printf("\r\n输出1:%.3f\r\n输出2:%.3f\r\n", aaa[0], aaa[1]);
}
}