TMS320F28335用GPIO模拟并行接口驱动DAC7724的完整实现
2026/9/16 3:00:16 网站建设 项目流程

简介:一份面向嵌入式开发者的数字信号处理器(DSP)数模转换例程包,围绕TMS320F28335浮点数字信号处理器与DAC7724四通道数模转换芯片展开。资料核心是C语言源程序及配套的工程框架,包含CCS工程文件、链接配置文件、调试生成的目标文件与编译日志,演示如何通过处理器精确控制十六位数模转换芯片,实现高精度模拟量输出。压缩包共25个文件,约329KB,除源代码与工程配置外,还有官方驱动模块的中间目标文件、调试输出与映射文件,以及符号数据库文件,基本覆盖从程序编译到烧写调试的完整环节。目前已有217人学习下载。由于包内同时保留调试中间文件和源码,学习者可对照源码逐模块分析初始化流程、数模转换时序和输出更新方法,快速迁移到电机控制、信号发生、电源管理等实际项目中。

1. 先看清楚28335的DAC困局:选外置DAC7724的理由

一块TMS320F28335,AD口有16个通道,片上DAC却一个都没有。做运动控制或者波形发生器时,模拟输出只能靠外部DAC补位。DAC7724是TI早期的并行12位四通道DAC,数据更新快,双缓冲结构正好适合多轴同步输出。虽然SPI型DAC(如DAC8552)接线更少,但28335的GPIO模拟SPI要消耗CPU周期,对纳秒级同步有要求时,并行接口直接写入更利索。这篇文章把28335用GPIO驱动DAC7724的完整做法,包括引脚接线、GPIO模拟时序、精度校准和PCB布局要点,按可复现的方式写出来。手里有这两颗料,正在画原理图和调程序的同事,可以直接按这个路数推进。

2. DAC7724的引脚时序和28335接口设计

先把DAC7724到底怎么被“操作”讲清楚,再决定接线和代码结构。

2.1 DAC7724的通道选择、双缓冲和一次完整更新

DAC7724内部有四个完整的DAC通道,每个通道由两级锁存器组成:输入锁存器和DAC锁存器。A1、A0地址线负责选中当前输入锁存器,数据线DB11~DB0在WR上升沿到来时被锁存到对应通道的输入锁存器。LDAC信号低电平时,四个输入锁存器的数据同时被送到各自的DAC锁存器,这个动作才是DAC输出真正变化的时间点。这样的好处是:四个通道的电压值可以先分别写进去,再统一拉低LDAC,做到同步更新。编程时最容易犯的错是只关心数据码值,忽略A1/A0之前是否稳定,导致数据写进了不期望的通道。

写时序很简单,把CS、WR都拉低,数据放到总线上,地址线给上通道号,然后让WR从低到高,数据就被锁存。之后再做LDAC处理。28335的GPIO输出翻转速度在几十ns量级,完全匹配DAC7724的写入要求。

提示:DAC7724没有读回功能,所有寄存器都是只写,因此排错时不能用总线回读确认状态,必须依赖示波器和输出电平时序。

2.2 28335的GPIO与DAC7724的接线映射表

实际工程中直接用GPIO模拟总线,不建议用XINTF,因为XINTF的时序配置对初学者不友好,而且DAC7724的写入速度要求并不高。常用引脚分配如下表,这是一套经过验证的映射,也可以按自己板子调整。

DAC7724引脚28335的GPIO方向说明
DB0~DB11GPIO0~GPIO11输出12位并行数据,连接到28335低12位数据GPIO
A0GPIO12输出通道地址最低位
A1GPIO13输出通道地址最高位
CSGPIO14输出片选,低有效
WRGPIO15输出写信号,低有效,上升沿锁存
LDACGPIO16输出更新信号,低有效
RESETGPIO17输出复位,低有效,平时拉高

这里有一个兼容性关键点:DAC7724的逻辑输入高电平阈值在VLOGIC=5V时大约是2.0V,而28335的GPIO输出高电平为3.3V,所以不需要电平转换芯片。但要注意DAC7724的VLOGIC和数字地建议接3.3V电源域,避免长线传输压降导致高电平不够。

2.3 读写时序参数和一组GPIO宏

DAC7724手册给出的典型时序参数中,数据建立时间、保持时间都在几十纳秒量级。28335的GPIO操作使用内存映射寄存器,单次赋值大概几十ns,加上数据传输和地址设置,完全满足需求。为了代码可读性,先把控制引脚定义为宏:

#define DAC_CS_LOW GpioDataRegs.GPADAT.bit.GPIO14 = 0 #define DAC_CS_HIGH GpioDataRegs.GPADAT.bit.GPIO14 = 1 #define DAC_WR_LOW GpioDataRegs.GPADAT.bit.GPIO15 = 0 #define DAC_WR_HIGH GpioDataRegs.GPADAT.bit.GPIO15 = 1 #define DAC_LDAC_LOW GpioDataRegs.GPADAT.bit.GPIO16 = 0 #define DAC_LDAC_HIGH GpioDataRegs.GPADAT.bit.GPIO16 = 1

