赞
踩
具有足够细密的相位步长。N为数据序号,phase为相位,Am为正弦波计算值,Data_10为10位数字量的10进制表示,用一个10位DAC描述,其中512对应的实际值为0。
从0开始正弦波幅度一直增加,但是直到第12个点才会被DAC的输出表示出来。
固定时钟MCLK为36MHz,DAC以MCLK为节拍在表上遍历,如果步长为1,则:
f
O
U
T
=
1
T
M
C
L
K
×
N
max
m
=
f
M
C
L
K
×
m
N
max
=
36
×
1
0
6
×
1
36000
=
1000
H
z
f_{O U T}=\frac{1}{T_{M C L K} \times \frac{N_{\max }}{m}}=\frac{f_{M C L K} \times m}{N_{\max }}=\frac{36 \times 10^{6} \times 1}{36000}=1000 \mathrm{~Hz}
fOUT=TMCLK×mNmax1=NmaxfMCLK×m=3600036×106×1=1000 Hz
这个公式可以这么理解:输出的正弦波遍历完一整个表的话就算是一个周期,遍历1步需要
T
M
C
L
K
T_{M C L K}
TMCLK秒,一共需要走
N
max
m
\frac{N_{\max }}{m}
mNmax步(表中一共
N
max
N_{\max }
Nmax个点),这样就可以计算出周期以及频率。
DDS的最小分辨率为:
Δ
f
O
U
T
=
f
M
C
L
K
×
Δ
m
N
max
=
36
×
1
0
6
×
1
36000
=
1000
H
z
\Delta f_{O U T}=\frac{f_{M C L K} \times \Delta m}{N_{\max }}=\frac{36 \times 10^{6} \times 1}{36000}=1000 \mathrm{~Hz}
ΔfOUT=NmaxfMCLK×Δm=3600036×106×1=1000 Hz
此外,样点总数除以m可以不为整数。虽然不能完整的扫描整个表,但是差别其实是很小的,可以忽略。
由相位累加器PA、相位幅度表和数模转换器DAC组成。以28位的DAC为例,它可以计数0-
2
28
2^{28}
228。步长为m,则正弦波频率为:
f
out
=
m
2
28
×
f
M
C
L
K
f_{\text {out }}=\frac{m}{2^{28}} \times f_{M C L K}
fout =228m×fMCLK
其原理与前文是一致的。
总体结构如图:
相位累加器外部一般不需要很高的位数。
优点:
缺点:
FREQ0与FREQ1是两个频率寄存器,通过SPI写入,代表的是相位步长m。两个寄存器是为了方便切换。然后是28位相位累加器,完成递增的操作。之后的加法器完成相位失调的接入,代表的是初相角,存在PHASE0和PHASE1中。加法器的结果(代表相位)高12位提供给查找表,作为地址读取内容,并通过DAC输出。
DAC有两种输出模式,电压输出和互补电流输出。电压输出无需过多处理,比如AD9833的DAC为单端电流输出型,通过内部200 Ω \Omega Ω的电阻转变为电压输出。
电流输出形式的一般为互补型,具有两个管脚,以AD9850为例:
两个管脚输出的电流满足:
I
O
U
T
+
I
O
U
T
B
=
I
F
S
I_{O U T}+I_{O U T B}=I_{F S}
IOUT+IOUTB=IFS
I
F
S
I_{F S}
IFS为满幅度输出电流,由
R
s
e
t
R_set
Rset决定。两个管脚的输出可以产生相差180°的差分信号。输出再经过电阻转化电压,注意输出脚顺从电压的限制。外部电路可能并联电容起到低通滤波的作用:
f
H
=
1
2
π
R
5
C
12
=
7.96
k
H
z
f_{H}=\frac{1}{2 \pi R_{5} C_{12}}=7.96 \mathrm{kHz}
fH=2πR5C121=7.96kHz
正弦信号不应超过此频率。若要更有效的低通滤波,可以采用椭圆滤波器。
结构:
AD9850采用32位的相位累加器将信号截断成14位输入到正弦查询表,查询表的输出再被截断成10位后输入到DAC,DAC再输出两个互补的电流。DAC满量程输出电流通过一个外接电阻RSET调节,调节关系为ISET=32(1.148V/RSET),RSET的典型值是3.9kΩ。
AD9850在接上精密时钟源和写入频率相位控制字之间后就可产生一个频率和相位都可编程控制的模拟正弦波输出,此正弦波可直 接用作频率信号源或经内部的高速比较器转换为方波输出。在125MHz的时钟下,32位的频率控制字可使AD9850的输出频率分辨率 达0.0291Hz;并具有5位相位控制位,而且允许相位按增量180°、90°、45°、22.5°、11.25°或这些值的组合进行调整。
控制:
AD9850有40位控制字,32位用于频率控制,5位用于相位控制。1位用于电源休眠(Power down)控制,2位用于选择工作方式。这40位控制字可通过并行方式或串行方式输入到AD9850,控制时序图如下:
在并行装入方式中,通过8位总线将可数据输入到寄存器,在重复5次之后再在FQ-UD上升沿把40位数据从输 入寄存器装入到频率/相位数据寄存器(更新DDS输出频率和相位),同时把地址指针复位到第一个输入寄存器。
在W-CLK的上升沿装入8位数据,并把指针指向下一个输入寄存器,连续5个W-CLK上升沿后,W-CLK的边沿就不再起作用,直到复位信号或FQ-UD上升沿把地址指针复位到第一个寄存器。
下图展示了各位数据的含义:
在串行输入方式,W-CLK上升沿把25引脚的一位数据串行移入,当移动40位后,用一个FQ-UD脉冲即可更新输出频率和相位。下图是相应的控制字串行输入的控制时序图:
AD9850的复位(RESET)信号为高电平有效,且脉冲宽度不小于5个参考时钟周期。AD9850的参考时钟频率一般远高于单片机的时钟频率,因此AD9850的复位(RESET)端可与单片机的复位端直接相连。串行的3脚、4脚需要连接在一起:
各位数据代表含义如下:
用于选择工作方式的两个控制位,无论并行还是串行最好都写成00.
单片机:
I/O方式的并行接口电路比较简单,但占用单片机资源相对较多,下图是I/O方式并行接口的电路图,AD9850的数据线D0~D7与P1口相连,FQ-UD和W-CLK分别与P3.0(10引脚)和P3.1(11引 脚)相连,所有的时序关系均可通过软件控制实现。
将DDS控制字从高至低存放于30H至34H中,汇编语言如下:
MOV R0,#05H ;数据05输入R0
MOV R1,#30H ;数据30输入R1
DD:MOV P1,@R1 ;(30H)地址上的数据传入P1,且程序位置为DD
SETB P3.1 ;P3.1置1
CLR P3.1 ;P3.1置0
INC R1 ;R1数据加1,指向下一个地址
DJNZ R0,DD ;R0先减1,若不为0,则转移到DD,循环一共执行5次
SETB P3.0 ;P3.0置1
CLR P3.0 ;P3.0置0
END ;结束
在程序中,每将一字节的数据送到P1口后,必须将P3.1(W-CLK)置高。在其上升沿,AD9850接收到P1口相连的 数据线上的数据,然后将P3.1置低,并准备下一字节的发送,连续发送5个字节后,须将P3.0(FQ-UD)置高,以使AD9850根据则输入的控制字更改频率和相位输出,随后再置P3.0为低,准备下一组发送。单片机的P3.0、P3.1引脚为串行口,当被占用时,W-CLK和FQ-UD引脚也可与其它I/O脚相连,这时需要修改相应的发送程序。
总线方式并行接口占用的单片机资源较少,在这种方式下,AD9850仅作为一扩展芯片而占用RAM的一段地址,必须时也可以只占用一个地址。 下图是总线方式并行接口的电路原理图。
同样将DDS控制字从高至低存放于30H至34H中,发送控制字的程序清单如下:
MOV R0,#05H ;数据05输入R0
MOV R1,#30H ;数据30输入R1
MOV DPTR,#0700H ;DRTR为数据指针寄存器,指向0700H
DD:MOV A,@R1 ;(30H)地址上的数据传入A,且程序位置为DD
MOVX @DPTR,A ;A的数据传入DPTR指向的地址
INC R1 ;R1数据加1,指向下一个地址
DJNZ R0,DD ;R0先减1,若不为0,则转移到DD,循环一共执行5次
MOVX A,@DPTR ;DPTR指向的地址中的数据
END ;结束
AD9850的W-CLK和FQ-UD信号都是上升沿有效,用MOVX @DPTR,A指令向AD9850传送控制字时,由74F138将高八位地址的低三位译码(0700H正好是高八位地址低三位是高电平,其余低电平),其输出经反相(38译码器输出低有效)并与反相后的信号相与得到一上升沿送至AD9850的W-CLK脚。这一步其实是关键也是不同之处,MOVX @DPTR,A指令执行时WR脚变为低电平反相后为高电平,p0输出数据,p2输出地址高8位,0700H对应的输出在经过38译码器再反相之后输出的也是高电平,相与之后WCLK接收到高电平,控制AD9850接收信号。一旦程序结束,WCLK接收到的是低电平。此时已送到总线上的数据将被AD9850接收完毕。
连续五次将40位的控制字全部发送以后,用MOVA A,@DPTR指令产生FQ-UD信号,使AD9850更改输出频率和相位,此时读入到单片机内的数据实际上无任何意义。这一步与前面是类似的,该指令使单片机读信号,信号地址为0700H,P2口产生信号地址的高8位,RD变为低电平,两者反相相与之后变为高电平,被FQ_UD接受;P0接受信号,但是接收到的信号无意义。一旦程序结束,FQ_UD接收到的是低电平。图中AD9850的地址为0700H。
上述接口电路和程序也适用于与AD9850脚对脚兼容的AD9851,值得注意的是:AD9851的控制字与AD9850控制辽中别位的定义稍有区别,编程时应予以注意。
原理图如下:
下面以AD9850为核心,一部分一部分的来分析。AD9850的引脚含义在数据手册中已经给出:
D0-D7为AD9850的8位数据接收口,连接在单片机的一组IO口上:
按照要求连接AD9850的数字电源、数字地,模拟电源、模拟地,电源为5V即可:
RESET、WCLK、FQ_UD 3个引脚引出到排针上,可以选择之前提到的第一钟方式连接在单片机上,通过单片机来控制:
CLK_IN引脚也引出到排针上,它需要接一个参考的时钟信号,可以外接上一个50MHz的晶振(超过的话就会关断):
2个QOUT是比较器的输出,输出的是方波,两者是互补的(AD9850是互补电流输出)
IOUT与IOUTB为互补电流输出,IOUT通过200
Ω
\Omega
Ω的电阻转化为电压信号,然后通过椭圆滤波器得到正弦信号输出。VINP与VINN为比较器的正端、负端输入信号,前者直接接正弦输出,后者接在R4、R5间,得到输出电压的均值。
这一部分有一个问题:就是无论是上面的原理图中,还是数据手册中给出的压流转换的电阻都是200
Ω
\Omega
Ω,在Rset为3.9k
Ω
\Omega
Ω的情况下,最大输出电流为10mA,而AD9850的顺从电压为1.5V:
按理说,根据:
电阻不应该超过150?
其余部分不做具体介绍。
变量定义:
/* 1 、可通过按键实现频率输出步进加减;步进频率范围0~10MHz; 2、步进值有六种选择:10Hz、100Hz、1000Hz、10KHz、100KHz、1000KHz 3、采用1602液晶显示屏,可以实时显示输出频率值,显示当前步进值,显示频率的单位都为Hz。 */ //基本功能全部实现;2009041407 #include <reg52.h> //调用头文件(单片机内部的寄存器定义) #define uchar unsigned char #define uint unsigned int /******本段为硬件I/O口定义********/ sbit LCD_E = P1^1;//定义1602液晶的使能管脚; sbit LCD_RW = P1^2;//定义1602液晶的读写管脚; sbit LCD_RS = P1^3;//定义1602液晶的选通管脚; sbit reset = P1^4; //ad9850的复位引脚; sbit w_clk = P1^7; //ad9850的时钟引脚; sbit fqud = P1^6; //ad9850的输出更新引脚; #define LCD_DATA P2 //向1602液晶传送数据的端口,这里用的是P2口; #define LCD_BUSY 0x80 // 用于检测LCD的忙标识(本程序中用的是延时,未检测) //LCD显示内容,定义到代码段; unsigned char code LcdBuf1[]= {"FRQ: Hz"}; unsigned char code LcdBuf2[]= {"Step:"}; double Con_Word_1 = 0x00;//定义了一个浮点变量,用于计算控制字; double Con_Word_2 = 0x00;//定义了一个浮点变量,用于计算控制字; long uint ConTrol_Word = 0x00;//用来存储控制字的数值; long uint Frequency_Out;//设置的频率值; uchar a,b,c,d,e,f,g,h; //为了向1602写入频率值,首先将频率值拆分存于这8个变量中; sbit Light = P1^0; //程序状态指示灯,它与单片机对9850控制无关,只是调试程序的时候使用! //定义按键;2X3矩阵键盘; sbit P3_4 = P3^4; sbit P3_6 = P3^6; sbit P3_7 = P3^7; sbit P3_3 = P3^3; sbit P3_5 = P3^5; uchar dat = 0;//键盘子程序处理过程中使用的中间变量; uchar keyzhi = 0x00;//键值;键盘扫描子程序的返回值存于该变量中;
LCD相关函数:
//函数声明 void lcd_init(void);//1602液晶初始化子程序; void display_string(unsigned char x,unsigned char y,unsigned char *s); //显示字符子程序;x、y是坐标;x:从左边数起第几个字符:y:是第一行还是第二行; /************************************************** ** 函数名称: delay ** 入口参数:h(unsigned int型) ** 出口参数:无 ** 功能描述: 短暂延时,使用11.0592晶体,约0.01MS ****************************************************/ void delay(long unsigned int h) { while(h--); //延时子程序 } /************************************************** ** 函数名称: WriteDataLcd ** 入口参数:wdata(unsigned char型) ** 出口参数:无 ** 功能描述: 写数据到LCD ****************************************************/ void WriteDataLcd(unsigned char wdata)//向1602液晶写入数据; { LCD_RS=1; LCD_RW=0; LCD_E=0; LCD_E=1; LCD_DATA=wdata; delay(100); //短暂延时,代替检测忙状态 LCD_E=0; } /************************************************** ** 函数名称: WriteCommandLcd ** 入口参数:wdata(unsigned char型) ** 出口参数:无 ** 功能描述: 写命令到LCD ****************************************************/ void WriteCommandLcd(unsigned char wdata)//向1602液晶写入命令; { LCD_RS=0; LCD_RW=0; LCD_E=0; LCD_E=1; LCD_DATA=wdata; delay(100); //短暂延时,代替检测忙状态 LCD_E=0; } //LCD初始化 void lcd_init(void) { LCD_DATA = 0; delay(1000); WriteCommandLcd(0x38); delay(500); WriteCommandLcd(0x38); //显示模式设置 delay(500); WriteCommandLcd(0x38); //显示模式设置 delay(500); WriteCommandLcd(0x01); //关闭显示 WriteCommandLcd(0x38); //显示清屏 WriteCommandLcd(0x0c); //显示光标移动设置 WriteCommandLcd(0x06); //显示开及光标移动设置 } /************************************************** ** 函数名称: display_xy ** 入口参数:x(unsigned char型),y(unsigned char型) ** 出口参数:无 ** 功能描述: 设置光标位置, x是行号,y是列号 ****************************************************/ void display_xy(unsigned char x,unsigned char y) { if(y==0x01) { x = x + 0x40 + 0x80; } else { x = x+0x80; } WriteCommandLcd(x); } /********************************************************************* ** 函数名称: display_string ** 入口参数:x(unsigned char型),y(unsigned char型),s(指针型) ** 出口参数:无 ** 功能描述: 在具体位置显示字符串,以/0结束,x是列号,y是行号 **********************************************************************/ void display_string(unsigned char x,unsigned char y,unsigned char *s) { display_xy(x,y); while(*s) { WriteDataLcd(*s); s++; } } Qu_Chu_Shu_Ma_Ge_Wei() //取出要显示的每一位数据; { a = Frequency_Out % 10; b = (Frequency_Out % 100)/10; c = (Frequency_Out % 1000)/100; d = (Frequency_Out % 10000)/1000; e = (Frequency_Out % 100000)/10000; f = (Frequency_Out % 1000000)/100000; g = (Frequency_Out % 10000000)/1000000; h = (Frequency_Out % 100000000)/10000000; } display_data() //显示数据子程序 { Qu_Chu_Shu_Ma_Ge_Wei();//取出要显示的每一位数据; display_string(1,0,LcdBuf1);//显示第一行,从第2个位置开始 WriteCommandLcd(0x85);//显示数值的话,用其真实的地址,如0x83等, WriteDataLcd(0x30+h); //如果要显示字符的话,暂时用x、y坐标的方式 WriteDataLcd(0x30+g);//以下都是送显示数据; WriteDataLcd(0x30+f); WriteDataLcd(0x30+e); WriteDataLcd(0x30+d); WriteDataLcd(0x30+c); WriteDataLcd(0x30+b); WriteDataLcd(0x30+a); }
按键处理函数:
scan_KEY(void) //键值处理子程序 { uchar key = 0; key = P3; key = key & 0xf8; switch(key) { case 0xb0: keyzhi = 3 ;break; case 0xa8: keyzhi = 2 ;break; case 0x98: keyzhi = 1 ;break; case 0x70: keyzhi = 6 ;break; case 0x68: keyzhi = 5 ;break; case 0x58: keyzhi = 4 ;break; default: keyzhi=0; } return(keyzhi); } keychuli()//键盘扫描子程序 { P3_3 = 0x01;P3_5 = 0x01; P3_4 = 0x01;P3_6 = 0x00;P3_7 = 0x00; delay(100); if((P3_3&P3_4&P3_5) == 0x00) { if((P3_3&P3_4&P3_5) == 0x00) { P3_6 = 0x00;P3_7 = 0x01; dat = scan_KEY(); if(dat == 0x00) { P3_6 = 0x01;P3_7 = 0x00; dat = scan_KEY(); } } else { dat = 0x00; goto down;//无键按下 } } down:return(dat); }
下面这块才是主要的程序,用来控制、使用AD9850:这一部分也是最核心的地方
Calculate_Control_Word(long uint Frequency_Out) { Con_Word_1 = Frequency_Out * 85; Con_Word_2 = Frequency_Out *0.88423027547;//50MHz计算方法 //首先有源晶体是50MHz的,然后用2的32次方减1,再除以50MHz得到的; Con_Word_2 = Con_Word_2 + 0.5;//小数部分四舍五入; Con_Word_1 = Con_Word_1 + Con_Word_2; //根据设定的频率以浮点形式计算控制字 ConTrol_Word = Con_Word_1 / 1;//将控制字换算成整数形式 } Send_Control_Word(long uint ConTrol_Word)//向AD9850送入频率控制字; { long uint ConTrol_Word_Temporary ; uchar data_word ; w_clk = 0x00; //根据时序图 fqud = 0x00; //根据时序图 //data_word = 0x00; //设置AD9851相位、掉电等相关控制字 data_word = 0x00; //设置AD9850相位、掉电等相关控制字 P2 = data_word; delay(200); w_clk=1; delay(200); w_clk=0; ConTrol_Word_Temporary = ConTrol_Word; ConTrol_Word = ConTrol_Word >> 24; data_word = ConTrol_Word % 256;//取出W1,频率控制字中的最高字节; P2 = data_word; delay(200); w_clk=1; delay(200); w_clk=0; ConTrol_Word = ConTrol_Word_Temporary; ConTrol_Word = ConTrol_Word >> 16; data_word = ConTrol_Word % 256;//取出W2,频率控制字中的次高字节; P2 = data_word; delay(200); w_clk=1; delay(200); w_clk=0; ConTrol_Word = ConTrol_Word_Temporary; ConTrol_Word = ConTrol_Word >> 8; data_word = ConTrol_Word % 256;//取出W3,频率控制字中的第三字节; P2 = data_word; delay(200); w_clk=1; delay(200); w_clk=0; ConTrol_Word = ConTrol_Word_Temporary; data_word = ConTrol_Word % 256;//取出W4,频率控制字中的最后一个字节; P2 = data_word; delay(200); w_clk=1; delay(200); w_clk=0; //根据时序图 fqud=1; //根据时序图 }
主程序:
main() { unsigned char i = 0x00; uint Step_Bian_Liang = 0x00;//步进变量; long uint Step_Data = 10;//默认为10Hz; long unsigned int Step_Data_Xian_Shi = 0x00; reset = 1; //复位AD9850; delay(1000); reset = 0; //使AD9850进入正常的工作状态; lcd_init(); //初始化子程序; Frequency_Out = 0;//开机默认的频率输出值为0; Calculate_Control_Word(Frequency_Out);//根据设置的频率换算成将要送入AD9850中的整数值; Send_Control_Word(ConTrol_Word); //向AD9850送出频率控制字; display_data(); //显示数据; display_string(0,1,LcdBuf2); //显示第二行,从第0个位置开始; WriteCommandLcd(0xc5);//从第二行第六个开始显示数据; WriteDataLcd(0x30+(Step_Data/1000));//送出显示数据; WriteDataLcd(0x30+(Step_Data/100)%10);//送出显示数据; WriteDataLcd(0x30+(Step_Data/10)%10);//送出显示数据; WriteDataLcd(0x30+(Step_Data%10));//送出显示数据; display_string(9,1,"Hz");//显示Hz; while(1) { keychuli();//键处理子程序 if(dat == 0x01)//键值为1;步进加 { dat = 0x00;delay(5000); Frequency_Out = Frequency_Out + Step_Data; if(Frequency_Out > 10000000) { Frequency_Out = Frequency_Out - Step_Data; } Light =~Light; display_data(); reset = 1; //复位AD9850; delay(100); reset = 0; //使AD9850进入正常的工作状态; Calculate_Control_Word(Frequency_Out);//根据设置的频率换算成将要送入AD9850中的整数值; Send_Control_Word(ConTrol_Word); //向AD9850送出频率控制字; } if(dat == 0x04)//键值为4;步进减 { dat = 0x00;delay(5000); if(Frequency_Out >= Step_Data) { Frequency_Out = Frequency_Out - Step_Data; } Light =~Light; display_data(); reset = 1; //复位AD9850; delay(1000); reset = 0; //使AD9850进入正常的工作状态; Calculate_Control_Word(Frequency_Out);//根据设置的频率换算成将要送入AD9850中的整数值; Send_Control_Word(ConTrol_Word); //向AD9850送出频率控制字; } if(dat == 0x06)//步进选择;这里有三种选择 { dat = 0x00;delay(5000);Light =~Light; Step_Bian_Liang++; Step_Bian_Liang = Step_Bian_Liang%6; if(Step_Bian_Liang == 0x00) { Step_Data = 10;//步进10;改成1 } if(Step_Bian_Liang == 0x01) { Step_Data = 100; } if(Step_Bian_Liang == 0x02) { Step_Data = 1000; } if(Step_Bian_Liang == 0x03) { Step_Data = 10000; } if(Step_Bian_Liang == 0x04) { Step_Data = 100000; } if(Step_Bian_Liang == 0x05) { Step_Data = 1000000; } if(Step_Data<=1000) { WriteCommandLcd(0xc5);//参考上面类似的部分 WriteDataLcd(0x30+(Step_Data/1000)); WriteDataLcd(0x30+(Step_Data/100)%10); WriteDataLcd(0x30+(Step_Data/10)%10); WriteDataLcd(0x30+(Step_Data%10)); display_string(9,1,"Hz "); } else { Step_Data_Xian_Shi = Step_Data/1000; WriteCommandLcd(0xc5);//参考上面类似的部分 WriteDataLcd(0x30+(Step_Data_Xian_Shi/1000)); WriteDataLcd(0x30+(Step_Data_Xian_Shi/100)%10); WriteDataLcd(0x30+(Step_Data_Xian_Shi/10)%10); WriteDataLcd(0x30+(Step_Data_Xian_Shi%10)); display_string(9,1,"kHz"); } } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。