基于STM32F103驱动AD9833模块 DDS信号发生器输出正弦波/三角波/方波可编程信号
AD9833是一款低功耗可编程波形发生器,能输出正弦波、三角波和方波,频率范围为1Hz-9MHz,采用SPI串行通信。具有28位频率寄存器和12位相位寄存器,可通过公式计算输出频率和相位偏移。输出阻抗200Ω,正弦波最大幅值600mVpp,方波5Vpp。使用时需注意输出信号带有直流分量,射频设备需加隔直器。本文将通过STM32F103的SPI接口控制AD9833,配合OLED屏和EC11编码器实现
文章目录
一、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/228≈0.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×106≈10737.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π/4096≈0.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通道为直接输出建议使用方波,不可独立调节。默认代码是点频信号,无法做调制。
更多推荐



所有评论(0)