ADC模数转换在stm32上的应用
ADC模数转换在stm32上的应用
文章目录
1. 什么是ADC?
ADC是"Analog-to-Digital Converter"(模数转换器)的缩写。它是一种电子器件或电路,用于将连续变化的模拟信号(如电压或电流)转换为数字信号(通常是二进制码),这样计算机和数字设备就可以处理这些信号。
在许多电子系统中,比如音频设备、通信系统、测量仪器等,ADC都是一个关键组件。通过ADC,真实世界的物理信号(如声音、温度、压力等)可以被转换成数字形式,进而被微处理器或计算机分析和处理。
ADC的主要参数包括:
ADC的类型有很多种,常见的有逐次逼近型(SAR)、积分型(Sigma-Delta)、并行比较型(Flash)等。每种类型的ADC都有其特定的应用场景和技术优势。
2. stm32中的ADC
STM32 微控制器中的 ADC(Analog-to-Digital Converter)是一种模拟到数字转换器,用于将模拟信号转换为数字信号,以便微控制器可以处理这些信号。STM32 系列微控制器提供了多种型号,而不同的型号可能拥有不同数量和特性的 ADC。
以下是一些关于 STM32 中 ADC 的主要特点:
-
类型:
- STM32 的 ADC 通常采用 12 位逐次逼近型设计,这意味着它可以提供 4096 (2^12) 个不同的数字输出值,对应于输入电压的变化。
-
分辨率:
- 12 位的分辨率意味着 ADC 可以区分出输入电压的微小变化,最小分辨率为 VREF / 4096。
-
通道数:
- 不同型号的 STM32 可能有不同的通道数。例如,STM32F10X 系列支持最多 18 个通道,其中 16 个可以连接到外部信号源,另外 2 个用于内部信号源。
- STM32F4 系列支持多达 19 个复用通道,可以测量来自 16 个外部源和 2 个内部源的信号。
-
转换模式:
- 单次转换模式:每次只进行一次转换。
- 连续转换模式:在连续模式下,ADC 会连续不断地进行转换,直到被软件停止。
- 扫描模式:此模式下,ADC 会在指定的多个通道上依次进行转换。
- 间断模式:允许在多个连续转换之间插入空闲周期,以减少功耗。
-
触发源:
- ADC 可以通过硬件触发启动转换,例如定时器的中断输出或外部中断线。
-
数据对齐:
- ADC 结果可以左对齐或右对齐在 16 位的数据寄存器中。左对齐意味着最低有效位 (LSB) 在寄存器的最低位位置;右对齐则意味着最高有效位 (MSB) 在寄存器的最高位位置。
-
供电要求:
- 通常,ADC 的供电范围为 2.4V 至 3.6V,而输入电压范围由参考电压 VREF 决定,即 VREF- ≤ VIN ≤ VREF+。
-
校准:
- ADC 提供了校准功能,以确保转换精度。校准可以通过软件命令启动。
-
中断和事件:
- ADC 支持中断机制,在转换结束、注入转换结束以及模拟看门狗事件时可以产生中断。
-
多 ADC 模式:
- 一些 STM32 型号配备了多个 ADC,它们可以独立工作,也可以在双重或三重模式下协同工作,以实现更高的采样率。
3. 12位逐次逼近型ADC
12位逐次逼近型ADC(Analog-to-Digital Converter)是一种常用的模数转换器类型,广泛应用于各种电子设备中。
1. 工作原理
逐次逼近型ADC的工作原理基于比较器和数字-模拟转换器(DAC)。其基本过程如下:
- 初始化:将DAC的输出设为中间值,此时DAC输出为参考电压的一半。
- 比较:将输入模拟电压与DAC输出进行比较。
- 调整:根据比较结果,调整DAC的输出以接近输入电压。
- 重复:重复上述步骤,每次改变DAC输出的一个位,直到所有位都被确定为止。
具体来说,对于12位ADC,这个过程需要进行12次迭代,每次迭代确定一位的值。每次迭代后,DAC输出值都会向输入电压靠近,最终得到最接近输入电压的12位数字值。
2. 主要特性
3. 关键部件
4. 优点
5. 缺点
6. 应用
7. 配置和使用
配置12位逐次逼近型ADC通常涉及以下几个步骤:
4. stm32的ADC转换模式
STM32 微控制器系列提供了多种 ADC(Analog-to-Digital Converter)转换模式来适应不同的应用场景。
1. 单次转换模式 (Single Conversion Mode)
2. 连续转换模式 (Continuous Conversion Mode)
3. 扫描模式 (Scan Mode)
4. 多ADC同步模式 (Dual/Multi-ADC Synchronization Mode)
5. 触发和外部事件管理
5. ADC单通道&多通道转换
-
启用ADC和GPIO时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
- 这两行代码启用了ADC1外设和GPIOA端口的时钟,这是配置它们之前必须做的步骤。
-
配置ADC时钟分频:
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
- 这行设置了ADC时钟为APB2总线时钟(PCLK2)的六分之一。ADC时钟频率不应超过其最大限制,通常约为14 MHz。
-
配置GPIO作为模拟输入:
GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure);
- 初始化GPIOA的第0号引脚为模拟输入模式。GPIO的速度设置为50 MHz,这虽然与ADC操作不直接相关,但需要正确配置。
-
配置常规通道:
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
ADC1
: 指定要配置的ADC实例,在这个例子中是ADC1。ADC_Channel_0
: 指定要配置的ADC通道,这里是ADC通道0。1
: 这个参数指定了在常规序列中的通道位置。在这个例子中,通道0被设置为序列中的第一个通道。ADC_SampleTime_55Cycles5
: 指定了采样时间,即ADC采集模拟信号的时间长度。在这个例子中,采样时间为55.5个ADC时钟周期。-
初始化ADC:
ADC_InitTypeDef ADC_InitStructure; ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_NbrOfChannel = 1; ADC_Init(ADC1, &ADC_InitStructure);
- 这段代码初始化ADC1:
- 设置ADC工作模式为独立模式。
- 数据对齐方式设置为右对齐。
- 外部触发转换禁用。
- 连续转换模式禁用。
- 扫描模式禁用。
- 通道数量设置为1个。
-
使能ADC并开始校准:
ADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1); while (ADC_GetResetCalibrationStatus(ADC1) == SET); ADC_StartCalibration(ADC1); while (ADC_GetCalibrationStatus(ADC1) == SET);
- 使能ADC1。
- 开始重置校准,等待校准重置完成。
- 开始校准过程,等待校准完成。
这个函数用于获取ADC转换后的数值。
-
启动软件触发的转换:
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
- 启动ADC的软件触发转换。
-
等待转换完成:
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
- 等待直到转换结束标志位被置位。
-
读取转换结果:
return ADC_GetConversionValue(ADC1);
- 返回ADC转换得到的数值。
单通道转换完整代码
void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1) == SET);
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1) == SET);
}
uint16_t AD_GetValue(void)
{
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
return ADC_GetConversionValue(ADC1);
}
多通道转换配置流程
与单通道不同的是只需要在部分地方进行改动即可:
-
配置多个GPIO作为模拟输入:
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
- 这里配置了GPIOA的引脚0、1、2和3为模拟输入,而不是只配置引脚0。
-
AD_GetValue
函数接受一个参数:uint16_t AD_GetValue(uint8_t ADC_Channel)
- 这个函数现在接受一个
uint8_t
类型的参数ADC_Channel
,表示要读取哪个ADC通道的数据。 -
配置指定的ADC通道:
ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5);
- 在
AD_GetValue
函数中,使用传递进来的ADC_Channel
参数来配置ADC通道,而不是固定配置通道0。 -
其他部分保持不变:
- 其他部分如ADC的初始化、使能、校准等没有变化。
完整
#include "stm32f10x.h" // Device header
void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1) == SET);
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1) == SET);
}
uint16_t AD_GetValue(uint8_t ADC_Channel)
{
ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
return ADC_GetConversionValue(ADC1);
}
作者:FightingLod