3. DSP ADC采样与数据处理

任务目标

本案例基于TMS320F28335 DSP内置ADC模块,完成对电流、电压模拟信号的采集、软件滤波与标定,最终通过串口输出可供上位机直接使用的数字量(物理量)。案例覆盖ADC初始化、采样触发、结果读取、滑动平均滤波及标定换算的完整最小链路。

硬件与依赖

  • 硬件:TMS320F28335控制板(ADCINA0接电流传感器输出0~3.3V,ADCINB0接电压传感器输出0~3.3V)
  • 开发环境:CCS 6.x+,使用官方库文件 DSP2833x_Device.h、DSP2833x_Examples.h
  • 串口:SCIA,波特率115200,8位数据位,1位停止位

核心实现

代码完成以下功能:初始化ADC为双通道顺序采样(SEQ1),采用ePWM1定时触发启动转换;每次触发读取A0、B0两通道结果;对连续16次采样做滑动平均滤波;按线性标定公式将ADC原始码值换算为实际电流(A)和电压(V),并通过串口以文本帧输出。

#include "DSP2833x_Device.h"
#include "DSP2833x_Examples.h"

#define SAMPLE_NUM  16
#define ADC_CLK_DIV 6      // ADC时钟 = 25MHz/(2*6) ≈ 2.08MHz
#define CURRENT_GAIN  0.001f   // V/A,传感器比例
#define VOLTAGE_GAIN  0.01f    // V/V,分压比例
#define VREF 3.3f

Uint16 adcBufA[SAMPLE_NUM], adcBufB[SAMPLE_NUM];
Uint16 bufIdx = 0;
float filtCurr = 0.0f, filtVolt = 0.0f;

void InitADC(void) {
    EALLOW;
    AdcRegs.ADCTRL1.bit.ACQ_PS = 0xF;      // 采样窗15个周期
    AdcRegs.ADCTRL1.bit.SEQ_CASC = 0;      // 双排序器独立
    AdcRegs.ADCTRL1.bit.CONT_RUN = 0;      // 启动-停止模式
    AdcRegs.ADCTRL2.bit.EPWM_SOCA_SEQ1 = 1;// ePWM1触发SEQ1
    AdcRegs.ADCTRL2.bit.INT_ENA_SEQ1 = 1;  // 允许SEQ1中断
    AdcRegs.ADCTRL3.bit.ADCCLKPS = ADC_CLK_DIV;
    AdcRegs.ADCTRL3.bit.SMODE_SEL = 1;     // 同步采样
    AdcRegs.ADCMAXCONV.bit.MAX_CONV1 = 1;  // 2个转换
    AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0;   // ADCINA0
    AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 1;   // ADCINB0
    EDIS;
}

void InitEPWM1_ADC(void) {
    EALLOW;
    EPwm1Regs.TBPRD = 3750;                // 50kHz @150MHz
    EPwm1Regs.TBCTL.bit.CTRMODE = 0;       // 递增计数
    EPwm1Regs.TBCTL.bit.HSPCLKDIV = 0;
    EPwm1Regs.TBCTL.bit.CLKDIV = 0;
    EPwm1Regs.ETSEL.bit.SOCAEN = 1;
    EPwm1Regs.ETSEL.bit.SOCASEL = 2;       // 周期匹配触发
    EPwm1Regs.ETPS.bit.SOCAPRD = 1;
    EDIS;
}

__interrupt void adc_isr(void) {
    adcBufA[bufIdx] = AdcRegs.ADCRESULT0 >> 4;  // 右移4位得12位结果
    adcBufB[bufIdx] = AdcRegs.ADCRESULT1 >> 4;
    bufIdx++;
    if (bufIdx >= SAMPLE_NUM) {
        Uint32 sumA = 0, sumB = 0;
        Uint16 i;
        for (i = 0; i < SAMPLE_NUM; i++) {
            sumA += adcBufA[i];
            sumB += adcBufB[i];
        }
        float avgA = (float)sumA / SAMPLE_NUM;
        float avgB = (float)sumB / SAMPLE_NUM;
        float voltA = avgA * VREF / 4095.0f;
        float voltB = avgB * VREF / 4095.0f;
        filtCurr = voltA / CURRENT_GAIN;   // 电流标定
        filtVolt = voltB / VOLTAGE_GAIN;   // 电压标定
        bufIdx = 0;
    }
    AdcRegs.ADCTRL2.bit.INT_SEQ1_CLR = 1;
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;
}

void InitUART(void) {
    EALLOW;
    GpioCtrlRegs.GPAMUX1.bit.GPIO28 = 1;   // SCIRXDA
    GpioCtrlRegs.GPAMUX1.bit.GPIO29 = 1;   // SCITXDA
    EDIS;
    SciaRegs.SCIFFTX.bit.TXFIFORESET = 1;
    SciaRegs.SCIFFTX.bit.SCIFFENA = 1;
    SciaRegs.SCICCR.bit.SCICHAR = 7;       // 8位数据
    SciaRegs.SCICTL1.bit.SCISWRESET = 1;
    SciaRegs.SCIHBAUD = 0;
    SciaRegs.SCILBAUD = 81;                // 115200 @150MHz
}

void SendFloat(float val) {
    char buf[16];
    sprintf(buf, "%.2f", val);
    Uint16 i = 0;
    while (buf[i] != '\0') {
        while (SciaRegs.SCIFFTX.bit.TXFFST == 16);
        SciaRegs.SCITXBUF = buf[i++];
    }
}

void main(void) {
    InitSysCtrl();
    DINT;
    InitPieCtrl();
    IER = 0x0000;
    IFR = 0x0000;
    InitPieVectTable();
    EALLOW;
    PieVectTable.SEQ1INT = &adc_isr;
    EDIS;
    InitADC();
    InitEPWM1_ADC();
    InitUART();
    IER |= M_INT1;
    EINT;
    while (1) {
        SendFloat(filtCurr);
        SciaRegs.SCITXBUF = ',';
        SendFloat(filtVolt);
        SciaRegs.SCITXBUF = '\r';
        SciaRegs.SCITXBUF = '\n';
        DELAY_US(100000);   // 100ms输出一次
    }
}

运行与验证

  1. 将工程编译后通过仿真器烧录至F28335,运行程序。
  2. 用信号发生器在ADCINA0输入1.65V直流(对应0.5A),ADCINB0输入1.0V(对应100V)。
  3. 打开串口助手(115200,8,N,1),观察输出帧格式应为 0.50,100.00 的循环打印。
  4. 通过标准:串口输出数值与输入信号换算值的误差在±2%以内,且数值稳定(波动小于±0.5%),说明滤波与标定链路正确。

关键说明

  • ADC结果寄存器为左对齐16位格式,右移4位后得到12位有效数据,该步骤不可省略。
  • 标定系数 CURRENT_GAIN 与 VOLTAGE_GAIN 需根据实际传感器/分压电路修改,本案例使用示例值。
  • ePWM1周期设为3750(150MHz时钟下对应50kHz触发率),每20次触发完成一轮16点平均,实际数据更新率约2.5kHz,满足多数伺服电流环采样需求。