☰
AD9834 DDS芯片实战:SPI驱动、寄存器配置与调试避坑指南
2026/9/26 14:41:16 网站建设 项目流程

简介:这份资源围绕AD9834芯片展开,面向电子设计初学者、单片机开发者及需要波形发生器方案的工程师,解决从芯片管脚认知到程序落地的完整链路问题。包内共43个文件,以c、h源码与ewp、ewd等IAR工程文件为主,辅以pdf、docx中文资料、bat脚本及dni、pbd等调试配置,压缩包约3.6MB,覆盖AD9834产生正弦波、三角波与方波的按键加液晶控制例程。资源明确给出FSYNC、SCLK、SDATA与LCD12864、按键的管脚映射,并附write_AD9834命令字说明,如0x2000、0x2038产生正弦波,0x2002为三角波,0x2028对应方波与正弦波输出,最高可稳定输出56MHz正弦波。已有884人学习下载,适合需要快速搭建波形发生器、理解SPI时序与寄存器配置的读者参考。

1. AD9834 到底能干什么:从一块 DDS 芯片到可复现的信号源

如果你手头有一块 AD9834 模块,却只把它当成“能出正弦波的芯片”,那大概率会低估它。AD9834 是一颗低功耗 DDS(直接数字频率合成)芯片,核心能力是用数字方式合成正弦波、三角波和方波,频率分辨率可以做到 0.1 Hz 级别,输出频率上限在 37.5 MHz 左右(具体取决于主时钟和供电)。它常出现在函数信号发生器、阻抗测量、锁相环参考、超声激励这些场景里。很多人搜“AD9834 管脚”和“AD9834 开发例程”,本质上是想把它接到 MCU 上,用几根线控制频率和相位,然后稳定输出。这篇笔记就按这个目标走:先把管脚和寄存器讲清楚,再给出一套能直接抄的 SPI 驱动和验证方法,最后说清楚调试时最容易翻车的地方。适合已经会写 GPIO 和 SPI、但没碰过 DDS 的嵌入式工程师,也适合想用 AD9834 替代传统模拟振荡器的硬件开发者。

2. AD9834 管脚与寄存器:先搞懂它怎么被控制

2.1 关键管脚定义与最小系统连接

AD9834 常见封装是 TSSOP-20,实际模块上通常已经引出必要引脚。要让它工作,至少需要关注这些管脚:

管脚名类型作用最小系统接法
MCLK输入主时钟,最高 75 MHz接有源晶振,常用 25 MHz
FSYNC输入SPI 片选,低有效接 MCU GPIO
SCLK输入SPI 时钟接 MCU SCK
SDATA输入SPI 数据接 MCU MOSI
FSELECT输入频率寄存器选择接 GPIO 或固定电平
PSELECT输入相位寄存器选择接 GPIO 或固定电平
RESET输入复位,高有效接 GPIO,初始化时拉高再拉低
FS ADJUST输入满量程电流调节接电阻到地,典型 6.8 kΩ
IOUT / IOUTB输出互补电流输出接运放转电压
AVDD / DVDD电源模拟/数字供电2.3 V ~ 5.5 V,加去耦

很多人第一次画板子会把 FSELECT 和 PSELECT 直接接地,结果只能用一个频率寄存器,切换频率时必须重新写寄存器,响应变慢。如果应用需要 FSK 或 PSK 调制,这两个脚必须接到 MCU 的 GPIO 上。另外,FS ADJUST 的电阻决定输出电流幅值,公式是 ( I_{FS} = 18 \times V_{REF} / R_{FS} ),典型值下约 3 mA,后面接 200 Ω 负载电阻可以得到约 0.6 V 峰峰值。这个参数直接决定你后面运放电路的增益怎么设。

2.2 寄存器映射与写入顺序

