MSPM0L1306 从零到入门: 第九章 ADC-电压采集

第九章 ADC-电压采集

本章带你用 MSPM0L1306 的 ADC12 采集单端电压,并把原始计数值换算成电压。重点讲清"时钟/采样时间/参考电压/通道映射/中断"的关系,给出稳健的、可扩展的代码骨架。

1. MSPM0L 系列的 ADC 概览

  • 架构与分辨率:12 位 SAR(逐次逼近)ADC,结果范围 0...4095。
  • 通道资源:最多 10 路外部模拟输入(具体映射见数据手册/引脚手册),每路对应若干可复用管脚;需将目标引脚配置为"模拟功能"。
  • 参考电压 Vref:
    • VDDA(默认,通常近似板载 3.3V,但可能有偏差)
    • 外部参考引脚(如你板上 PA23 可作参考电压,与R24跳线有关)
    • 内部参考(若芯片支持,常用于自校准)
  • 转换模式:单次/重复单次/多通道顺序单次/多通道顺序重复(本文先做"单通道重复")。
  • 触发源:软件触发/定时器触发/序列自动触发等。
  • 采样时间:由"采样时钟 × 采样周期数"决定,必须与信号源阻抗匹配(高阻信号要更长的采样时间,保证采样电容充分充电)。

小提醒:

  • 如果用 VDDA 为参考而你又要算"绝对电压",需知道真实 VDDA(3.30V 只是经验值)。更严谨做法是:用内部参考/外部参考,或用内部通道反推 VDDA。

2. 软件设计

2.1 编程大纲

  1. ADC 相关宏与引脚/通道定义
  2. ADC 时钟与基础配置(重复单次 + 中断)
  3. ADC 中断服务函数(取样就绪标志 + 保存结果)
  4. API:阻塞式读取 + mV 换算(支持整数运算)
  5. 主函数测试(打印计数与电压)

2.2 代码分析

2.2.1 ADC 相关参数与引脚定义
c 复制代码
static volatile bool     g_adcReady = false;
static volatile uint16_t g_adcLast  = 0;

关于参考电压:

  • 本例使用 DL_ADC12_REFERENCE_VOLTAGE_VDDA(与供电相同)。若要提高精度且板上支持外部参考,请改为外部参考并正确配置引脚/电平。
  • 若用 VDDA 作为 Vref,换算电压时请用"实测的 VDDA"而不是固定 3.3V(先用 3300mV 开发,后期可标定)。
2.2.2 ADC 基础配置

思路:

  • ADC 时钟用 SYSOSC,经 8 分频得到 4 MHz(与前文 32MHz 主频一致)。
  • 采样时间设定适中(例如 64 周期 → 16 μs @4MHz),可覆盖大多数 <10kΩ 的信号源阻抗。若源阻更高,请增大采样周期数。
  • 采用"重复单次 + 软件触发一次启动",让 ADC 连续采样;每次结果到 MEM0 触发中断。
c 复制代码
#include <ti_msp_dl_config.h>
#include <stdbool.h>
#include <stdint.h>

/* 采样完成标志与上次结果(ISR更新) */
static volatile bool     g_adcReady = false;
static volatile uint16_t g_adcLast  = 0;

/* ADC 时钟:SYSOSC/8,目标 24~32MHz 频段配置 */
static const DL_ADC12_ClockConfig gADC_ClockCfg = {
    .clockSel    = DL_ADC12_CLOCK_SYSOSC,
    .divideRatio = DL_ADC12_CLOCK_DIVIDE_8,
    .freqRange   = DL_ADC12_CLOCK_FREQ_RANGE_24_TO_32
};

