ADC模数转换在stm32上的应用
文章目录
- ADC模数转换在stm32上的应用
-
- [1. 什么是ADC?](#1. 什么是ADC?)
- [2. stm32中的ADC](#2. stm32中的ADC)
- [3. 12位逐次逼近型ADC](#3. 12位逐次逼近型ADC)
-
- [1. 工作原理](#1. 工作原理)
- [2. 主要特性](#2. 主要特性)
- [3. 关键部件](#3. 关键部件)
- [4. 优点](#4. 优点)
- [5. 缺点](#5. 缺点)
- [6. 应用](#6. 应用)
- [7. 配置和使用](#7. 配置和使用)
- [4. stm32的ADC转换模式](#4. stm32的ADC转换模式)
-
- [1. 单次转换模式 (Single Conversion Mode)](#1. 单次转换模式 (Single Conversion Mode))
- [2. 连续转换模式 (Continuous Conversion Mode)](#2. 连续转换模式 (Continuous Conversion Mode))
- [3. 扫描模式 (Scan Mode)](#3. 扫描模式 (Scan Mode))
- [4. 多ADC同步模式 (Dual/Multi-ADC Synchronization Mode)](#4. 多ADC同步模式 (Dual/Multi-ADC Synchronization Mode))
- [5. 触发和外部事件管理](#5. 触发和外部事件管理)
- [5. ADC单通道&多通道转换](#5. ADC单通道&多通道转换)
1. 什么是ADC?
ADC是"Analog-to-Digital Converter"(模数转换器)的缩写。它是一种电子器件或电路,用于将连续变化的模拟信号(如电压或电流)转换为数字信号(通常是二进制码),这样计算机和数字设备就可以处理这些信号。
在许多电子系统中,比如音频设备、通信系统、测量仪器等,ADC都是一个关键组件。通过ADC,真实世界的物理信号(如声音、温度、压力等)可以被转换成数字形式,进而被微处理器或计算机分析和处理。
ADC的主要参数包括:
- 分辨率:表示转换后的数字信号能表示多少个离散值,通常以位数表示(例如8位、12位、16位等)。
- 采样率:单位时间内能够完成的转换次数,通常用Hz(赫兹)来表示。
- 量化误差:由于数字信号只能近似表示模拟信号,这种近似带来的误差称为量化误差。
- 信噪比(SNR):信号功率与噪声功率的比值,反映了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. 主要特性
- 分辨率:12位ADC可以分辨出输入电压的4096个不同级别((2^{12} = 4096))。
- 量化误差:量化误差是指ADC输出的数字值与实际模拟输入之间的最大偏差。对于12位ADC,量化误差通常为满量程电压的1/4096。
- 转换时间:转换时间取决于时钟频率和所需的位数。对于12位ADC,如果每个比特需要一个时钟周期,则转换时间大约为12个时钟周期加上一些额外的时间(如建立时间和稳定时间)。
3. 关键部件
- 比较器:用于比较输入电压与DAC输出。
- 数字-模拟转换器(DAC):用于生成模拟电压供比较器比较。
- 逐次逼近寄存器(SAR):存储每次迭代中确定的比特位。
- 控制逻辑:控制整个转换过程,包括时序和位的更新。
4. 优点
- 速度快:逐次逼近型ADC通常比其他类型的ADC(如积分型ADC)更快。
- 易于实现:硬件结构简单,便于集成。
- 成本效益高:适用于大多数中低速应用,具有良好的性价比。
5. 缺点
- 功耗:由于需要快速转换,逐次逼近型ADC相对于积分型ADC而言功耗较高。
- 精度限制:虽然12位分辨率已经足够高,但在需要更高精度的情况下可能不是最佳选择。
6. 应用
- 数据采集系统:用于测量传感器输出的模拟信号。
- 音频处理:用于将麦克风等音频源的模拟信号转换为数字信号。
- 通信系统:用于接收端的信号处理。
- 工业自动化:用于监控和控制系统的信号采集。
7. 配置和使用
配置12位逐次逼近型ADC通常涉及以下几个步骤:
- 设置参考电压:确定ADC的参考电压范围。
- 选择时钟频率:根据转换速度需求选择合适的时钟频率。
- 配置分辨率:尽管是12位ADC,但某些情况下可能需要降低分辨率以提高转换速度。
- 设置触发源:选择硬件触发还是软件触发。
- 配置中断:设置中断使能,以便在转换完成后接收中断通知。
4. stm32的ADC转换模式
STM32 微控制器系列提供了多种 ADC(Analog-to-Digital Converter)转换模式来适应不同的应用场景。
1. 单次转换模式 (Single Conversion Mode)
- 描述:在这种模式下,每次启动转换后,ADC 将执行一次转换,然后进入待机状态等待下一次启动。
- 用途:适合不需要连续采样的场合,比如测量按键状态或温度等。
2. 连续转换模式 (Continuous Conversion Mode)
- 描述:在这种模式下,一旦启动转换,ADC 将连续不断地执行转换,直到通过软件停止或硬件事件自动停止。
- 用途:适合需要连续采集数据的应用场景,例如实时信号采集。
3. 扫描模式 (Scan Mode)
- 描述:在扫描模式下,ADC 可以依次转换一组通道,每个通道转换结束后会自动切换到下一个通道,直到完成所有指定通道的转换。
- 用途:当需要对多个通道进行连续采样时非常有用,比如多路传感器数据采集。
4. 多ADC同步模式 (Dual/Multi-ADC Synchronization Mode)
- 描述:STM32 的一些型号支持两个或更多 ADC 的同步操作,可以实现多个 ADC 在相同时间点开始转换,这对于需要同步采集多路信号的应用非常有用。
- 用途:例如在电机控制中,需要同时读取三相电流或电压。
5. 触发和外部事件管理
- 描述:STM32 ADC 支持由内部或外部事件触发转换开始。这些触发可以来自定时器的输出比较单元、外部引脚变化或其他 ADC 的转换结束。
- 用途:可以用来实现精确的采样时机控制,比如与 PWM 输出同步采集信号。
5. ADC单通道&多通道转换
-
启用ADC和GPIO时钟:
cRCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
- 这两行代码启用了ADC1外设和GPIOA端口的时钟,这是配置它们之前必须做的步骤。
-
配置ADC时钟分频:
cRCC_ADCCLKConfig(RCC_PCLK2_Div6);
- 这行设置了ADC时钟为APB2总线时钟(PCLK2)的六分之一。ADC时钟频率不应超过其最大限制,通常约为14 MHz。
-
配置GPIO作为模拟输入:
cGPIO_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操作不直接相关,但需要正确配置。
-
配置常规通道:
cADC_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:
cADC_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个。
- 这段代码初始化ADC1:
-
使能ADC并开始校准:
cADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1); while (ADC_GetResetCalibrationStatus(ADC1) == SET); ADC_StartCalibration(ADC1); while (ADC_GetCalibrationStatus(ADC1) == SET);
- 使能ADC1。
- 开始重置校准,等待校准重置完成。
- 开始校准过程,等待校准完成。
这个函数用于获取ADC转换后的数值。
-
启动软件触发的转换:
cADC_SoftwareStartConvCmd(ADC1, ENABLE);
- 启动ADC的软件触发转换。
-
等待转换完成:
cwhile (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
- 等待直到转换结束标志位被置位。
-
读取转换结果:
creturn ADC_GetConversionValue(ADC1);
- 返回ADC转换得到的数值。
单通道转换完整代码
cpp
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作为模拟输入:
cGPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
- 这里配置了GPIOA的引脚0、1、2和3为模拟输入,而不是只配置引脚0。
-
AD_GetValue
函数接受一个参数:cuint16_t AD_GetValue(uint8_t ADC_Channel)
- 这个函数现在接受一个
uint8_t
类型的参数ADC_Channel
,表示要读取哪个ADC通道的数据。
- 这个函数现在接受一个
-
配置指定的ADC通道:
cADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5);
- 在
AD_GetValue
函数中,使用传递进来的ADC_Channel
参数来配置ADC通道,而不是固定配置通道0。
- 在
-
其他部分保持不变:
- 其他部分如ADC的初始化、使能、校准等没有变化。
完整
cpp
#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);
}