一、AD9851模块简介
AD9851是一款高度集成的器件,采用先进的DDS技术,再加上内部高速,高性能的D/A转换器和比较器,形成一个数字可编程频率合成器和时钟发生器功能。当参考准确的时钟源时,AD9851产生稳定的频率并进行相位可编程的数字化模拟输出正弦波。该正弦波可以直接用作频率源,或内部转换为方波敏捷时钟生成器应用程序。 AD9851的创新高速DDS内核接受32位频率调谐字,导致输出调谐分辨率约为系统时钟为180MHz时为0.04Hz。AD9851包含独特的 6XREFCLK乘法器电路消除了需要一个高速参考振荡器。 6XREFCLK乘法器对SFDR和相位噪声特性的影响最小。 特性
- 通讯协议:SPI串行/并行
- 180MHz时钟频率,可选6位参考时钟
- DAC分辨率:10位
- 相位累加器:32位
- 输出信号:正弦波,方波。正弦波带65MHz低通滤波器,方波耦合输出
- 输出通道:2通道差分。相位差180度,高频输出由于有滤波器,相位会偏移
- 信号特点:正弦波无耦合输出。输出带自带直流分量,接入射频设备请加隔直器也可直接使用示波器测量
- 输出正弦波/方波最高主频:1Hz-65M/30MHz,方波10MHz以上需要示波器输入阻抗为50欧,方波占空比可调
- 输出幅度:正弦波1000mVpp,方波5Vpp,正弦波随频率增加幅度减小,方波随着频率增加波形变化
- 输出阻抗:75欧
二、AD9851引脚说明
| D0-D7 | 8 位数据输入。用于加载 32 位频率字和 8 位相位/控制字的数据端口。D7 = MSB,D0 = LSB。D7也用作 40 位串行数据字的输入引脚。 |
|---|---|
| W_CLK | 串行时钟输入。上升沿将并行或串行频率/相位/控制字异步加载到 40 位输入寄存器中。 |
| FQ_UD | 频率更新。上升沿异步传输 40 位输入寄存器的内容,以供 DDS 内核处理。当已知输入寄存器的内容仅包含有效且允许的数据时,应发出 FQ_UD。 |
| RSET | 主复位引脚,高电平有效。 |
| OUT | DAC差分输出 |
三、功能框图和时序图分析
时序简单分析:
- 复位 RESET 拉高 → 相位累加器清零,默认进入 并行模式,PLL 关闭。
- 并行方式 每次通过 W0–W4 装载 40 位数据(8 位一组),W0 含控制字与相位字,W1–W4 为频率调谐字。 WCLK 上升沿锁存数据,最后 FQ_UD 上升沿触发更新。
- 串行方式 首先在并行方式下写入特殊码 xxxxx011 → 切换至串行。 串行移入 40 位数据,LSB 先行,最后 FQ_UD 上升沿更新。
- 延迟 频率更新延迟:约 18 个 SYSCLK 周期。 相位更新延迟:约 13 个 SYSCLK 周期。
四、并行/串行装载 40位控制字分配
并行装载:W0 是“控制 + 相位”字;W1~W4 是 32 位频率调谐字(FTW,W1 放 MSB)。其中W0位定义为:
D7…D3:Phase[4:0](D7 是相位 MSB,D3 是相位 LSB,步进 11.25°)
D2:低功耗
D1:逻辑 0(仅在“请求进入串行模式”时临时使用;进入后必须再写回 0)
D0:6× REFCLK 倍频使能
串行装载:40-bit 一次性移入,先 32 位频率,再控制,再相位。
进入串行模式需要留意的地方:
上电复位后默认并行模式。在并行端口写入 W0 时,把 D1 置 1(仅用于“请求串行”),D0 按你是否要启用 ×6 设置;随后按手册 Figure 17 的时序切换到串行移位口。进入后务必再把并口 W0 的 D1 写回 0。串行移位是按位 LSB-first;若用 MCU 的 SPI(通常 MSB-first),要么“反相/反序”配置 SPI,要么对每个字节做逐字节位反转再发。
五、STM32F103驱动AD9851
准备工作
STM32F103ZET6开发板、AD9851模块、OLED显示屏、EC11旋转编码器。
接线说明
| STM32F103ZET6 | AD9851模块 |
|---|---|
| PA3 | FQ |
| PA4 | CLK |
| PA6 | REST |
| PC0 | D0 |
| PC1 | D1 |
| PC2 | D2 |
| PC3 | D3 |
| PC4 | D4 |
| PC5 | D5 |
| PC6 | D6 |
| PC7 | D7 |
| PA0 | 旋转编码器-A ,用于调节输出频率 |
| PA1 | 旋转编码器-B ,用于调节输出频率 |
| PA2 | 旋转编码器-S,移位调节 |
| PB8 | OLED-SCL |
| PB9 | OLED-SDA |
| 5V | 5V |
| GND | 共地 |
其中,当使用串行驱动时,DO、D1、D2脚需输入(110)固定电平,D3-6可以悬空不接,D7用作40位串行数据输入引脚。
示例代码
AD9851.c
//***************************************************//
//函数1: ad9851_reset() //
//函数2: ad9851_reset_serial() //
//函数3: ad9851_wr_parrel(unsigned char w0,double frequence)//
//函数4: ad9851_wr_serial(unsigned char w0,double frequence)//
//***************************************************//
// 子程序说明 //
//***************************************************//
//函数1: ad9851_reset()
// 复位ad9851,之后为并口写入模式
//函数2: ad9851_reset_serial()
// 复位ad9851,之后为串口写入模式
//函数3: ad9851_wr_parrel(unsigned char w0,double frequence)
// 并口写ad9851数据,w0为ad9851中w0的数据,frequence
// 为写入的频率
//函数4: ad9851_wr_serial(unsigned char w0,double frequence)
// 串口写ad9851数据,w0为ad9851中w0的数据,frequence
// 为写入的频率
//需定义的位:
//ad9851_w_clk ;
//ad9851_fq_up ;
//ad9851_rest ;
//ad9851_bit_data ;
//例:
//sbit ad9851_w_clk =P2^2;
//sbit ad9851_fq_up =P2^1;
//sbit ad9851_rest =P2^0;
//sbit ad9851_bit_data =P1^7;
//***************************************************//
// 写数据说明 //
//***************************************************//
//写数据例:
// ad9851_reset()
// wr_lcd02_data(unsigned char x)
// ad9851_wr_parrel(0x01,1000)
// ad9851_wr_serial(0x01,1000)
//***************************************************//
//---------------------------------------------------//
// 程序 //
//---------------------------------------------------//
# include <AD9851.h>
# include <stdio.h>
u8 AD9851_FD=0x00; //倍频数
void (*_AD9851_Setfq)(u8 w0,double frequence);
//P1为8位数据口
//***************************************************//
// ad9851复位(并口模式) //
//---------------------------------------------------//
void ad9851_reset()
{
ad9851_w_clk=0;
ad9851_fq_up=0;
//rest信号
ad9851_rest=0;
ad9851_rest=1;
ad9851_rest=0;
}
//***************************************************//
// ad9851复位(口模式) //
//---------------------------------------------------//
void ad9851_reset_serial()
{
ad9851_w_clk=0;
ad9851_fq_up=0;
//rest信号
ad9851_rest=0;
ad9851_rest=1;
ad9851_rest=0;
//w_clk信号
ad9851_w_clk=0;
ad9851_w_clk=1;
ad9851_w_clk=0;
//fq_up信号
ad9851_fq_up=0;
ad9851_fq_up=1;
ad9851_fq_up=0;
}
//***************************************************//
// 向ad9851中写命令与数据(并口) //
//---------------------------------------------------//
void ad9851_wr_parrel(u8 w0,double frequence)
{
u32 w;
long int y;
double x;
//计算频率的HEX值
x=4294967295/180;//适合180M晶振/180为最终时钟频率(或30M六倍频)
//如果时钟频率不为180MHZ,修改该处的频率值,单位MHz !!!
frequence=frequence/1000000;
frequence=frequence*x;
y=frequence;
//写w0数据
w=w0;
AD9851_DataBus=w|(w^0xff)<<16; //w0
ad9851_w_clk=1;
ad9851_w_clk=0;
//写w1数据
w=(y>>24);
AD9851_DataBus=w|(w^0xff)<<16; //w0
ad9851_w_clk=1;
ad9851_w_clk=0;
//写w2数据
w=(y>>16);
AD9851_DataBus=w|(w^0xff)<<16; //w0
ad9851_w_clk=1;
ad9851_w_clk=0;
//写w3数据
w=(y>>8);
AD9851_DataBus=w|(w^0xff)<<16; //w0
ad9851_w_clk=1;
ad9851_w_clk=0;
//写w4数据
w=(y>>=0);
AD9851_DataBus=w|(w^0xff)<<16; //w0
ad9851_w_clk=1;
ad9851_w_clk=0;
//移入始能
ad9851_fq_up=1;
ad9851_fq_up=0;
}
//***************************************************//
// 向ad9851中写命令与数据(串口) //
//---------------------------------------------------//
void ad9851_wr_serial(u8 w0,double frequence)
{
unsigned char i,w;
long int y;
double x;
//计算频率的HEX值
x=4294967295/125;//适合180M晶振/180为最终时钟频率(或30M六倍频)
//如果时钟频率不为180MHZ,修改该处的频率值,单位MHz !!!
frequence=frequence/1000000;
frequence=frequence*x;
y=frequence;
//写w4数据
w=(y>>=0);
for(i=0;i<8;i++)
{
ad9851_bit_data=(w>>i)&0x01;
ad9851_w_clk=1;
ad9851_w_clk=0;
}
//写w3数据
w=(y>>8);
for(i=0;i<8;i++)
{
ad9851_bit_data=(w>>i)&0x01;
ad9851_w_clk=1;
ad9851_w_clk=0;
}
//写w2数据
w=(y>>16);
for(i=0;i<8;i++)
{
ad9851_bit_data=(w>>i)&0x01;
ad9851_w_clk=1;
ad9851_w_clk=0;
}
//写w1数据
w=(y>>24);
for(i=0;i<8;i++)
{
ad9851_bit_data=(w>>i)&0x01;
ad9851_w_clk=1;
ad9851_w_clk=0;
}
//写w0数据
w=w0;
for(i=0;i<8;i++)
{
ad9851_bit_data=(w>>i)&0x01;
ad9851_w_clk=1;
ad9851_w_clk=0;
}
//移入始能
ad9851_fq_up=1;
ad9851_fq_up=0;
}
void AD9851_IO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure ;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC, ENABLE); //使能PB,PE端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0| GPIO_Pin_1| GPIO_Pin_2| GPIO_Pin_3| GPIO_Pin_4| GPIO_Pin_5| GPIO_Pin_6| GPIO_Pin_7 ;
GPIO_Init(GPIOC ,&GPIO_InitStructure) ;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4| GPIO_Pin_6;
GPIO_Init(GPIOA ,&GPIO_InitStructure) ;
}
void AD9851_Setfq(double fq)
{
if(ad9851_ad9850)
{
fq *= 1.44;
AD9851_FD = 0;
}
_AD9851_Setfq(AD9851_FD,fq);
}
//***************************************************//
// 测试程序1000Hz //
//---------------------------------------------------//
//输入:mode ad9851_parallel 并口 ad9851_serial 串口
//FD:0:不倍频 1:2倍频
void AD9851_Init(u8 mode,u8 FD)
{
AD9851_IO_Init();
AD9851_DataBus=0x00|(~0x00)<<16;
// ad9851_reset_serial();
// ad9851_wr_serial(0x00,1000000);
if(mode==ad9851_parallel){_AD9851_Setfq=ad9851_wr_parrel;ad9851_reset();}
else {_AD9851_Setfq=ad9851_wr_serial;ad9851_reset_serial();}
if(FD==1)AD9851_FD=0x01;
// AD9851_Setfq(1000000);
}
main.c
#include "stm32_config.h"
#include "stdio.h"
#include "Encoder.h"
#include "OLED.h"
#include "ad9851.h"
u32 SweepMinFre = 1000;
u32 SweepMaxFre = 1000000;
void SweepFre(void)
{
if(SweepMinFre > SweepMaxFre) SweepMinFre = 1000;
SweepMinFre += SweepMinFre;
AD9851_Setfq(SweepMinFre);
}
int main(void)
{
MY_NVIC_PriorityGroup_Config(NVIC_PriorityGroup_2); //设置中断分组
delay_init(72); //初始化延时函数
OLED_Init();
Encoder_Init();
delay_ms(300);
AD9851_Init(ad9851_parallel, 1);
AD9851_Setfq(1000);
// ad9851_wr_serial(0,1000);
// delay_ms(100);
OLED_ShowString(40,5,"AD9851",16,1);
OLED_ShowString(95,160,"Hz",16,1);
OLED_Refresh();
while(1)
{
Encoder_Value_Update();
Encoder_Display();
uint32_t value = Encoder_GetValue();
if(value > 65000000) value = 1000;
AD9851_Setfq(value);
OLED_ShowNum(20, 160, value, 9, 16, 1);
OLED_Refresh();
delay_ms(10);
// SweepFre();
// delay_ms(200);
}
}
效果展示
注意事项和常见问题
注意事项
(1)模块为低功耗模块,供电电源不超过5.2V。
(2)由于模块是高精度器件,为了避免不必要的干扰,建议使用线性电源供电。
(3)输出信号建议使用SMA转BNC的线直接示波器观测效果,接触不良或劣质的线材可能导致信号衰减或者噪声过大。
(4)配送的代码仅为配套主控板使用,不提供单片机教程,额外功能需要自行开发。
(5)如需简单测试模块功能,建议搭配本店控制板使用,先给DDS 模块供电,再给控制板供电即可产生波形,长按中间键切换功能。
常见问题
Q:AD9851模块的2个输出口是什么关系,可以设置成不同的频率输出吗?
A:AD9851模块的2个输出口不能设置成不同的频率输出。OUT1与 OUT1相位固定相差180°。
Q:模快的主频是多少?输出幅度可以调?
A:模块的主频是输入时钟和程序一起决定的,板载默认时钟为30MHz,程序控制倍频为6倍,即默认主频为180MHz。
输出幅度是固定的,没有办法程控。
Q:模块可以实现扫频么?方波占空比可调吗?
A:模块可以实现扫频。本店提供的代码可支持扫频。模块方波占空比可以调节。
Q:两通道可以独立调节吗?可以做调制吗?
A:两个通道固定相差180度,频率高了由于滤波器作用可能相移,不可独立调节。默认代码是点频信号,其他模式需要自行编程。