1 ADC基础概念
模数转换器(Analog-to-Digital Converter, ADC)是一种将连续的模拟信号转换成离散数字信号的器件。在STM32微控制器中,ADC模块扮演着连接模拟世界与数字世界的桥梁角色。它能够将外部传感器输入的模拟电压值转换为微控制器可以处理的数字量。
模数转换器(ADC)是将模拟信号转换为数字信号的关键模块。STM32的ADC具有高精度、多通道、可编程采样时间等特点。基本原理是通过对输入的模拟电压进行采样、保持和量化,最终转换为二进制数字信号。
1.1 ADC转换的基本步骤
ADC转换过程主要包含三个关键步骤:采样、量化和编码。
-
采样:
-
将连续的模拟信号在时间上离散化,按照一定的采样频率取样。
-
遵循奈奎斯特采样定理,采样频率应至少为信号最高频率的两倍,以避免混叠。
-
-
量化:
-
将采样得到的电压值映射到最接近的数字值。
-
量化误差是量化过程中产生的误差,影响信号的信噪比(SNR)。
-
-
编码:
-
将量化后的值转换成二进制代码。
-
ADC的位数决定了其分辨率,位数越高,表示的精度越高。
-
1.2 分辨率
转换精度由下面的公式决定:
分辨率 = Vref / 2^n
其中,Vref是参考电压,n是ADC位数(STM32通常为12位)。对于STM32的ADC来说,其核心特征是12位分辨率,这意味着可以将输入电压范围分成2^12 = 4096个等级。如果我们使用3.3V作为参考电压(Vref),那么理论上的最小分辨率为:分辨率 = 3.3V / 4096 ≈ 0.8mV
这意味着ADC能够分辨的最小电压变化约为0.8mV。实际使用中的电压值计算公式为:
实际电压值 = (ADC转换结果 × Vref) / 4096
1.3 电压选择
STM32的ADC模块支持多种参考电压源选择:
- 内部参考电压(1.2V)
- VDDA引脚电压(通常为3.3V)
- 外部参考电压(VREF+和VREF-引脚)
ADC的精度受多个因素影响:
- 参考电压的稳定性
- 采样时间的设置
- 输入信号源阻抗
- 环境温度
- 电源噪声
为了提高ADC转换的准确性,STM32提供了校准功能。校准过程会补偿ADC内部的偏移误差,建议在使用ADC之前进行校准。校准完成后,转换结果的准确度会显著提高。
1.4 其他功能
STM32的ADC还具备温度传感器和内部参考电压检测功能。内置温度传感器可用于监测芯片温度,帮助实现温度补偿或过温保护功能。内部参考电压检测则可用于系统电源监控。
在噪声处理方面,ADC模块提供了多种抗干扰措施:
- 可编程采样时间:较长的采样时间有助于抑制高频噪声
- 硬件过采样:通过多次采样取平均值提高精度
- 数字滤波:利用软件算法对采样结果进行滤波处理
2 ADC模块结构
ADC核心结构组成: ADC模块的核心是一个12位的逐次逼近型ADC转换器,它通过内部总线与其他功能模块相连。转换器前端是一个多路模拟开关矩阵,用于选择输入通道。转换器后端则是数据寄存器组,用于存储转换结果。整个模块由控制逻辑单元统一管理。
2.1 输入通道多路开关
STM32的ADC提供了最多18个外部输入通道和3个内部输入通道。外部通道可以连接到不同的GPIO引脚,用于采集外部模拟信号。内部通道包括内置温度传感器、内部参考电压和VBAT电压检测。多路开关矩阵确保在任何时刻只有一个通道与ADC转换器相连,避免信号串扰。
2.2 采样保持电路
在ADC转换器前端设有采样保持电路,它由采样开关、保持电容和缓冲放大器组成。当采样信号有效时,采样开关闭合,输入电压对保持电容进行充电。采样完成后,开关断开,电容电压保持稳定,确保在整个转换过程中输入电压不发生变化。采样时间可以通过软件编程设置,范围从1.5个ADC时钟周期到239.5个周期不等。
2.3 逐次逼近寄存器(SAR)
这是ADC的核心转换单元,采用逐次逼近算法实现模拟量到数字量的转换。转换过程从最高位开始,通过内部DAC产生比较电压,与输入电压逐位比较,最终得到12位数字结果。整个转换过程需要12.5个ADC时钟周期。
2.4 数据对齐和存储
转换结果可以选择左对齐或右对齐方式存储在16位数据寄存器中。对于12位ADC来说,右对齐时有效位在低12位,左对齐时有效位在高12位。数据寄存器还具备双缓冲功能,可以在读取上一次转换结果的同时进行新的转换。
2.5 触发控制单元
ADC的启动可以通过软件触发或硬件触发。硬件触发源包括:
- 定时器
- 外部中断线
- EXTI信号
- 其他外设触发信号
触发控制单元负责管理这些触发源,并确保转换过程的正确启动。
2.6 DMA接口
ADC模块集成了DMA请求生成器,支持通过DMA方式自动传输转换结果,无需CPU干预。这在需要连续采样或多通道扫描时特别有用,可以显著减少CPU负担。
2.7 时钟和控制电路
ADC模块有独立的时钟域,时钟来源可以是PCLK2分频得到。时钟频率直接影响采样率和转换时间。控制电路负责管理ADC的工作状态、中断生成和标志位设置。
2.8 工作模式控制
ADC支持多种工作模式:
- 单次转换模式:完成一次转换后停止
- 连续转换模式:自动连续进行转换
- 扫描模式:自动扫描多个通道
- 间断模式:在扫描模式下分组进行转换
- 注入模式:可以中断正常序列执行优先转换
2.9 状态监控和中断
ADC模块提供多个状态标志:
- 转换完成标志
- 注入转换完成标志
- 规则通道起始标志
- 注入通道起始标志
- 模拟看门狗标志 这些标志位可以触发相应的中断,方便软件及时处理转换结果。
3 ADC工作模式
3.1 单次转换模式
这是最基本的工作模式。在该模式下,每次触发只执行一次AD转换。转换完成后,ADC停止工作并置位EOC(转换结束)标志位。这种模式适用于采样频率较低或不需要固定采样周期的场合,比如按键电压检测、电池电量监测等。其特点是功耗低,控制简单。示例代码:
// 配置单次转换模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
// 启动单次转换
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
uint16_t value = ADC_GetConversionValue(ADC1);
3.2 连续转换模式
在连续转换模式下,ADC自动重复进行转换,无需软件干预。每次转换完成后,立即开始下一次转换。这种模式适用于需要高速连续采样的场合,如音频信号采集、波形监测等。为了避免数据丢失,通常需要配合DMA使用。示例代码:
// 配置连续转换模式
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
// 配置DMA
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE;
3.3 扫描模式
扫描模式允许ADC自动扫描多个通道。可以配置最多16个规则通道,按照预设顺序依次转换。每个通道可以独立设置采样时间。这种模式适用于需要同时监测多个模拟量的场合,如多路传感器采集。扫描模式可以与连续转换模式结合使用。示例代码:
// 配置扫描模式
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_NbrOfChannel = 3; // 设置扫描通道数
// 配置各通道
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5);
3.4 间断模式
间断模式是扫描模式的一个变体。它允许将规则通道分组,每次触发只转换一组通道。这种模式可以降低DMA传输的实时性要求,适用于采样速度要求不高但通道数较多的场合。示例代码:
// 配置间断模式
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_DiscontinuousConvMode = ENABLE;
ADC_InitStructure.ADC_NbrOfDiscConversion = 2; // 每组2个通道
3.5 注入模式
注入模式提供了4个高优先级的转换通道。注入转换可以中断正常转换序列,优先执行。转换完成后返回继续执行被中断的正常转换。这种模式适用于需要及时响应的紧急信号采集,如过流保护、过压检测等。示例代码:
// 配置注入通道
ADC_InjectedChannelConfig(ADC1, ADC_Channel_3, 1, ADC_SampleTime_55Cycles5);
ADC_InjectedChannelConfig(ADC1, ADC_Channel_4, 2, ADC_SampleTime_55Cycles5);
// 软件触发注入转换
ADC_SoftwareStartInjectedConvCmd(ADC1, ENABLE);
while(ADC_GetFlagStatus(ADC1, ADC_FLAG_JEOC) == RESET);
uint16_t injected_value = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_1);
3.6 触发模式
除了软件触发,ADC还支持多种硬件触发源:
- 定时器触发:可以实现精确的采样时序
- 外部引脚触发:响应外部事件
- 其他外设触发:与其他模块协同工作
示例定时器触发代码:
// 配置定时器触发
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO;
TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update);
在实际应用中,常常需要组合使用多种模式。例如,可以配置扫描模式+连续转换+DMA传输,实现多通道高速采样。或者配置间断模式+定时器触发,实现多通道定时采样。选择合适的工作模式组合,对于提高系统性能和可靠性至关重要。
4 采样时间和精度
4.1 采样时间基础概念
采样时间是ADC对输入信号进行采样所需的时间周期。在STM32中,采样时间是可编程的,提供了1.5、7.5、13.5、28.5、41.5、55.5、71.5和239.5个ADC时钟周期的选择。采样时间直接影响ADC的转换精度和最大采样率。完整的转换时间计算公式为:
总转换时间 = 采样时间 + 12.5个周期(12位转换)
例如,如果选择55.5个周期的采样时间,则完整转换时需要: 55.5 + 12.5 = 68个ADC时钟周期
4.2 采样时间与信号源阻抗
采样时间的选择需要考虑信号源的内阻。ADC内部等效为一个采样电容(Cs≈5pF),在采样开关闭合时,输入信号通过源内阻对采样电容充电。充电时间常数τ = R × C,其中R是信号源内阻,C是采样电容。为确保采样精度,采样时间应该大于5个时间常数(5τ)。计算公式:
最小采样时间 = 5 × R × C
例如,当信号源内阻为10kΩ时: 最小采样时间 = 5 × 10kΩ × 5pF = 250ns
4.3 温度对精度的影响
ADC的精度受温度变化影响显著。主要表现在:
- 偏移误差随温度变化
- 增益误差随温度变化
- 参考电压温漂
为补偿温度影响,可采取以下措施:
// 读取内部温度传感器
ADC_RegularChannelConfig(ADC1, ADC_Channel_TempSensor, 1, ADC_SampleTime_239Cycles5);
ADC_TempSensorVrefintCmd(ENABLE);
// 温度计算公式
float temperature = ((V25 - ADC_ConvertedValue) / Avg_Slope) + 25;
4.4 参考电压与精度
ADC的精度直接依赖于参考电压的稳定性。STM32提供多种参考电压选择:
- 内部1.2V基准
- VDDA电源电压
- 外部参考电压
不同参考电压源的精度特征:
- 内部基准:温漂较大,但不受外部干扰
- VDDA参考:精度依赖电源质量
- 外部参考:可获得最高精度,但需要额外器件
4.5 提高ADC精度的关键措施
- 校准补偿:
// ADC校准流程
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
- 过采样技术:
通过多次采样取平均值提高精度。有效位数增加计算公式: 增加的有效位数 = log2(√N),其中N为采样次数
示例代码:
uint16_t ADC_GetAverageValue(uint8_t times) {
uint32_t sum = 0;
for(uint8_t i = 0; i < times; i++) {
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
sum += ADC_GetConversionValue(ADC1);
}
return (uint16_t)(sum / times);
}
- 数字滤波:
- 中值滤波:去除脉冲干扰
- 滑动平均:抑制随机噪声
uint16_t ADC_MedianFilter(uint16_t* buffer, uint8_t size) {
// 冒泡排序
for(uint8_t i = 0; i < size-1; i++) {
for(uint8_t j = 0; j < size-1-i; j++) {
if(buffer[j] > buffer[j+1]) {
uint16_t temp = buffer[j];
buffer[j] = buffer[j+1];
buffer[j+1] = temp;
}
}
}
return buffer[size/2];
}
4.6 采样率与精度权衡
高采样率和高精度往往是矛盾的。需要根据应用场景合理平衡:
- 快速信号监测:优先采样率,可适当牺牲精度
- 精密测量:优先精度,可适当降低采样率
- 一般应用:采用中等采样时间,配合数字滤波
4.7 提高整体精度的建议
- 选择合适的参考电压源
- 根据信号源特性设置足够的采样时间
- 进行ADC校准
- 采用过采样和滤波技术
- 注意PCB布局布线,隔离数字噪声
- 必要时增加模拟前端调理电路
5 ADC硬件设计
5.1 电源系统设计
ADC的性能很大程度上依赖于稳定的电源供应。VDDA(模拟电源)和VSSA(模拟地)必须特别注意供电质量。推荐使用独立的LDO为ADC供电,并采用多级滤波方案。典型的滤波电路包括:
- 电源入口处使用10μF钽电容
- VDDA引脚附近放置1μF陶瓷电容
- 去耦电容应尽量靠近引脚放置
- 串联铁氧体磁珠隔离数字噪声
5.2 地平面设计
正确的地平面设计对于ADC精度至关重要。建议采用以下策略:
- 模拟地和数字地采用单点连接
- 模拟地平面应完整,避免被其他信号切割
- ADC周围的地平面应连续完整
- 地平面应位于信号层正下方,提供良好的返回路径
- 避免地平面下方布设高速数字信号
5.3 参考电压设计
当使用外部参考电压时,需要特别注意:
- 选择低温漂、低噪声的基准源芯片
- 参考电压源应有独立的电源滤波
- VREF+和VREF-走线应采用差分布线
- 参考电压源靠近ADC放置
- 布线时避免与数字信号交叉
5.4 信号调理电路设计
为确保ADC输入信号的质量,通常需要设计合适的信号调理电路:
传感器 -> 放大器 -> 滤波器 -> ADC输入
5.5 典型的信号调理电路包括
- 输入保护:
- TVS二极管保护
- RC低通滤波
- 串联限流电阻(通常100Ω-1kΩ)
- 电平转换:
- 运算放大器调理电路
- 分压电路(注意分压电阻不宜过大)
- 电平搬移电路
- 抗混叠滤波:
- RC低通滤波器
- 有源滤波器
- 滤波截止频率应小于采样频率的1/2
5.6 PCB布局注意事项
- ADC相关元件布局建议:
- ADC周围预留足够隔离距离
- 去耦电容靠近电源引脚放置
- 模拟信号走线应远离数字部分
- 关键信号采用差分布线
- 关键走线规则:
- 模拟信号走线宽度建议8-12mil
- 差分信号等长等宽布线
- 避免信号线下方开槽
- 转角采用45度角或圆弧
5.7 屏蔽设计
对于高精度ADC应用,可能需要考虑屏蔽措施:
- PCB级屏蔽:
- 在ADC区域增加接地铜皮
- 关键信号走线两侧布设地线
- 必要时使用埋地过孔环绕关键区域
- 系统级屏蔽:
- 金属外壳接地
- 信号线使用屏蔽电缆
- 接口处增加EMI滤波器
5.8 输入阻抗匹配
ADC输入等效电路包含采样开关和采样电容,需要考虑源阻抗匹配:
- 信号源阻抗不宜过大(建议<10kΩ)
- 必要时使用运算放大器做阻抗转换
- 考虑采样电容的充放电时间
5.9 温度考虑
ADC性能受温度影响显著,在布局时应注意:
- 远离发热元件
- 必要时增加散热措施
- 关键器件采用优质导热设计
- 考虑温度传感器的放置位置
5.10 电磁兼容性(EMC)
为提高系统抗干扰能力:
- 关键信号走线避开高速数字信号
- 使用差分信号传输
- 增加共模电感滤波
- 必要时使用光耦隔离
6 软件设计实践
以下是一个完整的ADC采样实现示例:
void ADC_Configuration(void) {
// GPIO配置
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// ADC配置
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
// 配置通道采样时间
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
ADC_Cmd(ADC1, ENABLE);
// ADC校准
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
}
uint16_t Get_ADC_Value(void) {
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
return ADC_GetConversionValue(ADC1);
}
7 常见应用场景
7.1 电压测量系统
这是ADC最基础的应用。可用于电源监控、电池电量检测等场景。实现时需要注意分压电路的设计,确保输入电压在ADC量程范围内。
// 电压测量示例代码
float Get_Voltage(void) {
uint16_t adcValue = ADC_GetConversionValue(ADC1);
// 假设使用分压比为11:1的分压电路,参考电压3.3V
float voltage = (float)adcValue / 4096 * 3.3 * 11;
return voltage;
}
7.2 温度传感器接口
STM32的ADC可以连接多种温度传感器,如NTC热敏电阻、PT100、热电偶等。以NTC为例,需要通过查表或计算得到温度值:
float Get_Temperature(void) {
uint16_t adcValue = ADC_GetConversionValue(ADC1);
float resistance = Calculate_NTC_Resistance(adcValue);
// B值方程计算温度
float temperature = 1.0 / (1.0 / 298.15 + log(resistance/10000.0) / 3950.0) - 273.15;
return temperature;
}
7.3 电流检测系统
通过采样分流电阻两端电压来实现电流测量。常用于电机控制、过流保护等场景。为提高精度,常使用运算放大器放大分流电压:
// 电流检测示例代码
float Get_Current(void) {
uint16_t adcValue = ADC_GetConversionValue(ADC1);
// 假设分流电阻0.1Ω,放大倍数20
float current = (float)adcValue / 4096 * 3.3 / (0.1 * 20);
return current;
}
7.4 光照强度测量
使用光敏电阻或光电二极管检测环境光强,应用于自动调光、光控开关等场景:
uint8_t Get_Light_Level(void) {
uint16_t adcValue = ADC_GetConversionValue(ADC1);
// 将ADC值映射到0-100的光照等级
uint8_t lightLevel = (uint8_t)(adcValue * 100 / 4096);
return lightLevel;
}
7.5 压力传感器应用
压力传感器常用于工业控制、气象监测等场景。通常需要进行温度补偿:
float Get_Pressure(void) {
uint16_t pressureADC = ADC_GetConversionValue(ADC1);
uint16_t tempADC = ADC_GetConversionValue(ADC2);
// 温度补偿计算
float temperature = Convert_To_Temperature(tempADC);
float pressure = Calculate_Pressure_With_Comp(pressureADC, temperature);
return pressure;
}
7.6 多路数据采集系统
在工业监控等场景中,常需要同时采集多路信号。使用扫描模式配合DMA实现:
#define CHANNEL_NUM 4
uint16_t ADC_Values[CHANNEL_NUM];
void ADC_DMA_Config(void) {
// DMA配置
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_BufferSize = CHANNEL_NUM;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&ADC_Values;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
// ... 其他DMA配置
// ADC配置
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_NbrOfConversion = CHANNEL_NUM;
}
7.7 电机控制应用
在无刷电机控制中,需要采集反电动势、相电流等参数:
void BLDC_Current_Measure(void) {
// 配置ADC注入组用于相电流采样
ADC_InjectedChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_7Cycles5);
ADC_InjectedChannelConfig(ADC1, ADC_Channel_2, 2, ADC_SampleTime_7Cycles5);
ADC_InjectedChannelConfig(ADC1, ADC_Channel_3, 3, ADC_SampleTime_7Cycles5);
// 使用定时器触发ADC采样
ADC_ExternalTrigInjectedConvConfig(ADC1, ADC_ExternalTrigInjecConv_T1_CC4);
}
7.8 波形采集系统
用于信号分析、故障诊断等场景,需要高速采样和大容量存储:
#define SAMPLE_NUM 1024
uint16_t waveBuf[SAMPLE_NUM];
void Wave_Capture(void) {
// 配置高速ADC采样
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_SampleTime = ADC_SampleTime_1Cycles5;
// 配置DMA存储
DMA_InitStructure.DMA_BufferSize = SAMPLE_NUM;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)waveBuf;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
}
7.9 电池管理系统
需要监测电池电压、充电电流、温度等参数:
typedef struct {
float voltage;
float current;
float temperature;
uint8_t percentage;
} Battery_Info_t;
Battery_Info_t Get_Battery_Info(void) {
Battery_Info_t info;
// 采集多路ADC数据
info.voltage = Get_Battery_Voltage();
info.current = Get_Charge_Current();
info.temperature = Get_Battery_Temperature();
info.percentage = Calculate_Battery_Percentage(info.voltage);
return info;
}
在实际应用中,往往需要根据具体场景组合使用这些功能,并考虑抗干扰、精度补偿、故障保护等方面的需求。
标签:采样,DMA,转换,入门,InitStructure,ADC,ADC1,模数转换器 From: https://blog.csdn.net/qq_56869120/article/details/145024776