stm32-模数转换

发布于 26 天前  105 次阅读


模拟数字转换器(ADC)

1. ADC 概述

  • ADC(Analog-to-Digital Converter) 是一种将模拟信号转换为数字信号的装置。它可以将连续变化的模拟电压转化为微控制器可处理的数字量,方便对模拟信号进行采集和分析。
  • STM32F103 系列微控制器内置了多通道、高精度的 ADC,支持多种触发方式和工作模式,适用于各种模拟信号的采集需求。

ADC 的主要特点:

  • 高分辨率:12 位分辨率,可将输入电压细分为 4096 个等级。
  • 多通道:提供多达 16 个通道,可同时采集多个模拟信号。
  • 多种转换模式:支持单次、连续、扫描、间断等模式,灵活满足不同应用需求。
  • 触发方式多样:支持软件触发和外部触发,包括定时器、中断等。
  • 支持 DMA:可与 DMA 配合,实现高速数据传输,减轻 CPU 负担。

2. ADC 配置步骤

在 STM32F103 中使用 ADC 时,一般按照以下步骤进行配置:

1. 使能 ADC 和 GPIO 时钟

首先,需要开启 ADC 和对应 GPIO 端口的时钟。

// 使能 ADC1 和 GPIOA 时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);

2. 配置 GPIO 引脚为模拟输入模式

将用于 ADC 输入的 GPIO 引脚配置为模拟输入模式。

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;      // 选择 PA1 引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;  // 模拟输入模式
GPIO_Init(GPIOA, &GPIO_InitStructure);

3. 初始化 ADC

设置 ADC 的工作模式、数据对齐方式、转换模式等参数。

ADC_InitTypeDef ADC_InitStructure;
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;                           // 转换通道数为 1
ADC_Init(ADC1, &ADC_InitStructure);

4. 配置 ADC 通道和采样时间

指定要转换的通道,并设置采样时间。

// 配置 ADC1 的通道 1,采样时间为 55.5 周期
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_55Cycles5);

5. 启用 ADC

在完成上述配置后,启用 ADC。

ADC_Cmd(ADC1, ENABLE);

6. 校准 ADC

在使用 ADC 前,需要进行校准以提高转换精度。

// 复位校准
ADC_ResetCalibration(ADC1);
// 等待复位完成
while (ADC_GetResetCalibrationStatus(ADC1));
// 开始校准
ADC_StartCalibration(ADC1);
// 等待校准完成
while (ADC_GetCalibrationStatus(ADC1));

7. 启动 ADC 转换

// 启动 ADC 软件转换
ADC_SoftwareStartConvCmd(ADC1, ENABLE);

3. 示例:读取电位器的模拟电压

以下示例演示如何使用 ADC1 读取连接在 PA1 引脚的电位器电压值。

ADC 初始化函数

void ADC1_Init(void) {
    // 1. 使能时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);

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

    // 3. 初始化 ADC
    ADC_InitTypeDef ADC_InitStructure;
    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;                     // 通道数为 1
    ADC_Init(ADC1, &ADC_InitStructure);

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

    // 5. 启用 ADC
    ADC_Cmd(ADC1, ENABLE);

    // 6. 复位校准
    ADC_ResetCalibration(ADC1);
    while (ADC_GetResetCalibrationStatus(ADC1));

    // 7. 开始校准
    ADC_StartCalibration(ADC1);
    while (ADC_GetCalibrationStatus(ADC1));

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

获取 ADC 转换值

uint16_t ADC1_GetValue(void) {
    // 等待转换完成
    while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
    // 返回转换结果
    return ADC_GetConversionValue(ADC1);
}

主函数示例

int main(void) {
    uint16_t adc_value;
    float voltage;

    ADC1_Init(); // 初始化 ADC1

    while (1) {
        adc_value = ADC1_GetValue(); // 获取 ADC 转换值
        voltage = adc_value * (3.3 / 4096); // 计算电压值(假设参考电压 3.3V)
        // 可将电压值通过串口发送或显示在屏幕上
    }
}

4. 注意事项

  • 参考电压稳定性:ADC 的精度受参考电压影响,确保参考电压稳定,避免噪声干扰。
  • 采样时间选择:根据输入信号源阻抗选择适当的采样时间,阻抗越高,采样时间应越长。
  • 通道配置:在多通道转换时,需要正确配置每个通道的顺序和采样时间。
  • 校准重要性:定期校准 ADC,可提高转换精度,特别是在温度变化较大的环境中。

5. 扩展:使用 DMA 提高数据采集效率

当需要高速采集大量数据时,可使用 DMA(直接存储器访问)将 ADC 转换结果直接存储到内存,降低 CPU 负担。

DMA 配置示例

void ADC1_DMA_Config(void) {
    uint16_t ADC_Buffer[10]; // 定义缓冲区

    // 1. 使能 DMA、ADC 和 GPIO 时钟
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);

    // 2. 配置 GPIO 和 ADC(同之前步骤)

    // 3. 配置 DMA
    DMA_InitTypeDef DMA_InitStructure;
    DMA_DeInit(DMA1_Channel1); // ADC1 使用 DMA1 通道1
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; // ADC 数据寄存器地址
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_Buffer;   // 缓冲区地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;              // 外设作为数据源
    DMA_InitStructure.DMA_BufferSize = 10;                          // 缓冲区大小
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址不增
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;          // 内存地址自增
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 16 位
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // 16 位
    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);

    // 4. 启用 DMA
    DMA_Cmd(DMA1_Channel1, ENABLE);

    // 5. 配置 ADC
    ADC_InitTypeDef ADC_InitStructure;
    // ...(与之前相同的 ADC 初始化步骤)
    ADC_DMACmd(ADC1, ENABLE); // 启用 ADC 的 DMA 功能

    // 6. 复位和校准 ADC(同之前步骤)

    // 7. 启动 ADC 转换(同之前步骤)
}
最后更新于 2024-11-17