AD9834 内部有 5 个主要寄存器:控制寄存器、频率寄存器 0、频率寄存器 1、相位寄存器 0、相位寄存器 1。每次写数据都是 16 位,通过 FSYNC 拉低开始,SCLK 上升沿采样 SDATA。控制寄存器的高 4 位是功能位,低 12 位保留。关键位定义如下:

  • D13:B28,1 表示 28 位频率写入模式,0 表示分两次写 14 位
  • D11:RESET,1 复位内部寄存器
  • D10:SLEEP1,1 关闭内部时钟
  • D9:SLEEP12,1 关闭 DAC
  • D8:OPBITEN,1 使能方波输出
  • D7:SIGN/PIB,方波时选择 MSB 或比较器输出
  • D6:DIV2,方波分频
  • D5:MODE,1 三角波,0 正弦波
  • D3:FSELECT,频率寄存器选择
  • D2:PSELECT,相位寄存器选择
  • D1:复位,与 D11 配合

初始化顺序建议固定为:先拉高 RESET 位,写控制寄存器;然后写频率寄存器 0 的低 14 位和高 14 位(如果 B28=1 则写两次 16 位,实际有效 28 位);再写相位寄存器;最后清 RESET 位,让芯片开始输出。这个顺序如果打乱,常见现象是输出频率不对或者干脆没有波形。

2.3 用 SPI 写一个频率值的完整代码

下面这段代码基于 STM32 HAL 库,其他平台改一下 SPI 发送函数即可。假设 FSYNC 接 PA4,SCLK 接 PA5,SDATA 接 PA7。

#include "stm32f1xx_hal.h" #define FSYNC_LOW() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET) #define FSYNC_HIGH() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET) extern SPI_HandleTypeDef hspi1; // 写 16 位到 AD9834 void AD9834_Write16(uint16_t data) { uint8_t buf[2]; buf[0] = (data >> 8) & 0xFF; buf[1] = data & 0xFF; FSYNC_LOW(); HAL_SPI_Transmit(&hspi1, buf, 2, 100); FSYNC_HIGH(); } // 设置频率,freq 单位 Hz,mclk 单位 Hz void AD9834_SetFrequency(uint32_t freq, uint32_t mclk) { uint32_t reg = (uint32_t)((double)freq * 268435456.0 / (double)mclk); // 2^28 uint16_t lsb = reg & 0x3FFF; uint16_t msb = (reg >> 14) & 0x3FFF; // 写频率寄存器 0 AD9834_Write16(0x4000 | lsb); // D14=0 表示频率寄存器0,D15=0 表示连续写 AD9834_Write16(0x4000 | msb); // 控制寄存器:B28=1,RESET=0,正弦输出 AD9834_Write16(0x2000); // B28=1,其他位默认 }

逻辑说明:AD9834_Write16先发高字节再发低字节,因为 AD9834 是 MSB 优先。频率计算用 ( 2^{28} \times f_{out} / f_{mclk} ),取整后拆成两个 14 位。控制寄存器写0x2000表示 B28=1,即 28 位频率模式,同时 RESET 位为 0,芯片开始输出。参数说明:mclk必须和实际晶振一致,否则频率会按比例偏。如果晶振是 25 MHz,要输出 1 kHz,reg约为 10737,拆成 lsb 和 msb 后写入即可。注意每次改频率前不需要重新复位,直接写频率寄存器再写一次控制寄存器即可。

3. 从零跑通 AD9834 开发例程:硬件连接与波形验证

3.1 最小验证电路与供电检查

在写代码之前,先确认硬件没有硬伤。AD9834 的模拟电源和数字电源可以共用 3.3 V,但建议在 AVDD 和 DVDD 附近各放 100 nF 和 10 μF 去耦电容。MCLK 输入必须是有源晶振,不能直接接 MCU 的 PWM,因为抖动太大会导致输出频谱变差。IOUT 和 IOUTB 是电流输出,必须接运放做 I/V 转换,典型电路是用一个 200 Ω 电阻从 IOUT 到地,再通过电压跟随器输出。如果你买的是模块,通常已经带了运放,直接测模块输出即可。