void ADC_Init_CH0_Repeat(void)
{
    /* 若非用 SysConfig 配置引脚为模拟功能,需要手动设置为模拟输入:
       DL_GPIO_initAnalogFunction(GPIO_ADC_CH0_IOMUX);  // IOMUX 常量以芯片包为准
       建议:用 SysConfig 勾选 ADC 通道并生成引脚配置,避免手写出错。 */

    /* 1) 时钟配置 */
    DL_ADC12_setClockConfig(ADC_INST, (DL_ADC12_ClockConfig *)&gADC_ClockCfg);

    /* 2) 单通道"重复单次"+ 自动采样源 + 软件触发启动 */
    DL_ADC12_initSingleSample(
        ADC_INST,
        DL_ADC12_REPEAT_MODE_ENABLED,                 /* 重复单次 */
        DL_ADC12_SAMPLING_SOURCE_AUTO,                /* 自动采样 */
        DL_ADC12_TRIG_SRC_SOFTWARE,                   /* 软件触发 */
        DL_ADC12_SAMP_CONV_RES_12_BIT,                /* 12bit */
        DL_ADC12_SAMP_CONV_DATA_FORMAT_UNSIGNED       /* 无符号 */
    );

    /* 3) 配置 MEM0 采样 CH0,参考电压选 VDDA,使用 Sample Timer0,单次结果,不加平均 */
    DL_ADC12_configConversionMem(
        ADC_INST,
        ADC_MEM_IDX,
        ADC_INPUT_CH,
        DL_ADC12_REFERENCE_VOLTAGE_VDDA,
        DL_ADC12_SAMPLE_TIMER_SOURCE_SCOMP0,
        DL_ADC12_AVERAGING_MODE_DISABLED,
        DL_ADC12_BURN_OUT_SOURCE_DISABLED,
        DL_ADC12_TRIGGER_MODE_AUTO_NEXT,              /* 自动进入下一次 */
        DL_ADC12_WINDOWS_COMP_MODE_DISABLED
    );

    /* 4) 采样时间:以采样时钟 4MHz 计,64 周期 ≈ 16us。
          高阻源(>10kΩ)或 RC 滤波较大时,可适当增大,如 128/256 周期。 */
    DL_ADC12_setSampleTime0(ADC_INST, 64U);

    /* 5) 中断:清标志→使能 MEM0 结果就绪中断 */
    DL_ADC12_clearInterruptStatus(ADC_INST, DL_ADC12_INTERRUPT_MEM0_RESULT_LOADED);
    DL_ADC12_enableInterrupt(ADC_INST,       DL_ADC12_INTERRUPT_MEM0_RESULT_LOADED);

    /* 6) 使能转换并启动"重复单次" */
    DL_ADC12_enableConversions(ADC_INST);
    DL_ADC12_startConversion(ADC_INST);
}

要点:

  • 若仅需"按需采样一次",可将 REPEAT_MODE_ENABLED 改为 DISABLED,并在读取函数中"开始→等待→读取→停止"。
  • 建议用 SysConfig 配置通道与引脚;手写 IOMUX 容易出错。
2.2.3 ADC 中断服务函数
c 复制代码
void ADC_INST_IRQHandler(void)
{
    switch (DL_ADC12_getPendingInterrupt(ADC_INST)) {
        case DL_ADC12_IIDX_MEM0_RESULT_LOADED:
            /* 读取会清掉本次 MEM0 标志,为下一次转换做准备 */
            g_adcLast  = (uint16_t)DL_ADC12_getMemResult(ADC_INST, ADC_MEM_IDX);
            g_adcReady = true;
            break;
        default:
            break;
    }
}

说明:

  • ISR 要尽量短小;若要滤波/均值,建议只做累计并把计算放到主循环或低优先级任务中。
  • 读取 MEM0 后,下一次转换会继续进行(重复单次)。
2.2.4 采集 API 与电压换算

提供"阻塞读取一帧 + mV 换算"的简洁接口。开发期可先假设 VREF_MV=3300,后期可改为"测得 VDDA"或"使用外部/内部参考"。

c 复制代码
/* 假设参考电压为 3.300V(开发阶段可用;量产请改为实测或稳定参考) */
#define VREF_MV  (3300U)

/* 阻塞式获取一次最新样点(由重复模式持续产生,ISR 更新 g_adcLast) */
uint16_t ADC_ReadBlocking(void)
{
    g_adcReady = false;
    /* 如果刚进来时已经有新样点,可直接返回;否则等待一次中断 */
    while (!g_adcReady) {
        __WFI(); /* 低功耗等待中断 */
    }
    return (uint16_t)g_adcLast;
}

/* 计数→毫伏(整数运算,避免浮点) */
static inline uint32_t adcCounts_to_mV(uint16_t counts, uint32_t vref_mV)
{
    /* 12bit 最大值 4095 */
    return ((uint32_t)counts * vref_mV) / 4095U;
}

可选:做一个简易滑动平均(抗抖/降噪),如下为 8 次均值示例(在主循环里调用,非 ISR)。

c 复制代码
/* 8 点滑动平均(示例) */
uint16_t ADC_ReadAvg8(void)
{
    uint32_t sum = 0;
    for (int i = 0; i < 8; ++i) {
        sum += ADC_ReadBlocking();
    }
    return (uint16_t)(sum / 8U);
}
2.2.5 主函数测试
c 复制代码
#include "ti_msp_dl_config.h"
#include <stdio.h>

