目录

一.ADC简介

二.ADC组成

2.1 逐次逼近型ADC结构

2.2 ADC模块总体结构

三.ADC配置

3.1 时钟源的配置

3.2 ADC触发源选择

3.2.1 软件触发

3.2.2 定时器定时触发

3.2.3 外部中断触发

3.3 ADC转化模式

3.4 ADC对齐模式

3.5 ADC校准

3.6  ADC配置流程

四.ADC代码示例

4.1 ADC单通道+单次转换

4.2 多通道连续转换(带DMA)

五.附STM32F103C8T6引脚定义表


一.ADC简介

ADC(Analog-to-Digital Converter,模数转换器)是嵌入式开发中常用的功能之一,用于将模拟信号(如传感器的电压)转换为数字信号供MCU处理。STM32F1系列的ADC模块支持12位精度,具有多通道和多模式的特点,广泛应用于信号采集、控制等领域。

STM32的ADC是12位逐次逼近型ADC,转换时间为1us,输入电压的范围是0~3.3V,转换结果的数值范围是0~4095(即2^12),有18个输入通道,可测量16个外部(GPIO),和2个内部信号源(内部温度传感器 和 内部参考电压1.2V,不随供电电压的变化而变化,可以测量CPU的温度),有规则组和注入组两个转换单元,以及模拟看门狗自动监测输入电压范围,超出范围可直接通往NVIC申请中断。

二.ADC组成

2.1 逐次逼近型ADC结构

这里以8位ADC为例,STM32的ADC通常是12位的,但两者原理是一样的。

可以通过配置左下角的ADDA,ADDB,ADDC,然后给ALE端口一个锁存信号,就可以选择左上角的哪个INx通道信号输入比较器,经过DAC内部的加权电阻网络,不断输出电压与输入电压进行比较,直到DAC的输出电压与外部输入的电压近似相等,再将DAC的值通过三态锁存缓冲器输出。与此同时,产生EOC结束信号。

2.2 ADC模块总体结构

从图中可以发现,除了对通道的选择外,STM32还对转换的通道进行了分组:注入通道 和 规则通道。下表是他们的不同点:

重点注意事项:

  • 注入组的优先级 比 规则组的优先级更高,可以打断规则组的采集。
  • 注入组的四个通道 正好 对应四个数据寄存器,不会出现数据被别的通道覆盖的现象,但规则组则是16个通道共用一个数据寄存器,需要及时把数据移走,不然在进行多个规则组通道采样时,会出现数据覆盖现象。最好配合DMA来进行,至于什么是DMA?我们后续章节再进行讲解。

总结:

规则组适用于:定期采集多个通道的数据,处理量较大但实时性要求较低的情况。

注入组适用于:实时性要求高的关键信号采集,通常用于紧急处理场景。

三.ADC配置

3.1 时钟源的配置

参考手册的时钟树:

假设当经过AHB分频后的时钟频率为72MHz,则ADC预分频器只能进行 6/8分频,因为这里的ADC最大只能接受14MHz的时钟频率,为什么最大只能接收14MHz的时钟频率呢?还记得我们之前提到的,ADC的最短转换时间是1us吗?

ADC的转换时间计算公式为:

                                Tconv = 采样时间 + 12.5个周期;

当ADC的时钟频率为最大14MHz时,参考手册:

此时采样时间为1.5个周期,所以 Tconv = 采样时间 + 12.5个周期 = 1.5 + 12.5 = 14 个周期,

又因为时钟频率为14MHz,所以总时间为 T = 14 * (1/14 MHz),也就是1us。与ADC的最短转换时间1us不谋而合。

3.2 ADC触发源选择

3.2.1 软件触发

通过调用封装好的标准库函数,直接进行软件触发。

3.2.2 定时器定时触发

可以在定时器的中断里触发,但不推荐这种做法,因为会导致程序频繁的进入中断,会影响到程序的正常运行,更推荐的做法是:将定时器的更新事件映射到TRGO引脚,选择为触发ADC转换,这样就可以实现硬件层面的自动触发。

3.2.3 外部中断触发

通过外部中断,会产生一个脉冲信号,触发ADC转换,具体什么是外部中断,请参考外部中断EXTI篇STM32单片机快速入门——(外部)中断篇_stm32 hall外部中断初始化-CSDN博客

3.3 ADC转化模式