上电后先别急着写程序,用示波器测 MCLK 脚,应该有稳定的方波,幅度在 0~3.3 V 或 0~5 V。如果 MCLK 没有波形,后面所有操作都是白费。然后测 FS ADJUST 脚电压,应该在 1.2 V 左右,如果为 0 或电源电压,检查电阻是否焊错或虚焊。这两步能排除大部分“芯片没反应”的问题。

3.2 初始化序列与第一个正弦波

完整的初始化流程如下:

  1. 拉高 RESET 位:写控制寄存器0x2100(B28=1,RESET=1)
  2. 写频率寄存器 0 的低 14 位和高 14 位
  3. 写相位寄存器 0,通常写 0 即可
  4. 清 RESET 位:写控制寄存器0x2000
  5. 延时 10 ms 等待输出稳定

用上面的AD9834_SetFrequency函数,调用AD9834_SetFrequency(1000, 25000000)就应该在输出端看到 1 kHz 正弦波。如果示波器看到的是方波,检查控制寄存器的 MODE 位(D5)是否为 0。如果看到的是三角波,说明 MODE 位被置 1 了。如果频率是设定值的两倍或一半,检查 MCLK 实际频率和代码里的mclk参数是否一致。

3.3 用串口打印寄存器回读结果

AD9834 不支持回读寄存器,但你可以通过 MCU 记录每次写入的值,用串口打印出来核对。下面这段代码把频率计算过程和写入值打印出来,方便对比。

#include <stdio.h> void AD9834_SetFrequencyDebug(uint32_t freq, uint32_t mclk) { uint32_t reg = (uint32_t)((double)freq * 268435456.0 / (double)mclk); uint16_t lsb = reg & 0x3FFF; uint16_t msb = (reg >> 14) & 0x3FFF; printf("freq=%lu, reg=%lu, lsb=0x%04X, msb=0x%04X\n", freq, reg, lsb, msb); AD9834_Write16(0x4000 | lsb); AD9834_Write16(0x4000 | msb); AD9834_Write16(0x2000); }

逻辑说明:printf输出实际计算的 28 位频率字和拆分后的两个 14 位值。参数说明:freq和mclk都用 Hz 为单位,避免单位混乱。如果你用 25 MHz 晶振输出 1 kHz,串口应该打印reg=10737,lsb=0x29F1,msb=0x0000。如果打印出来的reg是 0 或者异常大,检查double运算是否被编译器优化成整数除法。有些嵌入式环境默认不支持浮点打印,需要开启-u_printf_float链接选项。

4. AD9834 调试避坑:从现象到根因的排查清单

4.1 输出频率始终为 MCLK 的整数分之一

现象:设定 1 kHz,实际输出 25 kHz 或 12.5 kHz,正好是 MCLK 的整数分频。原因:频率寄存器没有正确写入,芯片可能停留在复位状态或者 B28 位没有置 1,导致只写了 14 位而高 14 位为 0。解决:用逻辑分析仪抓 FSYNC、SCLK、SDATA 三根线,确认每次写 16 位,并且控制寄存器最后写入的是0x2000而不是0x2100。另外检查 FSELECT 脚电平,如果为高,芯片会选频率寄存器 1,而你只写了寄存器 0。

4.2 波形幅度极小或几乎看不到

现象:示波器调到 1 mV/div 才勉强看到波形。原因:IOUT 是电流输出,没有接负载电阻或运放,直接接示波器高阻探头相当于开路。解决:在 IOUT 和地之间接一个 200 Ω 电阻,再用示波器测电阻两端。如果模块自带运放,检查运放供电是否正常,反馈电阻是否焊错。另外 FS ADJUST 电阻如果太大,满量程电流会很小,输出幅度也会低。

4.3 SPI 写入后芯片发热或电流异常

现象:AD9834 摸起来烫,电源电流超过 30 mA。原因:SDATA 或 SCLK 接错,或者 FSYNC 没有拉低导致芯片一直处于接收状态但时钟乱跳。解决:断电检查连线,确保 FSYNC 默认高电平,SCLK 空闲为低。上电后用万用表测 DVDD 和 AVDD 对地电阻,正常应该在几十 kΩ 以上。如果电阻很小,芯片可能已经损坏。

