简介:面向TMS320F283xx系列DSP开发者,这份工程压缩包给出了AD7606六通道16位模数转换器在CCS环境下的驱动与数据采集实现,适用于工业自动化、电力监控、医疗设备等对实时信号处理要求较高的场景。压缩包共100个文件、约429KB,核心内容包括C源文件与头文件,以及完整CCS工程配置(ccsproject、cproject、pjt)、链接命令(lkf)、启动加载(sbl)和编译日志,便于直接打开、编译与调试;AD7606支持六通道同步采样、最高2MSPS转换速率。已有616人学习浏览,资源内可见DSP2834x外设初始化、PIE中断、XINTF与系统控制等底层代码,并配有AD7606驱动和采样逻辑,可帮助理解多通道同步采样及数据读取流程。通过研读这些文件,工程师能较快掌握在DSP283xx上配置AD7606、完成实时数据采集与预处理的方法;同时obj、map等编译产物也有助于检查内存分配和排除链接问题,减少从零搭建工程与排查配置的时间成本。
1. 多通道同步采样这个需求,DSP28335和AD7606是怎么配上的
做三相逆变器电流监测的时候,最头疼的还不是电流大,而是要同一时刻测三路波形。用DSP28335内置ADC轮流采三个通道,每次转换间隔十几微秒,U相和W相之间就有了相位差,算功率的误差大到没法接受。换AD7606之后,这颗8通道同步采样的16位ADC在一条CONVST命令下同时锁存8个通道,DSP283xx的XINTF外部接口又能和它的并行总线直接对接,一次读走全部数据。这套资源就是跑通的DSP28335+AD7606驱动,包含初始化配置、并行读取、BUSY状态管理、过采样设置等完整流程,适合做电能质量监测、多相电机控制、振动分析的工程师,省掉对着数据手册查时序的功夫。
2. 硬件接口与驱动架构:先看懂AD7606的并行时序再写代码
这个资源的第一层价值是驱动代码本身,第二层价值是代码背后约定的硬件连接方式。大部分移植失败发生在原理图阶段,等代码烧进去才发现引脚对不上。下面先把接口层面的架构讲清楚。
2.1 同步采样本质:为什么内置ADC满足不了这个场景
DSP28335内置的ADC是单通道逐次逼近型(SAR),虽然转换速率标称不低,但本质上只有一个采样保持器在前端轮流切换。多通道采集时,通道1采样、转换,通道2再采样、转换,每个通道之间天然存在时间错位。对三相功率计算这种对相位一致性敏感的场景,错位会直接反映在无功功率测量结果上。
AD7606的架构完全不同。它的每个通道前都有独立的采样保持放大器,当CONVST引脚收到一个上升沿,8个通道的模拟输入同时被冻结,之后芯片内部按顺序完成8路逐次逼近转换。也就是说,虽然ADC转换核心仍是复用的,但采样时刻完全同步。对FFT分析、功率计算、故障录波这类应用,这一点是决定性的差异。
选型角度可以横向对比几个方案。第一方案是DSP内置ADC加外部采样保持器,硬件复杂度和成本都高。第二方案是选多片单通道同步采样ADC,需要额外的同步信号和GPIO资源,布线麻烦。第三方案就是把多通道做到一颗芯片里,用一条转换启动线同步,AD7606就是这个路线。8通道、16位、内置参考源、并行接口,对C2000平台来说基本是贴合的,这也是这一组合在很多电力电子项目里成为默认方案的直接原因。
2.2 引脚功能速览与关键信号连接
拿到驱动源码先别急着编译,对照硬件原理图检查引脚分配。典型连接方式如下表:
| AD7606引脚 | 功能 | 接到DSP28335 | 注意事项 |
|---|---|---|---|
| DB[15:0] | 并行数据输出 | XD[15:0] | 16位模式下全部接线 |
| CS | 片选 | XZCS2(Zone2片选) | 映射地址0x00200000 |
| RD | 读选通 | XRD | 读操作有效沿为低电平 |
| CONVST | 转换启动 | GPIO24 | 上升沿触发同步采样 |
| BUSY | 转换状态 | GPIO28 | 转换期间高电平 |
| RESET | 复位 | GPIO26 | 复位脉冲最小25ns |
| RANGE | 量程选择 | GPIO27或硬接 | 高=±10V,低=±5V |
| OS[2:0] | 过采样倍率 | 跳线或硬接 | 悬空会导致随机电平,详见避坑篇 |
| VDRIVE | IO电平基准 | 3.3V | 必须和DSP IO电平一致 |
| REFCAP | 内部参考输出 | 并联电容到地 | 参考建立需要时间 |
这张表里的VDRIVE经常被漏掉。AD7606的数字IO输出高电平不直接等于模拟电源AVCC,而是由VDRIVE引脚决定。VDRIVE接3.3V,DB[15:0]输出的高电平就是3.3V,DSP可以直接读取。接5V则输出5V,28335的GPIO输入耐压上限在4.6V左右,长期工作在过压下会加速老化甚至击穿。这是原理图阶段就要确认的,等板子打出来再改就很麻烦。
REFCAP引脚的电容也是关键。AD7606内部参考源通过REFCAP引脚外接去耦电容,容值建议按数据手册的推荐,一般用10uF钽电容并联0.1uF陶瓷电容。电容偏小会让参考电压纹波变大,直接表现为采样结果的噪声底抬高,这个噪声用软件滤波去不掉,因为它是叠加在满量程上的共模波动。
2.3 XINTF映射与读时序参数计算
数据从AD7606到DSP,走的是XINTF外部接口。28335的XINTF分六个区域,每个区域有独立片选和时序配置。常用Zone 2,基地址0x00200000。选Zone 2而不是Zone 6,主要是地址空间安排灵活,不少老工程也把Zone 2用作外扩RAM或Flash,兼容性好。
地址映射后的读取操作是单向的,只需配置XRD时序。XTIMING2寄存器里读时序由三个字段控制:XRDLEAD表示RD下降沿相对地址总线有效的提前量,对应手册里的地址建立时间;XRDACTIVE是RD低电平保持周期数,对应手册里的RD脉冲宽度参数;XRDTRAIL是RD上升沿后地址保持的周期数,对应数据释放时间。
AD7606数据手册明确RD低电平脉冲宽度最小约21ns。28335跑150MHz时,一个SYSCLKOUT是6.67ns,3个周期约20ns,擦边过不了。有的代码按默认值设3能跑通,换一批物料后偶发读错,根源就是各批次芯片最小时序略有差异。我一般设到6个周期,也就是约40ns,留一倍以上裕量。XRDLEAD和XRDTRAIL设2个周期,约13ns,对多数外设足够。
注意:这里的参数是按150MHz主频算的。如果项目把PLL配置改成了100MHz或200MHz,周期长度变了,XRDACTIVE=6对应的实际脉宽要重新计算,不能照搬。
2.4 上电复位与参考电压建立时间
AD7606内部有偏置校准逻辑,上电后必须以复位脉冲初始化,否则状态机可能停留在不确定状态。复位脉冲时序本身不复杂,但初始化顺序有讲究。
void AD7606_Init(unsigned char range) { // 第一步:量程先设好,避免复位期间悬空 GpioDataRegs.GPADAT.bit.GPIO27 = range; // 1=±10V, 0=±5V // 第二步:拉低CONVST,避免复位期间毛刺触发误转换 GpioDataRegs.GPADAT.bit.GPIO24 = 0; DELAY_US(100); // 第三步:复位脉冲 GpioDataRegs.GPADAT.bit.GPIO26 = 1; DELAY_US(10); GpioDataRegs.GPADAT.bit.GPIO26 = 0; // 第四步:等参考电压稳定 DELAY_US(100); }第二步容易被忽视。AD7606对CONVST的上升沿敏感,复位期间如果CONVST引脚恰好有毛刺,芯片会直接进入转换状态,后续的BUSY轮询逻辑全部错乱。先把CONVST拉低,等于给这个电平一个确定的初始状态。
量程设置放在复位之前,原因是RANGE引脚的输入状态在复位完成后被内部电路采用,提前设好能保证芯片第一次进入工作状态时量程已经确定。实际应用中如果工程里有别的代码段先初始化GPIO,确认和这里的设置不冲突就行。
3. 代码实现:GPIO配置、XINTF时序、转换与读取全套流程
驱动代码可以分四块看:GPIO初始化、XINTF时序配置、转换启动和状态判断、并行读取。调试的时候按这个顺序逐块验证,哪一步出了问题现象也比较容易定位。
3.1 GPIO分配与寄存器级初始化
驱动最底层是GPIO配置。28335的GPIO引脚多,功能复用寄存器也复杂,查错时最容易把GPAMUX和GPBMUX搞混。下面是实际项目里用的一组配置,GPIO24作为CONVST输出,GPIO26作为RESET输出,GPIO27作为RANGE输出,GPIO28作为BUSY输入。
#define AD7606_BASE 0x00200000 #define GPIO_RANGE 27 #define GPIO_CONVST 24 #define GPIO_RESET 26 volatile unsigned int *AD7606 = (volatile unsigned int *)AD7606_BASE; void AD7606_InitGPIO(void) { EALLOW; // GPIO24: CONVST输出,初始低电平 GpioCtrlRegs.GPAMUX2.bit.GPIO24 = 0; GpioCtrlRegs.GPADIR.bit.GPIO24 = 1; GpioDataRegs.GPADAT.bit.GPIO24 = 0; // GPIO26: RESET输出,初始低电平 GpioCtrlRegs.GPAMUX2.bit.GPIO26 = 0; GpioCtrlRegs.GPADIR.bit.GPIO26 = 1; GpioDataRegs.GPADAT.bit.GPIO26 = 0; // GPIO27: RANGE输出,默认±10V GpioCtrlRegs.GPAMUX2.bit.GPIO27 = 0; GpioCtrlRegs.GPADIR.bit.GPIO27 = 1; GpioDataRegs.GPADAT.bit.GPIO27 = 1; // GPIO28: BUSY输入 GpioCtrlRegs.GPAMUX2.bit.GPIO28 = 0; GpioCtrlRegs.GPADIR.bit.GPIO28 = 0; EDIS; }GPAMUX2的位定义对应GPIO24~31,和GPAMUX1完全不同,查数据手册时要按引脚号找到正确的寄存器。GPIO28作为输入不需要设置上拉下拉,AD7606的BUSY是推挽输出,驱动能力足够。RANGE信号一旦设好就不要在运行中改,改变量程后建议重新复位AD7606,否则内部偏移校准可能残留。
3.2 XINTF寄存器配置:把读时序调到最稳的状态
Zone 2的配置涉及XTIMING2的若干字段,以及XINTCNF2里的保持控制。实际代码:
void AD7606_InitXintf(void) { EALLOW; // 使能Zone2片选与全局保持 XintfRegs.XINTCNF2.bit.ENABLE_ZHOLD = 1; XintfRegs.XINTCNF2.bit.HOLDAS = 0; // 读时序:前导2周期,有效6周期,尾随2周期 XintfRegs.XTIMING2.bit.XRDLEAD = 2; XintfRegs.XTIMING2.bit.XRDACTIVE = 6; XintfRegs.XTIMING2.bit.XRDTRAIL = 2; // 写时序:AD7606用不到,但保持最小有效配置 XintfRegs.XTIMING2.bit.XWRLEAD = 1; XintfRegs.XTIMING2.bit.XWRACTIVE = 3; XintfRegs.XTIMING2.bit.XWRTRAIL = 1; // 16位数据总线,不使用XREADY握手 XintfRegs.XTIMING2.bit.XSIZE = 1; XintfRegs.XTIMING2.bit.READYMODE = 0; EDIS; }XRDACTIVE用6而不是更小,前面推算了21ns下界对应3个周期,但实际信号线上还有PCB走线电容和AD7606内部输出延迟,DSP输入端的采样窗口也有建立时间要求。留40ns的好处是无论元件批次差异还是温度变化,都能稳定读取。XWR字段给的三个周期实际用不到,纯粹为了让XINTF模块内部状态机正常,不配置也行,但保持显式赋值更稳妥。
XINTCNF2里的HOLDAS字段控制XINTF仲裁。如果系统里只有一个外设挂在XINTF上,保持关闭没问题。如果还挂了SRAM或Flash,需要合理配置仲裁周期,不然两者轮流访问时会出现总线冲突。
3.3 转换启动与BUSY轮询:电平翻转的时序细节
启动转换最简单的方式是GPIO翻转。翻转速度受限于GPIO数据寄存器写时序,但对于采样率在几十kHz以内的应用完全足够。更快更精准的做法是把CONVST接到PWM模块输出,用PWM比较操作产生转换脉冲,适合高频采样。
void AD7606_SoftwareStart(void) { // 拉低,确保一个完整的低电平脉冲 GpioDataRegs.GPADAT.bit.GPIO24 = 0; DELAY_US(5); // 低电平保持时间远大于25ns // 上升沿触发同步采样 GpioDataRegs.GPADAT.bit.GPIO24 = 1; DELAY_US(1); // 拉回低电平,准备下一次转换 GpioDataRegs.GPADAT.bit.GPIO24 = 0; } unsigned char AD7606_WaitBusyLow(void) { unsigned int timeout = 0; while (GpioDataRegs.GPADAT.bit.GPIO28 == 1) { if (++timeout > 2000) { return 1; // 超时返回错误 } } return 0; // BUSY已变低,转换完成 }DELAY_US在28335的官方库里有,但它的精度依赖系统时钟频率正确配置。如果时钟初始化把SYSCLKOUT设成了150MHz,延时基本准确;如果倍频配置不对,DELAY_US的实际时长偏移可能达到两倍以上。BUSY轮询里的超时计数器做成2000次循环,在最坏情况下对应几毫秒,既能兜底又不至于在主循环里卡太久。
3.4 并行读取与多通道数据解析
读数据的核心是每次对XINTF地址读一个16位整数。访问过程由硬件自动产生CS和RD脉冲,CPU不需要手动控制RD引脚。因为AD7606_BASE指针声明为volatile,编译器不会做合并读取优化,每次循环都会真实访问一次外部总线。
void AD7606_ReadChannels(unsigned int *buffer, unsigned short ch_cnt) { unsigned short i; for (i = 0; i < ch_cnt; i++) { buffer[i] = *AD7606; } }读取顺序固定是V1到V8,地址每次相同。如果只要采样两路,ch_cnt传2即可,但AD7606在转换时依然是8路同时转换,只是DSP只读前两路结果。若要连续读取全部8通道,buffer长度至少8个unsigned int。
读到的编码是16位二进制补码,范围-32768到32767。对应电压换算关系很简单:±10V档时,电压值等于编码值乘以10再除以32768;±5V档时,电压值等于编码值乘以5再除以32768。上位机显示时可以直接用有符号short类型转换。
3.5 软件滤波与后台调度:过采样之外的降噪手段
过采样引脚OS[2:0]可以配置内部硬件平均,倍率固定为2/4/8。软件层面还能做滑动平均,对每个通道累积16次转换值再右移4位,达到降低白噪声的目的。
#define SAMPLE_BUF_SIZE 16 unsigned int AD7606_GetAveragedValue(unsigned short ch_index) { unsigned int sum = 0; unsigned short i; for (i = 0; i < SAMPLE_BUF_SIZE; i++) { AD7606_SoftwareStart(); AD7606_WaitBusyLow(); // 只读目标通道,其余通道数据丢弃 sum += *(AD7606 + ch_index); } return (sum >> 4); }这个函数每次调用会启动16次转换,适合低速采集。如果采样率只需要1kSPS,用软件平均比硬件过采样更灵活,因为平均次数可以是16或32,不必局限于2的幂。代价是平均期间CPU被采样循环占住,不能处理其他任务,所以平均逻辑一般放在后台任务里,或者用中断加标志位的调度方式避开主循环。
4. 避坑记录:移植中五个高频问题与排查路径
AD7606看起来是一个简单的并行ADC,真接到28335上会遇到不少只在特定条件下才暴露的坑。下面按调试顺序整理五个常见问题,每条按现象、原因、解决的思路记录,遇到类似表现可以直接对应。
4.1 采样值整体偏大,校准系数调不动
现象:给V1通道输入1.000V直流,读到的码值是9000左右,按±5V档计算应该是6554附近,偏差接近30%。换个通道也一样偏移。
原因:RANGE引脚的电平与代码配置不一致。RANGE由GPIO控制,GPIO在上电复位期间默认是高阻输入,RANGE引脚因此悬空,芯片内部下拉让量程落到±5V档,而代码按±10V档做编码映射,数值自然偏移。还有一种情况是硬件上RANGE引脚根本没接,悬空状态下的量程不符合预期。
解决:用万用表直接量RANGE引脚直流电平,确认是高还是低。若由GPIO控制,在初始化函数里先让GPIO输出固定值,延时100ms,然后才给AD7606复位。这样量程信号在复位前已经稳定,保证第一次转换就按正确量程工作。
4.2 转换卡死在BUSY高电平
现象:调用启动转换后,程序在等待BUSY变低的循环里出不来,仿真器暂停后看代码指针停在while里。示波器上BUSY引脚一直保持3.3V高电平。
原因:CONVST低电平脉宽不足。AD7606对CONVST的低电平要求最小25ns,代码写的是先拉低再拉高,中间只有一条GPIO写语句,实际宽度由编译器生成的指令序列决定,可能只有十几纳秒。另一个常见诱因是高优化等级下,编译器把对同一寄存器的两次连续写合并成一次,根本没有产生下降沿再上升沿的过程。
解决:在拉低和拉高之间插入DELAY_US,保证低电平宽度大于50ns。同时把CONVST的两次写操作分别放在两个小函数里,避免写合并优化。更稳的做法是把CONVST接到PWM输出脚,用PWM比较事件触发,波形由硬件产生,彻底不受编译器优化影响。
4.3 并行读数偶尔出现不合理毛刺
现象:采集正弦波过程中,波形98%的点与实际信号吻合,但偶尔冒出接近正负满量程的跳变点,间隔没有规律。
原因:XRDACTIVE设置偏小,AD7606数据输出在RD上升沿附近没有稳定。另一类诱因是中断嵌套:中断服务程序里访问了XINTF地址,打断了正在进行的连续读取,中断返回后数据线上的电平已经变化,读到的值不确定。
解决:先把XRDACTIVE调到6,确认跳变率明显下降。再把所有XINTF地址访问放到主循环的临界区,由关中断保护。如果中断确实需要读取ADC结果,在中断服务程序里一次性转存读到的值,禁止在中断里连续多次访问同一地址。
4.4 OS引脚过采样设置不生效
现象:OS[2:0]配置成4倍过采样,波形毛刺幅度几乎没变化,用FFT看噪声底也没有下降。
原因:OS引脚的电平在转换启动时被采样,而初始化顺序里OS配置在完成第一次CONVST之后,之前的转换都按过采样关闭处理。另外,OS引脚如果悬空,内部逻辑可能读到随机电平,过采样倍数随机且不稳定,时好时坏的那种情况多半就是这个。
解决:在所有其他AD7606寄存器配置之前先设置OS[2:0]对应GPIO状态。如果是跳线帽连接,检查跳线帽是否完全接触。实际工程里推荐将OS引脚用10k电阻下拉到地,需要时再配置GPIO驱动,硬件上保证不悬空。
4.5 复位后首次读数全零,受影响后再跑一次正常
现象:刚烧录程序后第一次读到的8通道数据全部为0,按复位键再运行第二次,数据正常。把看门狗关掉后第一次就正常。
原因:电源域建立时间不足。3.3V电源在DSP时钟稳定前已经达到阈值,但AD7606内部参考电压还没建立完成。复位脉冲发出后,参考源仍在爬升,此时转换输出无效。看门狗介入后,等主程序重新执行时参考电压反而稳定了,所以看起来像第二次才正常。
解决:拉长上电后的等待时间,从100ms改成500ms再初始化AD7606。同时避免在初始化函数里依赖看门狗喂狗操作,因为看门狗复位会导致初始化重新执行,而采集任务状态没有恢复。
5. 进阶实践:用DAC回环测试验证同步采样精度
拿到驱动代码后,直接上传感器是风险最大的路径。我自己的习惯是先搭一个回环测试平台:用DSP的PWM模块配合外部RC滤波生成已知正弦波,送入AD7606的V1通道,同时用另一个相位偏移90度的PWM输出送入V4通道,通过对比两路采集结果的相位差,验证同步采样是否真实生效。
5.1 搭建回环测试链路
PWM模块工作在计数模式,输出50%占空比、频率可调的方波,经过二阶RC低通滤波后得到平滑正弦波。滤波器截至频率设为信号频率的5倍以上,保证波形不畸变。AD7606采样率设为10kSPS,每次启动转换后等待BUSY变低,把8个通道全部读入缓冲区,通过SCI或者CAN传到上位机做分析。
5.2 用互相关检验相位差
用MATLAB做互相关分析,确认V1和V4之间的相位关系是否精确等于设定值。
fs = 10000; % 采样率,与驱动配置一致 t = (0:length(v1)-1) / fs; V1 = v1 - mean(v1); % 去直流分量 V4 = v4 - mean(v4); [c, lag] = xcorr(V1, V4, 50, 'coeff'); [~, idx] = max(c); phase_diff = lag(idx) * 360 * 50 / fs; % 换算为角度如果相位差在设定频率下等于90度且误差小于0.5度,说明同步采样没有引入额外相位误差。如果偏差超过1度,先检查CONVST信号是否存在毛刺,再检查PCB走线是否让V1和V4在模拟输入端引入了串扰。
5.3 高速采样时改用DMA读取
采样率超过50kSPS时,CPU在每次转换后进入读取函数会有中断延迟,导致有效采样率不稳定。改用DMA可以避免CPU逐点读取。配置要点是把DMA的源地址设为XINTF地址,触发源设为BUSY下降沿对应的事件。DMA每次搬运8个16位数据到内存缓冲区,传输完成后触发一次CPU中断,CPU对缓冲区做FFT或存储,这套方案能把有效采样率推到100kSPS以上。
实测中同样的XINTF配置在28335上稳定,搬到28379D时因为主频变成200MHz,XRDACTIVE从6可以收紧到4,读取时序余量变大。这提示一个通用检查项:不同型号DSP的XINTF输出波形与寄存器配置存在微小差异,移植到新平台必须用示波器抓一次XD和XRD信号,确认RD低电平实际宽度。从那以后,每次换DSP型号,我都强制走一遍“示波器抓XD/XRD、确认脉宽、再跑采集”的流程,这套流程节省的查错时间远超配置时间。希望帮到你。
本文还有配套的精品资源,点击获取