一、AD9833模块简介

  AD9833是一款低功耗、可编程波形发生器,能够产生正弦波、三角波和方波输出。各种类型的检测、致动和时域反射(TDR)应用都需要波形发生器。 输出频率和相位可通过软件进行编程,调整简单。无需外部元件。 频率寄存器为28位;时钟速率为25MHz,可以实现0.1Hz的分辨率。 同样,时钟速率为1MHz时,AD9833可以实现0.004Hz的分辨率。
特性

  • 模块供电:DC 5V
  • 模块电流:10mA(MAX),正常驱动电流8mA(TYPE)
  • 通讯协议:SPI串行
  • 模块主频:25MHz(MAX)
  • DAC分辨率:10位
  • 相位累加器位数:28位
  • 输出通道:单通道输出,A通道直接输出,B通道带滤波器输出
  • 输出信号:正弦波、方波、三角波,正弦波带9MHz低通滤波器
  • 模块信号特点:正弦波无耦合输出,输出自带直流分量,接入射频设备需加隔直器,也可直接使用示波器测量
  • 最高主频输出信号范围:正弦波:1Hz ~ 9MHz,方波:1Hz ~ 1MHz(方波占空比不可调)
  • 输出幅值:正弦波600mVpp(MAX),方波5Vpp,正弦波随着频率增加幅度减少,方波随着频率增加波形变化
  • 输出阻抗:200欧

应用

  • 频率信号发生,正弦波、方波、三角波信号发生,传感器激励等

二、模块接口说明

FSYNC 低电平有效控制输入
SCLK 串行时钟输入
SDATA 串行数据输入

三、功能框图和时序图说明


  频率寄存器和相移寄存器:分别各两个,可以配置两个不同的频率和相移,可通过设置激活其中一个。

  12位地址截断:28位相位累加器,但输出被截断至12位。使用相位累加器的全分辨率不仅不切实际,也根本不必要,因为这要求查找表具有2^28个条目。只需具有足够的相位分辨率,使得因截断而产生的误差小于10位DAC的分辨率即可。这就要求SIN ROM的相位分辨率比10位DAC高出两位。

  MSB直接输出:通过将10位DAC的MSB以数字直接输出的形式到VOUT引脚,可以得到方波。也可以通过内置的2分频通道将输出频率降低到原来的1/2。

  控制寄存器:一个16位的控制寄存器


  AD9833通信流程:数据以一个16位字的形式,通过SPI传送到AD9833;通信开始前需将同步引脚拉低,且同步引脚拉低时时钟线SCLK应为高电平;数据可一次传输一个字,传输完成后将同步引脚拉高结束通信;也可一次传输多个字,传输时保持同步引脚为低电平,直至多个字传输完成后拉高。

四、主要寄存器说明

控制寄存器
  16 位寄存器,用于配置器件模式(如波形类型、复位、睡眠模式)。其中D1位选择输出正弦波还是三角波,D10-11位选择用哪个频率和相位寄存器,D12位选择写入频率寄存器的MSB或LSB。

频率寄存器
  AD9833 有两个独立的 28 位频率寄存器:FREQ0 和 FREQ1。用于精确控制输出波形的频率。它们存储“频率调谐字”(Frequency Tuning Word),该值决定了相位累加器的增量步长,从而生成所需的输出频率。两个寄存器的设计允许快速切换频率,例如在 FSK (频率键控移位) 调制应用中,通过控制寄存器的 FSELECT 位(D11)来选择使用哪个频率寄存器(0 表示 FREQ0,1 表示 FREQ1)。

  寄存器结构和位定义
    总位宽:每个频率寄存器均为 28 位,分成两个部分:MSB (高 14 位) 和 LSB (低 14 位)。
    位分配(表 7 和表 8所示):
    MSB 14 位:对应频率的高位部分,用于粗调。LSB 14 位:对应频率的低位部分,用于细调。

  寄存器地址(在串行写入时,前两位 D15:D14):
    FREQ0:01 (D15:D14 = 01)。FREQ1:10 (D15:D14 = 10)