4.4 方波输出频率只有设定值的一半

现象:切到方波模式后,输出频率是正弦波频率的一半。原因:控制寄存器的 DIV2 位(D6)被置 1,方波经过二分频。解决:写控制寄存器时把 D6 清零。如果你用0x2000作为基础值,它本身 D6=0,但如果你之前写过0x2020之类的值,就会带上分频。建议每次切换波形都重新写完整的控制字,不要只改某一位。

4.5 多片 AD9834 共用 SPI 时互相干扰

现象:单独测试每片都正常,接在一起后频率乱跳。原因:FSYNC 没有独立控制,或者 SCLK 走线太长导致反射。解决:每片 AD9834 的 FSYNC 必须接独立 GPIO,SPI 时钟频率降到 1 MHz 以下,走线尽量短。如果必须共用 SCLK 和 SDATA,确保同一时刻只有一片的 FSYNC 为低。可以在 FSYNC 上拉 10 kΩ 电阻到 DVDD,防止悬空误触发。

5. 进阶技巧:用 AD9834 做频率扫描与相位调制

5.1 频率扫描的定时器实现

频率扫描在阻抗测量和超声激励里很常见。思路是用一个定时器每 1 ms 触发一次,在中断里更新频率寄存器。下面是一个基于 STM32 TIM2 的示例。

uint32_t sweep_freq = 1000; uint32_t sweep_step = 10; void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM2) { AD9834_SetFrequency(sweep_freq, 25000000); sweep_freq += sweep_step; if (sweep_freq > 100000) { sweep_freq = 1000; } } }

逻辑说明:每 1 ms 更新一次频率,从 1 kHz 扫到 100 kHz 再回卷。参数说明:sweep_step决定扫描速度,10 Hz/ms 意味着 1 秒扫 10 kHz。注意AD9834_SetFrequency内部有浮点运算,在中断里执行时间较长,如果 MCU 主频较低,建议预先算好频率字表,中断里只做查表和写 SPI。

5.2 相位调制与 PSK 的接线要点

AD9834 支持两路相位寄存器,通过 PSELECT 脚切换。要做 BPSK,可以把两个相位寄存器分别设为 0 和 180 度对应的值。相位寄存器是 12 位,对应 0~2π,所以 180 度对应 2048。初始化时写相位寄存器 0 为 0,相位寄存器 1 为 2048,然后通过 GPIO 翻转 PSELECT 脚即可切换相位。注意 PSELECT 切换后输出会有短暂毛刺,如果后级对毛刺敏感,需要在运放后加低通滤波。

5.3 用 Excel 或 Python 预计算频率字

如果你要频繁切换频率,每次都在 MCU 里做浮点乘法很浪费。可以用 Python 预计算一个频率字表,直接以数组形式放在代码里。

mclk = 25000000 freqs = [1000, 2000, 5000, 10000, 20000, 50000] for f in freqs: reg = int(f * (1 << 28) / mclk) lsb = reg & 0x3FFF msb = (reg >> 14) & 0x3FFF print(f"{f} Hz: lsb=0x{lsb:04X}, msb=0x{msb:04X}")

逻辑说明:1 << 28就是 268435456,避免手写长数字出错。参数说明:mclk改成你实际使用的晶振频率。输出结果可以直接复制到 C 数组里,MCU 端只做查表和 SPI 写入,速度最快。这个习惯我一直在用,尤其是做多频点切换的时候,能省掉很多现场调试时间。

5.4 验证输出频谱的简易方法

没有频谱仪的情况下,可以用示波器的 FFT 功能看谐波。AD9834 正弦输出的谐波主要来自 DAC 量化,典型 SFDR 在 60 dBc 左右。如果 FFT 上看到大量杂散,先检查 MCLK 是否干净,再检查电源去耦。我一般会在 IOUT 后加一个截止频率略高于输出频率的 LC 低通,能明显改善高频段的频谱。另外,输出幅度不要设得太接近满量程,留 3 dB 余量可以降低失真。

希望帮到你。

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

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

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

立即咨询