这些宏直接操作寄存器,比调用函数快得多。注意如果其它外设用了GPIO16,需要先确认没有抢占。模拟总线的核心动作就是:先给地址,再写数据,最后提升WR。不需要额外的IO口做读操作,因为DAC7724没有读回总线。这就是最底层的操作逻辑。

3. 28335用GPIO模拟写DAC7724的完整C代码

下面给出可以直接跑的最小代码。这里不依赖TI的官方驱动库函数,只写顶层寄存器操作,在CCS环境和裸机工程里都能编译。

3.1 GPIO复用与方向配置

DSP28335上电后GPIO默认是IO模式,但GPAMUX寄存器可能被boot程序改写。最稳妥的办法是在初始化代码里显式设置复用和方向。下面是初始化函数:

void DAC_GPIO_Init(void) { EALLOW; // GPIO0~GPIO15 由 GPAMUX1 控制,GPIO16~GPIO17 由 GPAMUX2 控制 // 全部设为普通IO模式 GpioCtrlRegs.GPAMUX1.all = 0x00000000; // GPIO0~GPIO15 GpioCtrlRegs.GPAMUX2.all = 0x00000000; // GPIO16~GPIO31 // 方向:GPIO0~GPIO17 全部输出 GpioCtrlRegs.GPADIR.all = 0x0003FFFF; // 低18位置1 // 控制引脚恢复默认状态:CS、WR、LDAC、RESET 全部置高 GpioDataRegs.GPADAT.all |= 0x0003FFFF; EDIS; }

这段代码里GPAMUX1GPAMUX2全部清零,确保没有任何GPIO引脚被复用为外设功能。GPADIR低18位置1,把GPIO0到GPIO17全部配置为输出。最后通过GPADAT把CS、WR、LDAC、RESET都置高,避免上电瞬间的毛刺触发误写入。EALLOW和EDIS是TMS320F28335访问受保护寄存器的标准写法,不写的话寄存器写操作会被CPU忽略。

3.2 写DAC通道的函数实现

写DAC时要把数据放到GPIO0~GPIO11上,但同时不能干扰高位的控制引脚。所以使用GPADAT.all读出当前状态,再对低12位做替换:

void DAC_WriteChannel(unsigned int ch, unsigned int data) { unsigned long temp; // 限制ch范围0~3,data范围0~4095 ch = ch & 0x03; data = data & 0x0FFF; // 设置通道地址 A0、A1 GpioDataRegs.GPADAT.bit.GPIO12 = ch & 0x01; GpioDataRegs.GPADAT.bit.GPIO13 = (ch >> 1) & 0x01; // 把数据写入低12位,保留高6位(GPIO12~17)状态 temp = GpioDataRegs.GPADAT.all; temp = (temp & 0xFFFFF000) | data; GpioDataRegs.GPADAT.all = temp; // 产生WR上升沿锁存数据 DAC_CS_LOW; DAC_WR_LOW; asm(" NOP"); asm(" NOP"); DAC_WR_HIGH; DAC_CS_HIGH; }

参数说明:ch是0到3,对应DAC7724的A1/A0组合;data是12位码值,0x000到0xFFF。为什么要用temp而不是直接改GPADAT?因为GPADAT的写操作会影响整个端口,如果把所有位一起写了,其他控制引脚状态就会丢失。这里的asm(" NOP")是为了保证WR低电平持续时间足够,两条NOP后,低电平宽度通常在几十纳秒,满足多数DAC7724批次的要求。如果你的芯片走线过长或者时钟频率较低,可以增加到四条NOP。

3.3 双缓冲四通道同步更新

双缓冲的意义在于同时改变四路输出。写代码时先调用四次上面的函数把数据送进输入锁存器,最后一口气拉低LDAC:

void DAC_UpdateAll(unsigned int dac0, unsigned int dac1, unsigned int dac2, unsigned int dac3) { DAC_WriteChannel(0, dac0); DAC_WriteChannel(1, dac1); DAC_WriteChannel(2, dac2); DAC_WriteChannel(3, dac3); DAC_LDAC_LOW; asm(" NOP"); asm(" NOP"); DAC_LDAC_HIGH; }

这里有一个需要注意的细节:DAC_WriteChannel内部已经拉高了LDAC,所以四通道写入之间输出不会变化,等DAC_LDAC_LOW同步更新。如果你的应用需要每个通道立刻反映码值,那就在写完单通道后单独拉低一下LDAC。两种用法各有场景:伺服同步输出用DAC_UpdateAll,普通调试单通道用前者。

4. 输出精度、双极性校准和PCB抗噪的3个关键点

代码跑通只是第一步,DAC输出值准不准、纹波大不大,往往决定这块板子能不能用在控制环里。

4.1 基准电压和12位码值到输出电压的换算

DAC7724的输出电压由基准电压VREF决定。在单极性模式下,输出范围是0到2倍VREF,计算公式是:

Vout = (N / 4096) × 2 × VREF

这里的N是写入的12位码值。若VREF=2.5V,那么输出范围约0到5V。1个LSB对应:

1 LSB = (2 × VREF) / 4096 = 5 / 4096 ≈ 1.22mV

双极性输出则常用外部运放把0~5V偏置到±2.5V:

Vout = ((N / 4096) × 2 × VREF) - VREF

比如N=0时输出-2.5V,N=2048时输出0V,N=4095时接近+2.5V。这个换算关系要提前做成表格或者宏定义,否则PID算法里一旦用百分比表示期望电压,很容易把码值和实际电压算反。

注意:DAC7724的零点和满度都有微小失调,批量板子之间会有几个LSB偏差,要求高时需要在后台做一次出厂校准,把校准系数存到EEPROM或Flash里。

4.2 双极性偏置电路的接法

DAC7724本身不直接输出负电压,需要外接一个运放做偏移。常见做法是在DAC输出端加一个跟随器隔离,再与一个-2.5V基准通过加法器合成,增益为1。调零时给DAC写入0x800,调节电位器使输出为0V;满度校准时写0xFFF,调整增益电阻使输出为接近+2.5V。

这里要注意运放的输入失调电压和温漂。如果选LM358这类通用运放,零位可能会漂几十毫伏,对12位输出来说不可接受。建议使用高精度低失调的运放,例如OPA2188或OP07等级。另外一个隐含坑是:DAC7724的输出放大器是轨到轨吗?不是,它靠近电源轨时有饱和区,0码值和满码值附近的线性度会变差,所以设计双极性电路时给输出留出5%的裕量,不要满量程硬怼到电源电压。

4.3 时钟抖动与电源噪声对DAC输出影响的3个PCB布局要点

“时钟抖动与电源噪声”是并行DAC的老话题。DSP28335的GPIO翻转会产生开关电流,布局不合理时,噪声会耦合到DAC参考源和输出端。

要点1:在DAC7724的每个电源引脚就近放0.1uF陶瓷电容和10uF钽电容。AVDD和VREF必须分开走线,模拟电源从LDO单独出来后先到10uF再到0.1uF,然后才进DAC引脚。数字电源和模拟电源不要共用一个过孔,否则高频开关电流会直接串入模拟电源。

要点2:保持VREF走线下面有完整的地平面,不要在基准线下面走数字GPIO线。GPIO数据线在靠近28335引脚或靠近DAC引脚处加22欧姆到33欧姆串联电阻,抑制信号振铃。这个电阻值不是随便定的,它匹配了信号源阻抗和走线特征阻抗,能把上升沿过冲减小三分之一以上。

要点3:DSP的时钟源或XCLKOUT不要贴着DAC7724的VREF引脚。很多工程师为了布局方便,把晶振放在模拟区,结果VREF上出现几十毫伏的毛刺。把晶振和数字开关器件移到板边,模拟区走线尽量加宽并远离时钟线。这些措施都是在输出精度上真正起决定性的东西,比软件多写几行滤波重要得多。

5. 验证DAC7724输出波形的一个实用技巧

5.1 用斜坡码值配合示波器观察单调性

先用一个简单的斜坡输出函数,从0递增到4095,每步做一个很小的延时,单通道输出:

void DAC_RampTest(void) { volatile unsigned int i, j; for(i = 0; i < 4096; i++) { DAC_WriteChannel(0, i); for(j = 0; j < 1000; j++); } }

用示波器直流耦合看输出,应该看到整齐的阶梯波。如果出现某一步跳变或者台阶方向反了,多半是数据线接反了,用GPIO映射表逐根核对;如果波形是纯离散的乱跳,先查GPADIR方向寄存器是不是被意外改掉了。

5.2 用定时器中断实现等间隔DAC输出

很多波形生成要求固定采样率,靠软件延时并不精确。实用技巧是使用28335的CPU定时器0产生周期中断,在中断服务函数里取出下一个码值写入DAC。定时器周期值这样算:周期值 = 主频 / 采样频率,例如150MHz主频,希望10kHz采样率,周期就是15000。

interrupt void CpuTimer0_ISR(void) { static unsigned int idx = 0; // 查表输出16点正弦波,表长可以自行扩展 DAC_WriteChannel(0, sinTable[idx]); idx = (idx + 1) % 16; PieCtrlRegs.PIEACK.all = 0xFFFF; }

这里要注意中断服务函数里不做浮点数学运算,正弦表提前计算好。中断触发的同步性比主循环好很多,抖动可以控制在微秒量级。验证多通道同步时,可以用一个空闲GPIO在DAC_UpdateAll前翻转,用双通道示波器同时看两路输出,确认LDAC同步动作是否一致。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询