写入流程
  写入频率寄存器通过 3 线串行接口(FSYNC、SCLK、SDATA)进行,每次传输 16 位字(包括 2 位地址 + 14 位数据)。由于寄存器是 28 位,需要分两次写入:

  控制寄存器设置:先写入控制寄存器(地址 00),设置 B28 位 (D13) = 1(表示启用 28 位连续写入模式),以及 HLB 位 (D12) 来选择写入 MSB 或 LSB(1 表示 MSB,0 表示 LSB)。同时设置 FSELECT 位选择目标寄存器(FREQ0 或 FREQ1)。

  数据写入:
   如果 B28=1:连续发送两个 16 位字(FSYNC 保持低电平)。
     第一笔:地址 + LSB 14 位数据(例如,对于 FREQ0:D15:D14=01,D13:D0=LSB 数据)。
     第二笔:地址 + MSB 14 位数据(相同地址,D13:D0=MSB 数据)。
   如果 B28=0:可单独写入 MSB 或 LSB(使用 HLB 位选择),但推荐使用连续发送模式以确保完整 28 位数据。SDATA 输入可参考表 9,10,11。

相位寄存器
  AD9833 有两个独立的 12 位相位寄存器:PHASE0 和 PHASE1。用于添加相位偏移到相位累加器的输出,从而控制波形的起始相位或实现相位调制(如 PSK,相位键控移位)。通过控制寄存器的 PSELECT 位(D10)选择使用哪个相位寄存器(0 表示 PHASE0,1 表示 PHASE1)。

  寄存器结构和位定义
    总位宽:每个相位寄存器均为 12 位(无 MSB/LSB 分割)。
    位分配(表 7 和表 12所示):
    12 位相位偏移字:对应相位的高精度调整。

  寄存器地址(D15:D14=11):PHASE0 和 PHASE1 共享地址 11,可通过控制寄存器的 PSELECT 位区分。

写入流程
  相位寄存器写入更简单,因为只有 12 位:

  控制寄存器设置:写入控制寄存器(地址 00),设置 PSELECT 位选择 PHASE0 或 PHASE1。B28 和 HLB 位不影响相位写入。

  数据写入:单笔 16 位字传输。
    SDATA = 11 (地址) + 相位 12 位数据,其中D13位选 PHASE0 还是 PHASE1,D12为无关位,D11-0为数据有效位。

  注意:上电或复位后,频率寄存器和相位寄存器默认为 0。

五、频率和相位计算

频率计算

  输出频率 f O U T f_{OUT} fOUT 的计算基于以下公式:
f O U T = FREQREG × f M C L K 2 28 f_{OUT} = \frac{\text{FREQREG} \times f_{MCLK}}{2^{28}} fOUT=228FREQREG×fMCLK
  FREQREG:28 位频率调谐字的十进制值(范围 0 ~ 2^{28} - 1 ≈ 268,435,455)。
   f M C L K f_{MCLK} fMCLK: 外部主时钟频率(最高 25 MHz)。
  分辨率:当 MCLK=25 MHz 时,最小频率步长 ≈ 0.093 Hz(即 25 × 10 6 / 2 28 ≈ 0.093 25 \times 10^6 / 2^{28} \approx 0.093 25×106/2280.093 Hz)。

示例:
  假设 MCLK=25 MHz,想要输出 1 kHz 正弦波。
  计算 FREQREG = ( 1000 × 2 28 ) / 25 × 10 6 ≈ 10737.418 (1000 \times 2^{28}) / 25 \times 10^6 \approx 10737.418 (1000×228)/25×10610737.418(十进制)。
  二进制(取整):0000 0000 0010 1001 1111 0001(28 位)。
  分成 MSB/LSB:MSB=0000 0000 0000 0000,LSB=0010 1001 1111 0001。
  写入:先设置控制寄存器 B28=1, HLB=0(LSB 先),然后连续写入两个 16 位字。

相位计算

  相位偏移 Δ ϕ \Delta \phi Δϕ 的计算基于以下公式:
Δ ϕ = PHASEREG × 2 π 2 12 = PHASEREG × 2 π 4096 \Delta \phi = \frac{\text{PHASEREG} \times 2\pi}{2^{12}} = \frac{\text{PHASEREG} \times 2\pi}{4096} Δϕ=212PHASEREG×2π=4096PHASEREG×2π

  PHASEREG:12 位相位字的十进制值(范围 0 ~ 4095)。
  分辨率:最小相位步长 = 2 π / 4096 ≈ 0.0015 2\pi / 4096 \approx 0.0015 2π/40960.0015 弧度(或约 0.088 度)。
  满量程:对应 2π 弧度(360 度)。

示例:
  想要 90 度相位偏移(π/2 弧度)。
  计算 PHASEREG = (90 / 360) * 4096 ≈ 1024(十进制)。
  二进制:0100 0000 0000(12 位)。
  写入:SDATA = 1100 0100 0000 0000

六、STM32F103驱动AD9833输出波形信号

准备工作

  STM32F103C8T6最小系统板,AD9833模块,OLED屏,EC11旋转编码器模块,按键和导线若干。

引脚接线

