1.ADC驱动(STM32)
ADC模块中,**常规模式(Regular Mode)和注入模式(Injected Mode)**是两种不同的ADC工作模式
常规模式:用于普通的ADC转换,是默认的ADC工作模式。
注入模式:用于对特定通道进行高优先级的ADC采样。
特性 | 常规模式(Regular Mode) | 注入模式(Injected Mode) |
优先级 | 低 | 高 |
采样通道 | 支持多通道,按顺序扫描 | 支持多通道,但专用优先处理 |
DMA的兼容性 | 支持,适合连续采样传输 | 通常不使用 DMA |
场景 | 采集温度、湿度等非关键的环境传感器数据 | 采集电流、电压等高优先级的实时数据 |
1.1 常规模式
-
转换触发方式:软件启动或者外部硬件触发(比如使用函数开启读取数据这种)
-
数据转换完存储在ADC的数据寄存器(ADC_DR)中(这个知道就行,平常也用不到)
-
支持、适合使用DMA进行连续数据传输
-
可配置扫描模式,对多个通道依次采样
-
转换通道数可以通过
NbrOfConversion
配置
1.1.1 初始化代码
下面的代码按照:配置ADC参数、初始化ADC、启动校准、配置常规模式的参数、写入常规通道顺序写的
int32_t stm32_adc_init(void)
{
// 定义ADC配置结构体(常规模式)
ADC_ChannelConfTypeDef sConfig = {0};
// 初始化ADC3的实例
hadc3.Instance = ADC3;
// 配置ADC的初始化参数
hadc3.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV4; // 异步时钟分频因子设为4
hadc3.Init.Resolution = ADC_RESOLUTION_12B; // 分辨率为12位
hadc3.Init.DataAlign = ADC3_DATAALIGN_RIGHT; // 数据右对齐
hadc3.Init.ScanConvMode = ADC_SCAN_ENABLE; // 启用扫描模式
hadc3.Init.EOCSelection = ADC_EOC_SINGLE_CONV; // 每次单次转换结束触发EOC
hadc3.Init.LowPowerAutoWait = DISABLE; // 禁用低功耗模式
hadc3.Init.ContinuousConvMode = DISABLE; // 禁用连续转换模式(单次模式)
hadc3.Init.NbrOfConversion = 2; // 常规模式中转换2个通道
hadc3.Init.DiscontinuousConvMode = DISABLE; // 禁用不连续模式
hadc3.Init.DMAContinuousRequests = ENABLE; // 启用DMA连续请求
hadc3.Init.SamplingMode = ADC_SAMPLING_MODE_NORMAL; // 使用普通采样模式
hadc3.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DR; // 数据直接存储到数据寄存器
hadc3.Init.Overrun = ADC_OVR_DATA_PRESERVED; // 数据溢出时保持旧数据
hadc3.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE; // 禁用左移位操作
hadc3.Init.OversamplingMode = DISABLE; // 禁用过采样
// 初始化ADC,检查初始化是否成功
if (HAL_ADC_Init(&hadc3) != HAL_OK)
{
Error_Handler(); // 如果失败,调用错误处理函数
}
// 启动ADC校准
HAL_ADCEx_Calibration_Start(&hadc3, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED);
// 配置第1个通道:通道0
sConfig.Channel = ADC_CHANNEL_0; // 通道0
sConfig.Rank = ADC_REGULAR_RANK_1; // 常规序列中的第1个通道
sConfig.SamplingTime = ADC_SAMPLETIME_810CYCLES_5; // 采样时间为810.5周期
sConfig.SingleDiff = ADC_SINGLE_ENDED; // 单端输入模式
sConfig.OffsetNumber = ADC_OFFSET_NONE; // 不使用偏移校正
sConfig.Offset = 0; // 偏移量为0
if (HAL_ADC_ConfigChannel(&hadc3, &sConfig) != HAL_OK)
{
Error_Handler(); // 如果配置失败,调用错误处理函数
}
// 配置第2个通道:通道1
sConfig.Channel = ADC_CHANNEL_1; // 通道1
sConfig.Rank = ADC_REGULAR_RANK_2; // 常规序列中的第2个通道
if (HAL_ADC_ConfigChannel(&hadc3, &sConfig) != HAL_OK)
{
Error_Handler(); // 如果配置失败,调用错误处理函数
}
// 开启ADC转换,通常在主程序中启动
HAL_ADC_Start(&hadc3);
return 0; // 返回0表示初始化成功
}
1.2 注入模式
-
转换触发方式:常规一样,但是独立于常规模式的触发,可以在任意时间插入采样
-
注入模式启动,会中断常规模式转换,优先完成注入模式的采样
-
通常不使用 DMA
-
转换结果存储在专用的注入数据寄存器(
ADC_JDRx
)中 -
每次触发可以采样一个或多个通道(
InjectedNbrOfConversion
用于配置注入通道数)。
1.2.1 CUBEMX中的配置(参考)
可以通过配置CUBEMX中的配置进行参考,这里也使用的是注入模式的三个通道,IN0 1 和Temperature,这个当作参考,代码我删了一个通道。
其中IN1的single-ended表示的是:ADC 只采集输入信号相对于地的电压值(单端输入),比如VCC是3.3V,GND是0V,那么输入信号0V--->ADC读取0,输入信号3.3V--->ADC读取4095(分辨率12位)。看配置模块的要求,信号直接连接。
还有一个是differential模式(差分模式):采取差分信号进行读取数据,抗干扰能力强。这个除非使用的是差分信号接口才选择这个
1.2.2 初始化代码
下面的代码按照:配置ADC参数、初始化ADC、启动校准、配置注入模式的参数、写入注入通道顺序写的
int32_t stm32_adc_init(void)
{
// 定义并初始化一个注入模式配置结构体变量
ADC_InjectionConfTypeDef sConfigInjected = {0};
// 初始化ADC3的实例
hadc3.Instance = ADC3;
// 配置ADC的初始化参数
hadc3.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV4; // 异步时钟分频因子设为4
hadc3.Init.Resolution = ADC_RESOLUTION_12B; // 分辨率为12位
hadc3.Init.DataAlign = ADC3_DATAALIGN_RIGHT; // 数据右对齐
hadc3.Init.ScanConvMode = ADC_SCAN_ENABLE; // 启用扫描模式
hadc3.Init.EOCSelection = ADC_EOC_SINGLE_CONV; // 每次单次转换结束触发EOC
hadc3.Init.LowPowerAutoWait = DISABLE; // 禁用低功耗模式
hadc3.Init.ContinuousConvMode = DISABLE; // 禁用连续转换模式
hadc3.Init.NbrOfConversion = 1; // 常规模式中转换数量为1
hadc3.Init.DiscontinuousConvMode = DISABLE; // 禁用不连续模式
hadc3.Init.DMAContinuousRequests = DISABLE; // 禁用DMA连续请求
hadc3.Init.SamplingMode = ADC_SAMPLING_MODE_NORMAL; // 使用普通采样模式
hadc3.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DR; // 数据直接存储到数据寄存器
hadc3.Init.Overrun = ADC_OVR_DATA_PRESERVED; // 数据溢出时保持旧数据
hadc3.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE; // 禁用左移位操作
hadc3.Init.OversamplingMode = DISABLE; // 禁用过采样
// 初始化ADC,检查初始化是否成功
if (HAL_ADC_Init(&hadc3) != HAL_OK)
{
Error_Handler(); // 如果失败,调用错误处理函数
}
// 启动ADC校准,使用偏移校准和单端模式
HAL_ADCEx_Calibration_Start(&hadc3, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED);
// 配置注入通道的第一个通道:通道0
sConfigInjected.InjectedChannel = ADC_CHANNEL_0; // 注入通道为0
sConfigInjected.InjectedRank = ADC_REGULAR_RANK_1; // 注入序列中的第1个通道
sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_810CYCLES_5; // 采样时间为810.5周期
sConfigInjected.InjectedSingleDiff = ADC_SINGLE_ENDED; // 单端输入模式
sConfigInjected.InjectedOffsetNumber = ADC_OFFSET_NONE; // 不使用偏移校正
sConfigInjected.InjectedOffset = 0; // 偏移量为0
sConfigInjected.InjectedNbrOfConversion = 2; // 注入模式中有2个通道
sConfigInjected.InjectedDiscontinuousConvMode = DISABLE; // 禁用注入的不连续模式
sConfigInjected.AutoInjectedConv = DISABLE; // 禁用自动注入
sConfigInjected.QueueInjectedContext = DISABLE; // 禁用注入上下文队列
sConfigInjected.ExternalTrigInjecConv = ADC_INJECTED_SOFTWARE_START; // 使用软件触发注入模式
sConfigInjected.ExternalTrigInjecConvEdge = ADC_EXTERNALTRIGINJECCONV_EDGE_NONE; // 无触发边沿
sConfigInjected.InjecOversamplingMode = DISABLE; // 禁用注入模式的过采样
// 配置第一个注入通道
if (HAL_ADCEx_InjectedConfigChannel(&hadc3, &sConfigInjected) != HAL_OK)
{
Error_Handler(); // 如果配置失败,调用错误处理函数
}
// 配置第二个注入通道:通道1
sConfigInjected.InjectedChannel = ADC_CHANNEL_1; // 注入通道为1
sConfigInjected.InjectedRank = ADC_INJECTED_RANK_2; // 注入序列中的第2个通道
if (HAL_ADCEx_InjectedConfigChannel(&hadc3, &sConfigInjected) != HAL_OK)
{
Error_Handler(); // 如果配置失败,调用错误处理函数
}
// 开始注入模式的转换
HAL_ADCEx_InjectedStart(&hadc3);
return 0; // 返回0表示初始化成功
}
1.3 使用
配置完初始化之后,可以通过以下方式调用读取:这里用的是轮询模式,即阻塞模式读取;还可以用DMA(配置有一点区别,函数中调用不同的函数开启,适合连续采样)、和中断方式(实时处理采样数据,转换完成时触发中断,将采集的函数放在中断回调函数里面,通常用这个)。
#include "main.h"
int main(void)
{
HAL_Init(); // 初始化HAL库
SystemClock_Config(); // 配置系统时钟
stm32_adc_init(); // 初始化ADC注入模式
uint32_t values[3]; // 用于存储3个注入通道的ADC值
while (1)
{
/*注入模式:
// 触发注入模式转换(软件触发)
HAL_ADCEx_InjectedStart(&hadc3);
// 等待转换完成(注入模式支持轮询方式)
if (HAL_ADCEx_PollForInjectedConversion(&hadc3, HAL_MAX_DELAY) == HAL_OK)
{
// 从注入数据寄存器中读取结果
values[0] = HAL_ADCEx_InjectedGetValue(&hadc3, ADC_INJECTED_RANK_1);
values[1] = HAL_ADCEx_InjectedGetValue(&hadc3, ADC_INJECTED_RANK_2);
}
*/
/*常规模式:
// 启动ADC
HAL_ADC_Start(&hadc3);
// 等待转换完成(轮询模式)
for (uint32_t i = 0; i < 2; i++)
{
// 等待当前通道转换完成
if (HAL_ADC_PollForConversion(&hadc3, HAL_MAX_DELAY) == HAL_OK)
{
// 读取当前通道的转换值
adc_values[i] = HAL_ADC_GetValue(&hadc3);
}
}
HAL_ADC_Stop(&hadc3); // 停止ADC转换
*/
}
}