简介:这份资源围绕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 初始化序列与第一个正弦波
完整的初始化流程如下:
- 拉高 RESET 位:写控制寄存器
0x2100(B28=1,RESET=1) - 写频率寄存器 0 的低 14 位和高 14 位
- 写相位寄存器 0,通常写 0 即可
- 清 RESET 位:写控制寄存器
0x2000 - 延时 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 余量可以降低失真。
希望帮到你。
本文还有配套的精品资源,点击获取