STM32F103C8T6 AD9833
PA3 FSYNC
PA4 SCLK
PA5 SDATA
PA0 EC11旋转编码器 -> B,减少频率
PA1 EC11旋转编码器 -> A,增加频率
PA2 EC11旋转编码器 -> S,移位调节
PB8 OLED -> SLC
PB9 OLED -> SDA
PB12 按键,切换输出波形

代码示例

AD9833.c

#include "AD9833.h"		// AD9833 definitions.
#include "delay.h"		// AD9833 definitions.

#define FCLK 25000000	//设置晶振频率
#define RealFreDat    268435456.0/FCLK//总的公式为 Fout=(Fclk/2的28次方)*28位寄存器的值

/***************************************************************************//**
 * @brief Initializes the SPI communication peripheral and resets the part.
 *
 * @return 1.
*******************************************************************************/
unsigned char AD9833_Init(void)
{
    AD9833_SPI_Init(0, 1000000, 1, 1);
    AD9833_SetRegisterValue(AD9833_REG_CMD | AD9833_RESET);
	
    return (1);
}

/***************************************************************************//**
 * @brief Sets the Reset bit of the AD9833.
 *
 * @return None.
*******************************************************************************/
void AD9833_Reset(void)
{
    AD9833_SetRegisterValue(AD9833_REG_CMD | AD9833_RESET);
	delay_ms(10);
}

/***************************************************************************//**
 * @brief Clears the Reset bit of the AD9833.
 *
 * @return None.
*******************************************************************************/
void AD9833_ClearReset(void)
{
	AD9833_SetRegisterValue(AD9833_REG_CMD);
}
/***************************************************************************//**
 * @brief Writes the value to a register.
 *
 * @param -  regValue - The value to write to the register.
 *
 * @return  None.    
*******************************************************************************/
void AD9833_SetRegisterValue(unsigned short regValue)
{
	unsigned char data[5] = {0x03, 0x00, 0x00};	
	
	data[1] = (unsigned char)((regValue & 0xFF00) >> 8);
	data[2] = (unsigned char)((regValue & 0x00FF) >> 0);
	ADI_CS_LOW;	    
	AD9833_SPI_Write(data,2);
	ADI_CS_HIGH;
}
void AD9833_SetFrequencyQuick(float fout,unsigned short type)
{

	//AD9833_Setup(AD9833_FSEL0, AD9833_PSEL0, type);
	AD9833_SetFrequency(AD9833_REG_FREQ0, fout,type);// 400 kHz

}

/***************************************************************************//**
 * @brief Writes to the frequency registers.
 *
 * @param -  reg - Frequence register to be written to.
 * @param -  val - The value to be written.
 *
 * @return  None.    
*******************************************************************************/
void AD9833_SetFrequency(unsigned short reg, float fout,unsigned short type)
{
	unsigned short freqHi = reg;
	unsigned short freqLo = reg;
	unsigned long val=RealFreDat*fout;
	freqHi |= (val & 0xFFFC000) >> 14 ;
	freqLo |= (val & 0x3FFF);
	AD9833_SetRegisterValue(AD9833_B28|type);
	AD9833_SetRegisterValue(freqLo);
	AD9833_SetRegisterValue(freqHi);
}
/***************************************************************************//**
 * @brief Writes to the phase registers.
 *
 * @param -  reg - Phase register to be written to.
 * @param -  val - The value to be written.
 *
 * @return  None.    
*******************************************************************************/
void AD9833_SetPhase(unsigned short reg, unsigned short val)
{
	unsigned short phase = reg;
	phase |= val;
	AD9833_SetRegisterValue(phase);
}
/***************************************************************************//**
 * @brief Selects the Frequency,Phase and Waveform type.
 *
 * @param -  freq  - Frequency register used.
 * @param -  phase - Phase register used.
 * @param -  type  - Type of waveform to be output.
 *
 * @return  None.    AD9833_Setup(1000,0,AD9833_OUT_SINUS);
*******************************************************************************/
void AD9833_Setup(unsigned short freq,
				  unsigned short phase,
			 	  unsigned short type)
{
	unsigned short val = 0;
	
	val = freq | phase | type;
	AD9833_SetRegisterValue(val);
}
/***************************************************************************//**
 * @brief Sets the type of waveform to be output.
 *
 * @param -  type - type of waveform to be output.
 *
 * @return  None.    
*******************************************************************************/
void AD9833_SetWave(unsigned short type)
{
	AD9833_SetRegisterValue(type);
}

main.c

#include "stm32_config.h"
#include "stdio.h"
#include "key.h"
#include "Encoder.h"
#include "OLED.h"
#include "ad9833.h"