/* 若已在其他章节完成 UART printf 重定向,这里直接用 printf 输出 */
int main(void)
{
    SYSCFG_DL_init();            /* 时钟=32MHz、GPIO等基础配置(建议用 SysConfig 生成) */
    __enable_irq();

    /* 串口、SysTick 如需可初始化(用于打印与延时) */
    // SYSCFG_DL_UART_0_init();
    // SysTick_init();

    /* ADC 初始化 + 使能中断 */
    ADC_Init_CH0_Repeat();
    NVIC_ClearPendingIRQ(ADC_INST_INT_IRQN);
    NVIC_EnableIRQ(ADC_INST_INT_IRQN);

    printf("\r\n--- MSPM0 ADC Single-Channel Repeat Demo ---\r\n");

    while (1)
    {
        /* 读取一次(或均值) */
        uint16_t adc_raw = ADC_ReadBlocking();
        uint32_t mv      = adcCounts_to_mV(adc_raw, VREF_MV);

        printf("ADC raw: %u, Voltage: %lu.%03lu V\r\n",
               adc_raw, mv / 1000UL, mv % 1000UL);

        /* 如果有 SysTick,可做一个简易 1s 周期 */
        // delay_ms(1000);
        for (volatile uint32_t d = 0; d < 3200000; ++d) { __NOP(); } /* 约 ~100ms 粗延时(32MHz) */
    }
}

运行现象:

  • 串口周期打印"12位计数 + 近似电压值"。
  • 用可调电源或电位器给 PA27(ADC CH0)输入 0...Vref 的电压,观察数值随之变化。

3. 关键细节与常见问题

  • 引脚必须为"模拟模式":数字输入/输出/上拉下拉会影响精度,务必由 SysConfig 或 DL_GPIO_initAnalogFunction(...) 设置为模拟。
  • 采样时间与源阻抗:若信号源阻抗较高(>10kΩ)或串入 RC 滤波,需增大采样周期数(如 128/256/512),保证采样电容充分充电。
  • 参考电压的选择:
    • 用 VDDA 作为参考时,若板上 3.3V 有波动,你换算的"绝对电压"会跟着飘。可改用外部精密参考,或使用内部参考并做一次"测量内部参考→反推 VDDA"的校准。
    • 若切换参考或通道,请确保采样时间/稳定时间足够(有些参考需要上电稳定时间)。
  • 中断处理要短小:复杂滤波应放在主循环或低优先级任务,或用 DMA 搬运到缓冲区后再处理。
  • 触发与速率:若要固定采样频率,建议用定时器触发 ADC,而不是"重复单次+全速跑";配合 DMA 做等间隔采样更稳健。
  • 输入保护:超过 0...Vref 的电压会导致结果钳位甚至损伤芯片;外接电压请加分压、钳位与限流。

4. 小结与进阶

  • 你已掌握:单通道重复采样 + 中断取数 + 整数电压换算。
  • 进阶方向:
    1. 多通道顺序采样(序列 + DMA 环形缓冲),批量采集多路传感器
    2. 定时器触发的等间隔采样(精准采样率)
    3. 标定与温漂:做零点/满量程校准,或读取工厂校准参数修正
    4. 使用外部/内部精密参考,提升绝对精度

相关推荐
ACP广源盛139246256731 小时前
GSV2221G@ACP#产品参数规格解析与应用方式分享
单片机·嵌入式硬件·音视频
TDengine (老段)2 小时前
网络延时对 TDengine TSDB 写入性能的影响:实验解析与实践建议
大数据·数据库·物联网·时序数据库·tdengine·涛思数据
猫猫的小茶馆2 小时前
【ARM】BootLoader(Uboot)介绍
linux·汇编·arm开发·单片机·嵌入式硬件·mcu·架构
雾削木2 小时前
STM32CubeHAL 外设仿真大合集 | Proteus 8.15 (LCD1602+OLED+DHT11+DS18B20+舵机+蜂鸣器)
单片机·嵌入式硬件
西城微科方案开发3 小时前
基于西城微科SIC8833芯片的口袋电子秤方案解析
单片机·嵌入式硬件·方案公司推荐
WHFENGHE3 小时前
输电线路倾斜仪在线监测装置:结构安全的关键技术支撑
物联网
专业开发者4 小时前
GNSS 省电模式
物联网
三佛科技-134163842124 小时前
SM7015 输出12V/18V 电流150MA非隔离LED电源驱动IC典型应用电路
单片机·嵌入式硬件·智能家居·pcb工艺
Jazel4 小时前
DDR5 Write leveling详解
嵌入式硬件