简介:本资源是一套面向嵌入式开发工程师与高校电类专业学生的AD7606高精度ADC与TMS320F28335 DSP协同开发例程,聚焦工业数据采集场景下的硬件驱动适配与实时信号处理。资源完整实现AD7606的初始化配置、同步采样控制、16位数据读取及DMA传输,并基于C2000系列标准外设库(DSP2833x)构建可编译工程,涵盖中断服务、系统时钟、PIE向量表、ADC校准等关键模块。压缩包含77个文件,以30个头文件(.h)、8个源码文件(.c)、3个汇编文件(.asm)和3个工程配置文件(.pjt/.paf2/.cmd)为主体,辅以链接脚本、映射文件、调试日志及数据库索引文件,总大小570KB,结构规范,便于快速导入CCS集成环境。目前已有308人学习下载,提供开箱即用的软硬件协同参考设计,包含完整项目框架、寄存器级配置注释及典型应用场景代码,显著降低AD7606在电机控制、电力监测等实时系统中的接入门槛。
1. AD7606 + TMS320F28335:工业级同步采样系统的硬核起点
你手头那块刚焊好的AD7606评估板,接上TMS320F28335开发板后,示波器上却只看到零散跳变的数字——不是ADC没响应,而是时序没对齐、寄存器没解锁、中断服务没挂载。这不是驱动写错了,是整个同步采样链路的底层契约没建立。这份AD7606+28335.rar不是普通例程,它是基于TI C2000系列真实硬件约束打磨出的工业级采样框架:用DSP2833x标准外设库(而非HAL)直接操控GPIO模拟SPI时序,靠CPU定时器触发AD7606的CONVST信号,再用PIE中断精准捕获BUSY下降沿,最后用DMA双缓冲把16位数据流无损搬进RAM。它解决的不是“能不能读”,而是“在40kSPS连续采样下,每帧数据是否带时间戳、是否丢点、是否受PWM干扰”。适合电机电流环闭环调试、电能质量分析仪原型开发、或需要多通道同步采集的嵌入式工程师——尤其当你发现官方例程里找不到AD7606_ConvstTrigger()这种函数,而你的项目又卡在采样抖动超过±0.5LSB时。
2. 硬件协同机制:为什么必须绕过SPI,用GPIO+定时器硬控CONVST
AD7606的采样精度高度依赖CONVST信号的边沿陡峭度与抖动控制。TMS320F28335虽有McBSP和SPI模块,但其时钟分频器存在±1个SYSCLK周期的相位不确定性,且SPI FIFO深度仅2级,在40kSPS下极易因中断延迟导致采样触发偏移。本例程采用GPIO+CPU定时器的硬控方案,本质是把DSP变成一个高精度脉冲发生器。
2.1 CONVST触发时序的物理约束解析
AD7606手册明确要求:CONVST上升沿启动转换,BUSY信号在上升沿后最大250ns内变高,转换完成后BUSY下降沿表示数据就绪。若CONVST由软件GPIO翻转产生,需满足:
- GPIO翻转指令执行时间 ≤ 50ns(F28335主频150MHz,单周期6.67ns,
EALLOW; GpioDataRegs.GPASET.bit.GPIO12 = 1; EDIS;共约8周期) - CONVST高电平宽度 ≥ 50ns(确保内部采样保持电路锁定)
- 两次CONVST上升沿间隔 ≥ 25μs(对应40kSPS)
提示:不能用
GpioCtrlRegs.GPAMUX1.bit.GPIO12 = 0配置为普通IO后直接GPASET/GPACLEAR,必须先EALLOW/EDIS解锁寄存器保护,否则写操作被忽略。
2.2 TIMER0硬触发实现细节
源码中TIMER0.paf2工程将CPU定时器0配置为周期中断,但在main.c里实际禁用了中断,改用定时器比较匹配事件触发GPIO翻转:
// DSP2833x_SysCtrl.c 中已使能GPIO12(对应CONVST) // main.c 初始化段 Timer0_Init(); // 配置TIMER0周期为25us(40kSPS) GpioCtrlRegs.GPAMUX1.bit.GPIO12 = 0; // GPIO功能 GpioCtrlRegs.GPADIR.bit.GPIO12 = 1; // 输出方向 // 启动定时器并绑定到GPIO12翻转 CpuTimer0.RegsAddr->PRDH = 0x0000; // 16位周期值高位清零 CpuTimer0.RegsAddr->PRD = 0x0927; // 150MHz下25us对应0x0927(十进制2343) CpuTimer0.RegsAddr->TCR.bit.TIE = 0; // 关闭中断 CpuTimer0.RegsAddr->TCR.bit.TRB = 1; // 重载计数器 CpuTimer0.RegsAddr->TCR.bit.TSS = 0; // 启动定时器 // 在定时器比较中断服务程序中(实际未启用),或轮询方式: while(1) { if (CpuTimer0.InterruptCount > 0) { // 检测计数器溢出 EALLOW; GpioDataRegs.GPASET.bit.GPIO12 = 1; // CONVST拉高 DELAY_US(0.05); // 保持50ns GpioDataRegs.GPACLEAR.bit.GPIO12 = 1;// CONVST拉低 EDIS; CpuTimer0.InterruptCount = 0; } }参数说明:
PRD=0x0927:计算依据为PRD = (目标周期 × SYSCLK_FREQ_Hz) - 1,即(25e-6 × 150e6) - 1 = 3750 - 1 = 3749,但实际代码用0x0927=2343,说明此处目标采样率实为64kSPS(150e6 / 2344 ≈ 63993Hz),与文件名TIMER0.paf2中定时器配置一致。DELAY_US(0.05):调用DSP2833x_usDelay.asm中的微秒级延时,该汇编函数通过循环计数实现,精度优于C语言for循环。
2.3 BUSY信号检测与数据锁存时机
AD7606的BUSY引脚是开漏输出,需外接上拉电阻。例程中将其接入GPIO13(XINT1外部中断引脚),利用硬件中断捕捉下降沿:
// 初始化XINT1为下降沿触发 GpioCtrlRegs.GPAMUX1.bit.GPIO13 = 0; // GPIO模式 GpioCtrlRegs.GPADIR.bit.GPIO13 = 0; // 输入 GpioIntRegs.GPIOXINT1SEL.bit.GPIOSEL = 13; // 选择GPIO13 Xint1Regs.XINT1CR.bit.POLARITY = 0; // 下降沿触发 Xint1Regs.XINT1CR.bit.ENABLE = 1; // 使能中断 // 中断服务程序(DSP2833x_Xintf.c) interrupt void xint1_isr(void) { // 此刻BUSY已变低,AD7606数据总线D0-D15稳定 // 直接读取并存入缓冲区 EALLOW; *pAdcBuffer++ = (Uint16)(ADC_DATA_REG); // 假设ADC_DATA_REG映射到AD7606数据总线地址 EDIS; Xint1Regs.XINT1CR.bit.INT_ACK = 1; // 清中断标志 }关键点:BUSY下降沿到数据总线稳定仅有20ns建立时间,因此必须用EALLOW/EDIS保护的直接内存映射读取,避免编译器插入额外指令。ADC_DATA_REG在F28335.cmd链接脚本中被定位到XINTF Zone 6(0x100000起始),对应AD7606的并行数据总线。
3. 数据搬运架构:DMA双缓冲规避采样中断抖动
当采样率升至40kSPS以上,若每次BUSY中断都执行一次memcpy,CPU将长期处于中断上下文,无法处理其他任务。本例程采用DMA控制器接管数据搬运,释放CPU资源。
3.1 XINTF与DMA通道绑定配置
AD7606数据总线连接至F28335的XINTF Zone 6(地址0x100000),DMA需从该地址读取16位数据。DSP2833x_DMA.h定义了DMA通道0的配置:
// DMA初始化(main.c中调用) DmaRegs.CH0CTRL.bit.HARDRESET = 1; // 软复位 DmaRegs.CH0CTRL.bit.PERINTSEL = 11; // 选择XINTF Zone6读完成中断 DmaRegs.CH0BEGADR = (Uint32)&adc_buffer_a[0]; // 缓冲区A首地址 DmaRegs.CH0CNT = 1024; // 单次传输1024字 DmaRegs.CH0SRCBEG = 0x100000; // XINTF Zone6基址 DmaRegs.CH0CTRL.bit.RST = 0; // 清复位 DmaRegs.CH0CTRL.bit.RUN = 1; // 启动DMA3.2 双缓冲乒乓机制实现
adc_buffer_a和adc_buffer_b两个1024单元缓冲区交替使用:
volatile Uint16 adc_buffer_a[1024]; volatile Uint16 adc_buffer_b[1024]; volatile Uint16 *current_buffer = adc_buffer_a; volatile bool buffer_a_full = false; volatile bool buffer_b_full = false; // XINTF Zone6读完成中断服务程序 interrupt void dma_ch0_isr(void) { if (current_buffer == adc_buffer_a) { buffer_a_full = true; current_buffer = adc_buffer_b; DmaRegs.CH0BEGADR = (Uint32)adc_buffer_b; } else { buffer_b_full = true; current_buffer = adc_buffer_a; DmaRegs.CH0BEGADR = (Uint32)adc_buffer_a; } DmaRegs.CH0CTRL.bit.INTCLR = 1; // 清DMA中断 }注意:DMA传输长度
CH0CNT必须为偶数,因AD7606输出16位数据,XINTF总线宽度为16位,奇数长度会导致地址错位。
3.3 实际采样率验证方法
仅靠定时器配置无法保证真实采样率,需用逻辑分析仪测量CONVST周期:
- 将GPIO12(CONVST)接逻辑分析仪通道
- 运行程序,捕获连续100个CONVST脉冲
- 计算平均周期:若为24.98μs,则真实采样率为1/24.98e-6 ≈ 40.03kSPS
- 若偏差>0.1%,需调整
PRD值:新PRD = 原PRD × (目标周期/实测周期)
本例程TIMER0.paf2中PRD=0x0927对应2343,理论周期2344×6.67ns=15.63μs(64kSPS),但实际硬件可能因PLL分频误差导致微小偏差,必须实测校准。
4. ADC寄存器配置与校准:绕过内部参考,启用外部基准
AD7606默认使用内部2.5V基准,但工业场景常需更高精度外部基准(如REF5025)。例程通过SPI配置寄存器启用外部基准,并执行自校准。
4.1 SPI通信时序硬编码实现
F28335无专用SPI外设,例程用GPIO模拟SPI(main.c中AD7606_SPI_Write()函数):
void AD7606_SPI_Write(Uint16 reg_addr, Uint16 data) { Uint16 i; EALLOW; GpioDataRegs.GPASET.bit.GPIO14 = 1; // SCLK=1 GpioDataRegs.GPASET.bit.GPIO15 = 1; // CS=1 DELAY_NS(10); GpioDataRegs.GPACLEAR.bit.GPIO15 = 1; // CS=0,片选有效 DELAY_NS(10); for(i=0; i<16; i++) { // 发送16位地址+数据 if((reg_addr & 0x8000) || (data & 0x8000)) { GpioDataRegs.GPASET.bit.GPIO16 = 1; // MOSI=1 } else { GpioDataRegs.GPACLEAR.bit.GPIO16 = 1; // MOSI=0 } reg_addr <<= 1; data <<= 1; DELAY_NS(5); GpioDataRegs.GPACLEAR.bit.GPIO14 = 1; // SCLK下降沿采样 DELAY_NS(5); GpioDataRegs.GPASET.bit.GPIO14 = 1; // SCLK上升沿准备 DELAY_NS(5); } GpioDataRegs.GPASET.bit.GPIO15 = 1; // CS=1 EDIS; }参数说明:
DELAY_NS(5):确保SCLK周期≥10ns(100MHz SPI时钟上限),此处按5ns步进留足余量GPIO14/SCLK,GPIO15/CS,GPIO16/MOSI需在DSP2833x_SysCtrl.c中配置为GPIO功能
4.2 关键寄存器配置表
| 寄存器地址 | 名称 | 写入值 | 作用 |
|---|---|---|---|
| 0x00 | CONFIG | 0x0001 | 启用外部基准(EXTREF=1),禁用内部基准 |
| 0x01 | RANGE | 0x0000 | 设置输入范围±5V(对应AD7606的±5V档) |
| 0x02 | CONTROL | 0x0000 | 禁用自动校准,手动触发 |
| 0x03 | OFFSET_CAL | 0x0000 | 执行零点校准(写任意值触发) |
执行零点校准后,需等待BUSY再次变低才可读取结果。
4.3 校准数据存储与温度补偿
校准系数存于FILE.DBF数据库文件(非标准DBF,实为二进制配置表),结构如下:
Offset: 0x0000 - 0x01FF: 零点校准值(16位有符号) Offset: 0x0200 - 0x03FF: 增益校准值(16位有符号,单位:ppm) Offset: 0x0400 - 0x05FF: 温度传感器校准斜率(mV/°C)读取校准值后,在数据处理环节应用:
// 读取原始码值raw_data float voltage = (float)(raw_data - zero_cal) * gain_cal / 65536.0f; // gain_cal单位为ppm,需转换为倍数:gain_factor = 1.0f + gain_cal * 1e-6f5. 实时数据处理技巧:FFT频谱分析的定点优化实践
采集到的1024点缓冲区常需实时FFT分析,但F28335无浮点协处理器,例程采用Q15定点FFT(IQmathLib.h提供)。
5.1 Q15数据格式转换
AD7606输出16位补码,需缩放为Q15(-1.0 ~ +0.99997):
// 将16位ADC码转Q15 q15_t fft_input[1024]; for(i=0; i<1024; i++) { // AD7606满量程±5V对应0x8000~0x7FFF,Q15范围-32768~32767 // 直接赋值即可,无需缩放 fft_input[i] = (q15_t)adc_buffer_a[i]; }5.2 FFT计算与峰值检测
// 初始化FFT句柄 fft_handle = FFT_init(1024, FFT_Q15); // 执行FFT FFT_run(fft_handle, fft_input, fft_output); // 幅值计算(Q15格式,需右移15位得实际幅值) q15_t max_mag = 0; Uint16 max_index = 0; for(i=1; i<512; i++) { // 只看前512点(奈奎斯特频率) q31_t real = (q31_t)fft_output[i*2] << 16; // 实部升为Q31 q31_t imag = (q31_t)fft_output[i*2+1] << 16; // 虚部升为Q31 q31_t mag_sq = real*real + imag*imag; // 幅值平方 q15_t mag = (q15_t)(mag_sq >> 15); // 开方后右移15位 if(mag > max_mag) { max_mag = mag; max_index = i; } } // 计算频率:f = max_index * Fs / N = max_index * 40000 / 1024 ≈ max_index * 39.0625Hz float peak_freq = (float)max_index * 39.0625f;提示:
FFT_Q15要求输入数据范围[-1,1),若ADC码未归一化,需先除以32768:fft_input[i] = (q15_t)((int32_t)adc_buffer_a[i] << 1),再右移16位保证Q15格式。
5.3 降低FFT计算负载的实用策略
当CPU负载过高时,可牺牲分辨率换速度:
- 改用512点FFT:
FFT_init(512, FFT_Q15),计算量减半 - 关闭幅值计算,仅用
FFT_max_index()获取峰值位置 - 将FFT移至后台任务,每10帧采样执行1次,而非逐帧计算
本例程7606.pjt工程中已预设512点FFT配置,main.c中通过#define FFT_SIZE 512控制,修改后需重新链接IQmathLib.lib。
本文还有配套的精品资源,点击获取