uint8_t mode;
uint32_t last_value = 0xFFFFFFFF;
uint8_t start;
uint32_t value;

int main(void)
{
	MY_NVIC_PriorityGroup_Config(NVIC_PriorityGroup_2);	//设置中断分组
	delay_init(72);	//初始化延时函数
	
	Key_Init();
	OLED_Init();
	Encoder_Init();
	
	AD9833_Init();
	AD9833_SetFrequencyQuick(10000,AD9833_OUT_SINUS);
	
	delay_ms(100);
	
	OLED_ShowString(0,5,"AD9833",16,1);
	OLED_ShowChinese(70,5,0,16,1);
	OLED_ShowChinese(86,5,1,16,1);
	OLED_ShowChinese(102,5,2,16,1);
	OLED_ShowString(95,160,"Hz",16,1);
	OLED_Refresh();
	
	while(1)
	{
		KeyEvent_TypeDef key_event = Key_Scan();
		switch (key_event)
		{
			case KEY_MODE_CLICK:
				// Mode键短按事件
				mode++;
				if (mode > 2) mode = 0;
				switch(mode)
				{
					case 0: 
						OLED_ShowChinese(70,5,0,16,1);
						OLED_ShowChinese(86,5,1,16,1);
						OLED_ShowChinese(102,5,2,16,1);
						start = AD9833_OUT_SINUS;
						AD9833_SetFrequencyQuick(value, start);
						break;
					case 1: 
						OLED_ShowChinese(70,5,3,16,1);
						OLED_ShowChinese(86,5,4,16,1);
						OLED_ShowChinese(102,5,5,16,1);
						start = AD9833_OUT_TRIANGLE;
						AD9833_SetFrequencyQuick(value, start);
						break;
					case 2: 
						OLED_ShowChinese(70,5,6,16,1);
						OLED_ShowChinese(86,5,7,16,1);
						OLED_ShowString(102,5,"   ",16,1);
						start = AD9833_OUT_MSB;
						AD9833_SetFrequencyQuick(value, start);
						break;
				}
				break;
//			case KEY_MODE_LONG:
//				// Mode键长按事件
//				break;
//			case KEY_MODE_REPEAT:
//				// Mode键连发事件
//				break;
//			case KEY_GROUP_CLICK:
//				// Group键短按事件
//				break;
//			case KEY_GROUP_LONG:
//				// Group键长按事件
//				break;
//			case KEY_GROUP_REPEAT:
//				// Group键连发事件
//				break;
//			default:
//				break;
		}
		
		delay_ms(10);
		
		Encoder_Value_Update();
		Encoder_Display();
		
		value = Encoder_GetValue();
		
		if (value != last_value)
		{
			AD9833_SetFrequencyQuick(value, start);
			last_value = value;
			OLED_ShowNum(20, 160, value, 9, 16, 1);
			OLED_Refresh();
		}
		
		delay_ms(10);
		OLED_Refresh();
	}
}

效果展示


下图为输出方波信号,经过滤波后输出(蓝色)和直接输出(黄色)的比较

七、注意事项和常见问题

注意事项
  (1)模块为低功耗模块,供电电源不超过5.5V。
  (2)由于模块是高精度器件,为了避免不必要的干扰,建议使用线性电源供电。
  (3)输出信号建议使用SMA转BNC的线直接示波器观测效果,接触不良或劣质的线材可能导致信号衰减或者噪声过大。
  (4)如需简单测试模块功能,建议搭配本店控制板使用,先给DDS 模块供电,再给控制板供电即可产生波形,长按中间键切换功能。

常见问题
  Q:AD9833模块的2个输出口是什么关系,可以设置成不同的频率输出吗?
  A:首先AD9833芯片只有一个输出通道。模块的A通道为芯片直接输出,B通道为A通道经滤波后输出。所以模块的2个输出口不能设置不同的频率输出。

  Q:模快的主频是多少?输出幅度可以调节吗?
  A:模块的主频是输入时钟决定的,板载默认时钟为25MHz,即默认主频为25MHz。输出幅度是固定的,没有办法程控。

  Q:模块可以实现扫频么?方波占空比可调吗?
  A:模块可以实现扫频,本店提供的代码可支持扫频。模块方波占空比不可以调节,边沿时间为ns级。

  Q:通道A和B可以独立调节吗?可以做调制吗?
  A:两个通道固定相差180度,B通道有滤波器,建议使用正弦波和三角波,A通道为直接输出建议使用方波,不可独立调节。默认代码是点频信号,无法做调制。

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