基于AD9851的信号发生器(带程序)
基于AD9851的信号发生器
最近几年的电子
设计
领导形象设计圆作业设计ao工艺污水处理厂设计附属工程施工组织设计清扫机器人结构设计
大赛,差不多每年都考了DDS的设计。本人曾经也调试过
DDS,但走了一些弯路,在这里写下一些心得,希望能对初调者有点帮助。下面
将有电路图,以及详细的代码。
学过FPGA的同仁们,应该对DDS的原理就会有很好的理解了。用FPGA是很容易把一个简单型的ad8952烧出来的!其实原理相当的简单,无非就是由相位累
加器,相位调制器,正弦查找ROM,DAC构成。通过改变相位累加的增量就很容
易的改变的输出的频率了。
如果相位累加器(频率控制子)的位宽为N位(ad9851的N=32位)这就意味着把一个周期的正弦波形离散成了2的N次方个点,把这些点的幅值存在一个
ROM中就构成了正弦查找ROM。如果系统时钟为Fclk,即把Fclk分成了2的N次方份。如果此时的相位累加增量为(频率控制字)为B,那么此时的输出频率应为Fout=(B*Fclk)/ N。显然B=1时其最小值为Fclk/ N。B的值也不能太大,否则会输出失真波形。Fout的最大值理论上应该至少小于Fclk/4。所以要想提高输出频率的最大值,就得靠提高系统的外部时钟Fclk。
下面结合本人的代码来具体讲讲AD9851的应用。
AD9851的一些具体介绍这里就不说了。其DATASHEET上都说的很清楚。若看不懂E文,可以发E_M给我,我有中文资料。AD9851要写40位的控制子。其中前面32位就是频率控制子了。后面是有1个6倍频使能位,1个logic0位,1个POWER_DOWN位,还有5位相位模式字。这里我们只解决频率控制问
题
快递公司问题件快递公司问题件货款处理关于圆的周长面积重点题型关于解方程组的题及答案关于南海问题
。也
就是说在代码中本人只写了,32位的频率控制子,还有一个6倍频使能。其余的剩下几位由于没用到也就默认写为0了。AD9851可以用并和串俩中方式写入
控制子。
其串行发送方式的控制子表如下:
本人主要将用串写控制子的时序与代码。其并的方式的代码也会给出。硬件
电路图网上有很多资源,自己去找找!
主要看看代码吧:
/*************************************************************
ad9851串口驱动程序
2007-8-28-------------water
************************************************************/
#include
#include
#include
//-----------------------定义管脚--------------------------------------------------------
sbit D7=P3^3; //控制子串传送位 sbit DDS_FQUD=P3^4; //更新发送频率 sbit DDS_CLK=P3^5; //接外部晶振时钟 这里为30M unsigned long control_word(float freq);
void send_control( unsigned long bytedata);
void AD9851Init(void) //DDS初始化函数,包括DDS复位和初
始化为串行发送
{
DDS_CLK=0;
DDS_FQUD=0;
DDS_CLK=1;
DDS_CLK=0;
DDS_FQUD=1;
DDS_FQUD=0;
}
main()
{
unsigned long x;
DDS_FQUD=0;
AD9851Init();
x=control_word(500000);
while(1)
send_control(x);
}
//计算9851控制字,freq为你要输出的频率 unsigned long control_word(float freq) {
unsigned long water;
water=23.86115*freq;//外部晶振为30M,6倍频后180M,其关系由公式算出。
//water=143.456*freq; //若不用6倍频,则其关系
return(water);
}
//发送控制字
void send_control( unsigned long bytedata) {
int i;
unsigned char model="0x01";//模式选择为六倍频
DDS_FQUD=0;
_nop_();_nop_();_nop_();_nop_();_nop_();
for(i=0;i<32;i++)//先写32位的频率控制子(现低位后高位)
{
D7=(bit)(bytedata&(0x00000001)); //按时序写入
DDS_CLK=1;
_nop_();_nop_();_nop_();_nop_();_nop_(); //必要的时序延时
DDS_CLK=0;
_nop_();_nop_();_nop_();_nop_();_nop_();
bytedata>>=1;
}
for(i=0;i<8;i++)//再写其他8位控制字,这里只写了6倍频使能
{
D7=(bit)(model&(0x01));
DDS_CLK=1;
_nop_();_nop_();_nop_();_nop_();_nop_();
DDS_CLK=0;
_nop_();_nop_();_nop_();_nop_();_nop_();
model>>=1;
}
DDS_FQUD=1;
DDS_FQUD=0;
}
上面的代码中已经有详细的注解。其参考DATASHEET中的时序如下:
并行方式的驱动代码如下:
AD9851.H文件
//======IO Define=======
//sbit DDSRST="P2"^0;
sbit FQ_UD_AD9851=P3^3;
sbit W_CLK_AD9851=P3^4;
//======================
unsigned long int freq = 0;
//unsigned char Control_AD9851 = 0x09; // Phase0 ,power down mode and 6
REFCLK Multiplier enable
//unsigned char Control_AD9851 = 0x00; // Phase0 ,power on mode and 6
REFCLK Multiplier disable
unsigned char Control_AD9851 = 0x01; // Phase0 ,power on mode and 6
REFCLK Multiplier enable
unsigned char W1=0X0e;//附初值为1MHZ unsigned char W2=0X38;
unsigned char W3=0Xe3;
unsigned char W4=0X8e;
void Parallel2Serial_AD9851(void)
{ FQ_UD_AD9851=0;
W_CLK_AD9851=0;
P2=Control_AD9851;_nop_();_nop_();_nop_();_nop_();//延时很重要,对时
序
W_CLK_AD9851=1;//字装入信号,上升沿有效
W_CLK_AD9851=0;
P2=W1;_nop_();_nop_();_nop_();_nop_();_nop_();
W_CLK_AD9851=1;
W_CLK_AD9851=0;
P2=W2;_nop_();_nop_();_nop_();_nop_();_nop_();
W_CLK_AD9851=1;
W_CLK_AD9851=0;
P2=W3;_nop_();_nop_();_nop_();_nop_();_nop_();
W_CLK_AD9851=1;
W_CLK_AD9851=0;
P2=W4;_nop_();_nop_();_nop_();_nop_();_nop_();
W_CLK_AD9851=1;
W_CLK_AD9851=0;
FQ_UD_AD9851=1;
FQ_UD_AD9851=0;
}
void Set_Freq(float Freqency)
{
//freq= (unsigned long int)(23.86092942*Freqency); // SYSCLK = 180
MHz
freq= (unsigned long int)(23.86115*Freqency); // SYSCLK = 180 MHz
W4=(unsigned char)freq&0xff;
freq=freq>>8;
W3=(unsigned char)freq&0xff;
freq=freq>>8;
W2=(unsigned char)freq&0xff;
freq=freq>>8;
W1=(unsigned char)freq&0xff;
Parallel2Serial_AD9851(); }
AD9851.C文件
/*******************************************************
ad9851并口驱动程序
外部提供30MHZ晶振,6倍频率模式
2007-8-28-----------------water
*********************************************************/
#include
#include
#include
sbit RST_AD9851= P3^2; //长延时
void Delay80Ms(unsigned int k) {
unsigned int j;
while(k--)
{
j=7269;
while(j--);
}
}
void main(void)
{
RST_AD9851=1;
RST_AD9851=1;
RST_AD9851=0;
//Set_Freq(23000000);//发送的频率
while(1)
{
Set_Freq(22000000);//发送的频率
}
}
以上就是AD9851的驱动程序。
下面是本人用KS0108控制器的LCD做的任意频率信号发生器(当然为简单
只写了正弦波和方波)AD9851是可以直接产生方波的(里面有比较器)。
全部代码如下(有兴趣的可以参考):
// body.h文件
void delay(unsigned int t);
void write_com(unsigned char cmdcode);
void write_data(unsigned char Dispdata);
unsigned char read_data();
void Clr_Scr();
void Disp_Img(unsigned char code *img);
void hz_disp16(unsigned char pag,unsigned char col, unsigned char code *hzk); void hz_disp32(unsigned char pag,unsigned char col, unsigned char code *hzk); void hz_disp48(unsigned char pag,unsigned char col, unsigned char code *hzk); void hz_disp64(unsigned char pag,unsigned char col, unsigned char code *hzk); void init_lcd();
void Putedot(unsigned char Order);
void Putstr( unsigned char *str , unsigned char i , unsigned char lie, unsigned
char hang);
void Msg(int flg) ;
bit judge_hitkey() ;
void mdelay(unsigned int N) ;
unsigned char kbscan(void);
unsigned long int covert_decimal_to_hex(unsigned long int r) ;
#define Disp_On 0x3f
#define Disp_Off 0x3e
#define Col_Add 0x40
#define Page_Add 0xb8
#define Start_Line 0xc0
#define Lcd_Bus P0 //MCU P1<------> LCM
sbit Mcs="P1"^5; //Master chip enable sbit Scs="P1"^6; //Slave chip enable sbit Enable="P1"^2; //6800 mode Enable single sbit Di="P1"^0; //Data or Instrument Select sbit RW="P1"^1; //Write or Read
sbit Lcd_Rst=P1^7; //Lcm reset
// body.c文件
#include
#include
#include
#include
#include
//#include
#include "data.h"
//-----------液晶定义引脚----------------------- #define Disp_On 0x3f
#define Disp_Off 0x3e
#define Col_Add 0x40
#define Page_Add 0xb8
#define Start_Line 0xc0
#define Lcd_Bus P0 //MCU P1<------> LCM sbit Mcs="P1"^5; //Master chip enable sbit Scs="P1"^6; //Slave chip enable sbit Enable="P1"^2; //6800 mode Enable single sbit Di="P1"^0; //Data or Instrument Select
sbit RW="P1"^1; //Write or Read
sbit Lcd_Rst=P1^7; //Lcm reset
//-----------------------ad9851定义管脚
-------------------------------------------------------- sbit D7=P3^3;
sbit DDS_FQUD=P3^4; //更新发送频率 sbit DDS_CLK=P3^5;
//--------------------ad9851函数声明------------- unsigned long control_word(float freq); void send_control( unsigned long bytedata);
//-----------全局变量---------------------------
unsigned char row;
unsigned char col;
extern unsigned char j_j; //一级菜单标志j_j为全局变量
extern float beauty;
/****************************液晶程序部分
************************************/ /*------------------延时子程序-----------------------------*/
void delay(unsigned int t)
{
unsigned int i,j;
for(i=0;i9) //超出范围重0开始
i1=0;
if(key==0x14) break; //退出键
if(key==0x18)
beauty=i1; // 把步进值给全局变量beauty
x=control_word(beauty);//得到频率控制字
send_control(x);//发送频率控制字
}
beauty=0;
Clr_Scr();
j_j=0; //返回一级菜单界面
i1=0; //消去i1的值
}
if(flg==5) //步进为10HZ界面
{
Mcs=0;Scs=1;
hz_disp32(3,0,hz);
Mcs=1;Scs=0;
hz_disp8(3,32,shu1[0]);
while(1)
{
key=kbscan();
while(judge_hitkey());
if(key==0x12)
i2++;
if(key==0x11)
i2--;
Mcs=1;Scs=0;
hz_disp8(3,24,shu1[i2]);
if(i2<0||i2>9) //超出范围重0开始
i2=0;
if(key==0x14) break; //退出键 返回一级菜单界面
if(key==0x18)
beauty=(float)i2*10; //把步进值给全局变量beauty
x=control_word(beauty);//得到频率控制字
send_control(x);//发送频率控制字
}
beauty=0;
Clr_Scr();
j_j=0;
i2=0;
}
if(flg==6) //步进为100HZ界面
{
Mcs=0;Scs=1;
hz_disp32(3,0,hz);
Mcs=1;Scs=0;
hz_disp8(3,32,shu1[0]);
hz_disp8(3,24,shu1[0]);
while(1)
{
key=kbscan();
while(judge_hitkey());
if(key==0x12)
i3++;
if(key==0x11)
i3--;
Mcs=1;Scs=0;
hz_disp8(3,16,shu1[i3]);
if(i3<0||i3>9) //超出范围重0开始
i3=0;
if(key==0x14) break; //退出键 返回一级菜单界面
if(key==0x18)
beauty=(float)i3*100; // 把步进值给全局变量beauty
x=control_word(beauty);//得到频率控制字
send_control(x);//发送频率控制字
}
beauty=0;
Clr_Scr();
j_j=0;
i3=0;
}
if(flg==7 ) //步进为1kHZ界面
{
Mcs=0;Scs=1;
Putstr(STR5,strlen(STR5),0,3);
Mcs=1;Scs=0;
hz_disp8(3,56,shu1[0]);
while(1)
{
key=kbscan();
while(judge_hitkey());
if(key==0x12)
i4++;
if(key==0x11)
i4--;
Mcs=1;Scs=0;
hz_disp8(3,56,shu1[i4]);
if(i4<0||i4>9) //超出范围重0开始
i4=0;
if(key==0x14) break; //退出键 返回一级菜单界面
if(key==0x18)
beauty=(float)i4*1000; // 把步进值给全局变量beauty
x=control_word(beauty);//得到频率控制字
send_control(x);//发送频率控制字
}
beauty=0;
Clr_Scr();
j_j=0;
i4=0;
}
}
//主程序所在文
件
//2007-8-14,修改,
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include"data.h"
unsigned char j_j;
float beauty;//要输出的频率
//-----------------------ad9851定义管脚
-------------------------------------------------------- sbit D7=P3^3;
sbit DDS_FQUD=P3^4; //更新发送频率
sbit DDS_CLK=P3^5;
unsigned long control_word(float freq); void send_control( unsigned long bytedata); //-------------------ad9851部分
-------------------------------------------------------------------------
//DDS初始化函数,包括DDS复位和初始化为串行发送
void AD9851Init(void)
{
DDS_CLK=0;
DDS_FQUD=0;
DDS_CLK=1;
DDS_CLK=0;
DDS_FQUD=1;
DDS_FQUD=0;
}
//计算9851控制字
unsigned long control_word(float freq)
{
unsigned long water;
water=23.86115*freq;
return(water);
}
//发送控制字
void send_control( unsigned long bytedata) {
int i;
unsigned char model="0x01";//模式选择为六倍频
DDS_FQUD=0;
_nop_();_nop_();_nop_();_nop_();_nop_();
for(i=0;i<32;i++)
{
D7=(bit)(bytedata&(0x00000001));
DDS_CLK=1;
_nop_();_nop_();_nop_();_nop_();_nop_();
DDS_CLK=0;
_nop_();_nop_();_nop_();_nop_();_nop_();
bytedata>>=1;
}
for(i=0;i<8;i++)
{
D7=(bit)(model&(0x01));
DDS_CLK=1;
_nop_();_nop_();_nop_();_nop_();_nop_();
DDS_CLK=0;
_nop_();_nop_();_nop_();_nop_();_nop_();
model>>=1;
}
DDS_FQUD=1;
DDS_FQUD=0;
}
//--------------------------主函数部分(主要包括菜单框架)
----------------------------------------------
main(void)
{
unsigned char key;
int i,menu,k;
unsigned long x;//频率控制字
i=1;
Clr_Scr();
init_lcd();
Msg(0);
delay(5000);
Clr_Scr();
Msg(1);
delay(8000);
Clr_Scr();
Msg(2);
DDS_FQUD=0;
AD9851Init();//ad9851初始化
x=control_word(1000);//得到频率控制字
send_control(x);//发送频率控制字
while(1)
{
// x="control"_word(2500000); //DDS
x=control_word(beauty);//得到频率控制字
send_control(x);//发送频率控制字
key=kbscan();
while(judge_hitkey()); //判断按键释放 肖注释
switch(key)
{
case 0x12: i++;Clr_Scr(); break;
case 0x11: i--;Clr_Scr(); break;
case 0x18: j_j=1;Clr_Scr(); break;
case 0x14: j_j=0;Clr_Scr(); break;
}
menu=10*j_j+i;
if(j_j==0)
{
Msg(2);
k=i; //下面有用
switch(i)
{
case 1: Mcs="1";Scs=0;hz_disp16(0,0,xia);break;
case 2: Mcs="1";Scs=0;hz_disp16(4,0,xia);break;
case 3: Mcs="1";Scs=0;hz_disp16(6,0,xia);break;
case 4: Mcs="0";Scs=1;hz_disp16(4,0,xia);break;
case 5: Mcs="0";Scs=1;hz_disp16(6,0,xia);break;
}
}
if(j_j==1)
{
i=k; //锁定2级菜单的光标键
switch(menu)
{
case 11: Msg(3);break;
case 12: Msg(4);break;
case 13: Msg(5);break;
case 14: Msg(6);break;
case 15: Msg(7);break;
}
}
if(i>5||i==0) // 光标超出菜单界面处理
i=1;
}
}