AD5686R多路DAC电路设计与SPI驱动实现指南 📅 发布时间:2026/9/16 4:57:26 👁 浏览次数: 简介这是一套面向毕业设计、课程设计与工程实训的AD5686R数模转换器DAC完整工程资料包含电路原理图、PCB设计文件以及兼容AD5684/AD5685/AD5686的驱动源码。适用于需要快速完成模拟输出模块开发或硬件设计参考的电子、嵌入式方向学生与开发者。资料整体以zip压缩包形式提供约60.06MB主要涵盖原理图、PCB工程、驱动源码及说明文档等类型所有工程均经严格测试可正常运行拿到后即可轻松复现。目前已有161人学习下载。其亮点在于驱动代码兼容多型号DAC可直接移植到STM32等主流MCU平台减少外设适配工作量原理图与PCB为硬件设计提供完整参考配套说明和目录结构清晰适合作为课程设计、毕业设计或大作业的整体方案。包内包含完整源码、工程文件及使用说明可支撑从硬件设计到驱动调试的全流程复刻。若复刻中遇到问题作者提供技术支持可协助排错也可在此基础上扩展更多功能用于项目立项或学习练手。1. 为什么做多路模拟输出要选 AD5686R做毕设里的四路波形发生器或者实训台上需要一路可调的 0~5V 偏置选 DAC 时 AD5686R 很容易被搜到。它是一片四通道、16 位、带内部基准的 nanoDACSPI 接口、同步更新、菊花链都齐不用再为基准源单独画一个 LDO 加精密基准的电路。很多同学第一次打样完量输出发现电压偏高或乱跳问题多半是 REF 引脚去耦、地平面处理和 SPI 帧位序不是芯片本身。这篇按原理图、PCB、驱动三条线写只要照着把引脚接对、把帧拼对AD5684、AD5685、AD5686 三款都能跑。无论你是做课程设计还是毕业设计这套方案都能直接落在板子上。2. AD5686R 电路原理图设计引脚分组、电源去耦和输出缓冲先从芯片本身要接的引脚看起。AD5686R 看起来是一个 16 引脚的小封装但引脚功能其实分成四组电源、基准与量程、SPI 控制、模拟输出。分组清楚之后原理图就不容易漏上拉电阻或漏放基准电容。2.1 AD5686R 的最小系统先分清四个引脚组下面这张表是我画原理图时习惯用的检查表顺序从电源到输出防止只盯着 SPI 三个引脚而把 LDAC、RST 这些控制引脚当普通 IO 处理。引脚组典型引脚原理图设计要点电源VDD、GND、VLOGICVDD 用 2.7~5.5V逻辑电源按 MCU 电平接两者之间不要互相串二极管基准与量程REF、GAINREF 对 GND 放 1uF 以上瓷片电容GAIN 决定输出范围是 0~VREF 还是 0~2VREF控制与 SPISYNC、SCLK、SDIN、SDO、LDAC、RSTSYNC 空闲拉高LDAC 不用时建议引到 GPIORST 通过 10k 上拉到逻辑电源模拟输出VOUTA、VOUTB、VOUTC、VOUTD负载较重时每个输出都要接一级运放缓冲不能直接拖长线画 AD5686R 电路原理图时我一般不把 SYNC 直接接地。虽然芯片只有这一根片选但驱动里写一帧数据需要 SYNC 产生下降沿再拉高如果接地SCLK 连续翻转会把无关数据灌进去。所以 SYNC 一定接到 MCU 的 GPIO软件模拟 SPI 时就用它切帧。REF 引脚是另一个容易踩坑的地方。AD5686R 带内部基准REF 引脚会把 2.5V 基准引出来需要在 REF 和 GND 之间放一个 1uF 电容。这个电容不要离引脚太远否则基准噪声会直接叠到输出上。如果后续想用外部基准覆盖原理图上最好在 REF 和外部基准源之间串一个 0Ω 电阻位调试时再决定要不要贴。2.2 电源去耦电容的位置比容量更有说头很多参考设计只标“VDD 加 0.1uF”但对这种内部同时有模拟电路、基准和数字逻辑的芯片电源去耦要分两级。10uF 负责低频储能100nF 负责高频瞬态两个电容都必须靠近各自服务的引脚。下面是一个可以直接抄的最小电源连接片段* AD5686R 电源与基准连接 VDD -- C1(10uF) ---- GND /* 储能放电源入口 */ -- C2(100nF) ---- GND /* 高频去耦贴 VDD 引脚 */ VLOGIC -- C3(1uF) ---- GND /* 逻辑侧去耦靠近 VLOGIC */ REF -- C4(1uF) ---- GND /* 内部基准输出C4 优先选 NP0/C0G */ RST -- R1(10k) ---- VLOGIC /* 上拉防止上电误复位 */ LDAC -- R2(10k) ---- VLOGIC /* 默认高软件或硬件低电平更新 */这里 C2 的位置最严格它要直接从 VDD 引脚和 GND 引脚之间跨接过孔不要绕远。C4 则决定基准电压的干净程度如果选 X7R 也能工作但温度变化大时输出会有极小漂移。LDAC 的上拉电阻可以换成跳线调试时先拉高只靠 SPI 命令更新等确认通信没问题再试硬同步。还要注意一个顺序问题VDD 和 VLOGIC 不属于同一个电源域时上电要先给 VDD 再给 VLOGIC。原理图上可以在 VLOGIC 输入串一个 0Ω 电阻方便调试时单独断开逻辑电源。2.3 输出缓冲和 GAIN 引脚的配合AD5686R 自带的输出级可以驱动一般高阻抗负载但不要直接把四个输出连到 ADC、比较器或长排线。常见做法是每个通道加一个单位增益缓冲运放输出端再串 100Ω 电阻做隔离。运放选型不用太高端轨到轨输入输出、失调小于 100uV 的即可比如 OPA2187、ADA4522-2 这类双运放一片覆盖两个通道。GAIN 引脚直接决定输出量程拉低时输出 0~VREF拉高时输出 0~2VREF。若用内部基准 2.5V拉高后满量程是 5V这时候 VDD 不要卡在 5V 上要留一点输出级压差余量。原理图上可以在 GAIN 放一个 0Ω 电阻和 10k 下拉电阻默认 0~2.5V需要 0~5V 时把 0Ω 贴到 VDD 侧。输出缓冲级的反馈线要从运放输出端直接回到反相输入端中间不要经过过孔或窄走线。AD5686R 的 VOUT 到运放同相输入端之间串一个 100Ω 电阻可以防止运放驱动长线时发生振铃。画完原理图用 ERC 查一遍未连接引脚重点确认 SYNC、LDAC、RST 没有悬空基本就可以进入 PCB 布局了。3. AD5686R 的 PCB 布局布线层叠、走线要求和铜皮处理DAC 的 PCB 设计难点不在走通而在让模拟输出不受数字控制信号干扰。AD5686R 是四通道同时输出任何一个通道的 VOUT 线如果从 SCLK 下面穿过去输出纹波都很难压住。下面按层叠、摆放、走线三个顺序说。3.1 PCB 层叠双面板够用但四层板更好做如果只是课程设计双面板完全能跑通前提是底层不要铺乱七八糟的线最好底层只放去耦电容和 SPI 线。量产板或者输出精度要求高的板子建议直接上四层板信号完整性会好做很多。层双面板方案四层板方案TopAD5686R、运放、模拟走线AD5686R、模拟走线、基准电容Inner 1无GND 完整地平面Inner 2无5V/3.3V 电源平面Bottom去耦电容、SCLK/SDIN 走线数字控制线、接插件四层板的内层地不要分 AGND 和 DGND。很多教程让模拟地单点连接但在 AD5686R 这种单芯片系统里只要地平面完整数字返回电流会自动走芯片下方的局部环路。把地平面切开反而会让 SCLK 的返回电流绕远路串扰进模拟输出。双面板虽然没有完整内层地但也要在底层尽量铺地不要出现断续的地线。PCB 层叠厚度一般选 1.6mm 标准板四层板内层间距控制在 0.1mm 左右保证 GND 到信号层的电容足够大。AD5686R 的 SCLK 最高 50MHz如果 MCU 只用 1MHz不需要严格阻抗控制但 SPI 线还是要短不要绕半块板。3.2 芯片位置与去耦电容布局顺序决定后面好不好调PCB 布局我习惯按“模拟电流路径”来排AD5686R 放在中间偏模拟区四个 VOUT 朝输出接口方向SPI 引脚朝 MCU 方向REF 电容和 100nF 去耦电容放同一面。这样板子背面可以只走数字线避免模拟信号和 SPI 线交叉。有一个容易忽略的点C2 的 100nF 和 C4 的 1uF 不是随便摆在芯片附近就行而是要让“芯片 VDD 引脚 - 电容焊盘 - GND 过孔”这条回路面积最小。先打过孔到地、再走线去电容焊盘的做法会增加回路电感。正确顺序是电容放 VDD 和 GND 焊盘旁边过孔从电容的地端打下去。如果芯片下方正好是电源平面可以在 AD5686R 正下方保留一块完整的 GND 铜皮。REF 引脚附近的铜皮不要大面积挖空否则基准电容的返回路径会变大。反过来运放反馈输入端那种高阻抗节点如果铜皮离得太近会引入杂散电容这时才需要把周围铜皮挖空 0.3mm 左右。3.3 AD5686R 周边走线规则与 DRC 检查PCB 走线要求按网络等级分开设不要所有线都用一样宽。DAC 的 VOUT 线上电流很小0.3mm 已经足够但电源线不能这么细。下面是我在 Altium Designer 20 里常用的一套规则嘉立创 EDA 里也能按相同思路设置。网络线宽间距要求说明VDD、GND0.4mm 以上0.3mm电源走线越短越好SCLK、SDIN、SDO、SYNC0.2mm与模拟线间距 0.5mm控制线不要包地包得过近VOUTA~VOUTD0.3mm0.3mm输出走线短且直别绕弯REF0.3mm0.5mm基准网络要加 Net Class避免被 SCLK 干扰Altium Designer 20 的 PCB DRC 检查里默认间距往往只有 6mil对模拟板不够。建议把全局间距改到 10mil再把上述模拟网络单独建一个 Net Class间距设成 20mil。跑 DRC 时重点看两类错误一类是模拟走线和 SPI 线平行距离过长另一类是去耦电容的地孔没有就近打到地平面。布线时不要追求把 SCLK 和 SDIN 包地。对 AD5686R 来说完整地平面比包地更有效包地线如果两端接地不当反而变成天线。SPI 线距离模拟输出线远一点比任何屏蔽都管用。4. 兼容 AD5684、AD5685、AD5686 的 SPI 驱动程序命令、位操作和同步更新AD5686R 的驱动核心是一个 24 位 SPI 帧。只要把这一帧拼对AD5684、AD5685、AD5686 都能用因为三款芯片的寄存器结构和命令编码一致只是有效数据位数不同。这也是很多参考代码把三款型号写在一个驱动文件里的原因。4.1 24 位 SPI 帧的位分配和命令编码每帧从最高位开始发送先发 DB23最后发 DB0。DB23 是读写控制位写操作置 0DB22 到 DB20 是命令DB19 到 DB16 是 DAC 地址DB15 到 DB0 是数据。位名称说明DB23R/W0 表示写1 表示读回DB22:DB20CMD0x01 写输入寄存器0x02 更新 DAC 寄存器0x03 写输入并更新 DACDB19:DB16ADDR0x0~0x3 对应四个通道0xF 表示全部通道DB15:DB0DATA16 位 DAC 数据对 AD5684数据是 12 位有效位放在 DB15:DB4AD5685 是 14 位有效位放在 DB15:DB2AD5686 才是完整 16 位。所以驱动里不能用一个写死的数据寄存器值要根据芯片型号做左移。4.2 用 GPIO 模拟 SPI 写一个最稳的底层函数SPI 外设的极性在各家 MCU 库里叫法不一致我在移植 AD5686R 驱动时更愿意用 GPIO 模拟先把时序跑通再决定是否换硬件 SPI。AD5686R 的 SCLK 空闲是低电平SDIN 在上升沿被锁存对应 SPI Mode 0。/* ad5686r.h */ #define AD5686_CMD_WRITE_INPUT 0x01u #define AD5686_CMD_UPDATE_DAC 0x02u #define AD5686_CMD_WRITE_UPDATE_DAC 0x03u #define AD5686_ADDR_DACA 0x00u #define AD5686_ADDR_DACB 0x01u #define AD5686_ADDR_DACC 0x02u #define AD5686_ADDR_DACD 0x03u #define AD5686_ADDR_ALL 0x0Fu typedef struct { void (*cs_low)(void); void (*cs_high)(void); void (*sclk_low)(void); void (*sclk_high)(void); void (*sdi_write)(uint8_t bit); uint8_t dac_bits; /* 12/14/16 */ uint16_t vref_mv; /* 2500 */ uint8_t gain_x2; /* GAIN 引脚电平 */ } ad5686_t; static void ad5686_frame(ad5686_t *dac, uint8_t cmd, uint8_t addr, uint16_t data) { uint32_t frame; /* 非 16 位芯片先把数据左对齐AD5684 左移 4AD5685 左移 2 */ if (dac-dac_bits 16) { data (16 - dac-dac_bits); } frame ((uint32_t)(cmd 0x07u) 20); frame | ((uint32_t)(addr 0x0Fu) 16); frame | (data 0xFFFFu); dac-cs_low(); for (int bit 23; bit 0; bit--) { dac-sclk_low(); dac-sdi_write((frame bit) 0x01u); dac-sclk_high(); } dac-cs_high(); }这段代码把命令、地址和数据拼进一个 32 位变量再按位从高到低送到 SDIN。data (16 - dac-dac_bits)这一步是关键写 AD5684 时如果应用层算出来的是 12 位代码 0x123底层发送前会变成 0x1230否则数据落在低 12 位不会被芯片识别。如果 MCU 有硬件 SPI可以把函数体内的循环替换成发送三个字节先发(frame 16) 0xFF再发(frame 8) 0xFF最后发frame 0xFF。注意每帧 24 位之间 SYNC 要拉高硬件 SPI 的 NSS 最好是 GPIO 控制不要用硬件自动片选。4.3 电压转 DAC 码和四通道输出函数应用层一般不会直接写寄存器而是给一个电压值。AD5686R 的输出范围由 VREF 和 GAIN 决定所以转换函数要把这两项都算进去。uint16_t ad5686_voltage_to_code(ad5686_t *dac, float voltage) { uint32_t max_code (1UL dac-dac_bits) - 1UL; float span dac-vref_mv / 1000.0f * (dac-gain_x2 ? 2.0f : 1.0f); if (voltage 0.0f) { return 0; } if (voltage span) { return (uint16_t)max_code; } return (uint16_t)((voltage / span) * (float)(max_code 1) 0.5f); } void ad5686_write_channel(ad5686_t *dac, uint8_t ch, uint16_t code) { ad5686_frame(dac, AD5686_CMD_WRITE_UPDATE_DAC, ch, code); } void ad5686_write_all(ad5686_t *dac, const uint16_t code[4]) { for (uint8_t ch 0; ch 4; ch) { ad5686_frame(dac, AD5686_CMD_WRITE_UPDATE_DAC, ch, code[ch]); } }电压转码时code[0]对应 DACAcode[3]对应 DACD。0.5f 是做四舍五入否则浮点截断会让每个码点都偏低一点。需要注意ad5686_write_all四条命令是分四条独立 SPI 帧发送的DAC 输出会依次更新不是严格同步。如果四个通道要同时输出不能直接调这个函数。同步更新的做法是先写四个输入寄存器再发一条“更新 DAC 寄存器全部”的命令。AD5686_CMD_WRITE_INPUT只把数据放进输入寄存器输出保持不动最后一条AD5686_CMD_UPDATE_DAC的地址设为 0xF四个通道在同一时刻锁存。void ad5686_write_all_sync(ad5686_t *dac, const uint16_t code[4]) { for (uint8_t ch 0; ch 4; ch) { ad5686_frame(dac, AD5686_CMD_WRITE_INPUT, ch, code[ch]); } ad5686_frame(dac, AD5686_CMD_UPDATE_DAC, AD5686_ADDR_ALL, 0); }这种方式常见于四路波形输出或电机偏置避免先更新的通道比后更新的通道早跳变。使用硬件 LDAC 也是类似原理保持 LDAC 为高先写输入寄存器最后统一拉低 LDAC。两种方法区别不大软同步不需要额外 GPIO我一般优先用软同步。驱动里还要做型号兼容。初始化时根据dac_bits设置 12、14 或 16vref_mv在没有内部基准的型号上改为外部基准电压gain_x2由 PCB 上 GAIN 引脚的接法决定。这样同一套函数可以直接编进 AD5684、AD5685、AD5686 的工程不需要为大作业重新写一版。5. 用逻辑分析仪和菊花链验证 AD5686R 驱动是否真兼容驱动写完后第一步不是接万用表量电压而是抓 SPI 帧。普通 24 位帧在逻辑分析仪上很容易看SYNC 拉低后第一个上升沿送出的应是命令位的高位。例如发一条ad5686_write_channel(0, 0x8000)解析器看到的帧应该等于0x038000也就是 DB23 为 0DB22:DB20 为 011DB19:DB16 为 0000数据为 0x8000。如果解析出来是0x304000说明数据被左移了一位问题出在 8 位 SPI 发送顺序上。多片 AD5686R 级联时驱动要点是菊花链的发送顺序。芯片的 SDO 接到下一片的 SDIN所有片的 SYNC 和 SCLK 并联。SDIN 数据会先进入第一片再随着后续时钟从 SDO 移到第二片所以先发送的数据对应链路上离 MCU 最远的那片即最后一帧先发。void ad5686_chain_write(ad5686_t *dac_chain[4], const uint16_t code[4]) { uint32_t frames[4]; for (int i 0; i 4; i) { frames[i] ((uint32_t)(AD5686_CMD_WRITE_UPDATE_DAC 0x07u) 20); frames[i] | ((uint32_t)(i 0x0Fu) 16); frames[i] | (code[3 - i] 0xFFFFu); } dac_chain[0]-cs_low(); for (int i 0; i 4; i) { for (int bit 23; bit 0; bit--) { dac_chain[0]-sclk_low(); dac_chain[0]-sdi_write((frames[i] bit) 0x01u); dac_chain[0]-sclk_high(); } } dac_chain[0]-cs_high(); }这里frames[0]放的是code[3]因为链路最后一片的寄存器最先收到数据。如果顺序反了四通道的输出会全部错位逻辑分析仪上却看不出单个帧错误。逐片确认输出时可以每次只写一片其他三片写 0这样能快速定位数据落在哪一片。验证兼容性时还可以利用 SDO 回读 DAC 寄存器。先把 R/W 位写成 1发送读回命令和地址再补一帧空时钟从 SDO 上读取 24 位数据。回读数据里需要按dac_bits右移才能得到实际代码。这个功能不强求但用来判断“驱动对不对”比万用表量电压更直接如果回读值和写入值不一致先查 SYNC 时序再查 SPI 模式。最后用万用表或示波器量四个 VOUT。给 AD5686R 写 0x8000输出应为 VREF/2写 0xFFFF输出应接近 VREF。如果输出整体偏大多半是 REF 电容没有生效如果某一通道数据错位优先检查菊花链顺序。把这些自检做完驱动移植的兼容性就算真正闭环了。本文还有配套的精品资源点击获取