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输出一次
}
}
运行与验证
- 将工程编译后通过仿真器烧录至F28335,运行程序。
- 用信号发生器在ADCINA0输入1.65V直流(对应0.5A),ADCINB0输入1.0V(对应100V)。
- 打开串口助手(115200,8,N,1),观察输出帧格式应为
0.50,100.00的循环打印。 - 通过标准:串口输出数值与输入信号换算值的误差在±2%以内,且数值稳定(波动小于±0.5%),说明滤波与标定链路正确。
关键说明
- ADC结果寄存器为左对齐16位格式,右移4位后得到12位有效数据,该步骤不可省略。
- 标定系数
CURRENT_GAIN与VOLTAGE_GAIN需根据实际传感器/分压电路修改,本案例使用示例值。 - ePWM1周期设为3750(150MHz时钟下对应50kHz触发率),每20次触发完成一轮16点平均,实际数据更新率约2.5kHz,满足多数伺服电流环采样需求。