ADC总共有四种转换模式,分别是:

  • 单次转换,非扫描模式
  • 单次转换,扫描模式
  • 连续转换,非扫描模式
  • 连续转换,扫描模式

我们分开进行讲解:

  • 转换
    • 单次:只转换一次
    • 连续:只要一开启转换,就会不断的进行。
  • 非扫描:一次只转换一个通道
  • 扫描:一次性可同时按顺序转换多个通道

进行排列组合,便可以得到以下模式:

3.4 ADC对齐模式

对齐模式有:

  • 左对齐
  • 右对齐

STM32F103C8T6芯片的ADC为12位的,但是数据寄存器为16位的,选择 左对齐 相当于把结果左位移了4位,将AD采集结果扩大了2^4倍。选择 右对齐 则读出的数据就是正常的原始数据。所以我们选择右对齐居多。

3.5 ADC校准

为什么要进行ADC的校准?

因为:ADC内部存在硬件偏差(Offset Error),导致零输入信号时转换结果并非0。校准可以修正这些偏差,使得零输入时输出接近真实值。温度、供电电压等环境因素会影响ADC性能,通过校准可以在不同条件下确保一致性

3.6  ADC配置流程

四.ADC代码示例

4.1 ADC单通道+单次转换

#include "stm32f10x.h"

void ADC_Config(void);
uint16_t ADC_Read(void);

int main(void)
{
    uint16_t adc_value;

    // 配置ADC
    ADC_Config();

    while (1)
    {
        // 读取ADC值
        adc_value = ADC_Read();
    }
}

void ADC_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    ADC_InitTypeDef ADC_InitStructure;
    
    // 1. 启用时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);

    // 2. 配置GPIO为模拟输入模式
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 3. 配置ADC参数
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;          // 单通道非扫描模式
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;    // 单次转换模式
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 软件触发
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; // 数据右对齐
    ADC_InitStructure.ADC_NbrOfChannel = 1;               // 转换通道数量为1
    ADC_Init(ADC1, &ADC_InitStructure);

    // 4. 配置ADC通道
    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);

    // 5. 启用ADC并进行校准
    ADC_Cmd(ADC1, ENABLE);
    ADC_ResetCalibration(ADC1);       // 重置校准
    while (ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1);       // 开始校准
    while (ADC_GetCalibrationStatus(ADC1));
}

uint16_t ADC_Read(void)
{
    // 启动ADC转换
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);

    // 等待转换完成
    while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));

    // 读取转换结果
    return ADC_GetConversionValue(ADC1);
}

4.2 多通道连续转换(带DMA)

#include "stm32f10x.h"

#define ADC1_DR_Address ((uint32_t)0x4001244C)

uint16_t ADC_ConvertedValue[2];

void ADC_DMA_Config(void);

int main(void)
{
    // 配置ADC和DMA
    ADC_DMA_Config();

    while (1)
    {
        // ADC_ConvertedValue中保存了两个通道的转换结果
        uint16_t value0 = ADC_ConvertedValue[0];
        uint16_t value1 = ADC_ConvertedValue[1];
    }
}

void ADC_DMA_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    ADC_InitTypeDef ADC_InitStructure;
    DMA_InitTypeDef DMA_InitStructure;

    // 1. 启用时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    // 2. 配置GPIO为模拟输入模式
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 3. 配置DMA
    DMA_DeInit(DMA1_Channel1);
    DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_ConvertedValue;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = 2;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);
    DMA_Cmd(DMA1_Channel1, ENABLE);

    // 4. 配置ADC
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
    ADC_InitStructure.ADC_ScanConvMode = ENABLE;            // 扫描模式
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;      // 连续转换模式
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 软件触发
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;  // 数据右对齐
    ADC_InitStructure.ADC_NbrOfChannel = 2;                // 转换通道数量为2
    ADC_Init(ADC1, &ADC_InitStructure);

    // 5. 配置ADC通道
    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5);

    // 6. 启用ADC DMA
    ADC_DMACmd(ADC1, ENABLE);

    // 7. 启用ADC并进行校准
    ADC_Cmd(ADC1, ENABLE);
    ADC_ResetCalibration(ADC1);
    while (ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1);
    while (ADC_GetCalibrationStatus(ADC1));

    // 8. 启动ADC转换
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}

五.附STM32F103C8T6引脚定义表

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