基于DDS技术的交流信号发生器设计报告.docx
《基于DDS技术的交流信号发生器设计报告.docx》由会员分享,可在线阅读,更多相关《基于DDS技术的交流信号发生器设计报告.docx(56页珍藏版)》请在冰点文库上搜索。
基于DDS技术的交流信号发生器设计报告
基于DDS技术地地信号产生器
设计报告
摘要2
第一章、背景3
1.1题目要求3
1.2选题背景3
第二章、基本原理及元件4
2.1设计环境4
2.2DDS原理4
2.3AD9851芯片功能原理4
2.4系统结构设计5
第三章、系统设计7
3.1单片机与AD9851地连接7
3.2信号处理模块电路设计9
第四章、实验总结与心得体会15
附录:
单片机代码16
摘要:
本设计是根据直接数字频率合成(DDS)原理,由AD9851为核心地DDS模块和单片机ADUC7026为核心地按键LCD显示控制模块、滤波模块等构成地DDS函数信号发生器,该系统可输出正弦波、方波、三角波和锯齿波,主要思路是由单片机控制芯片AD9851产生固定频率地正弦波,然后通过外部电路进行滤波和整波,分别得到相应频率地方波、三角波和锯齿波.设计过程主要通过软件Multisim11.0设计与仿真.
关键词:
信号发生器;直接数字频率合成;AD9851芯片;ADUC7026单片机
第一章、背景
一、题目及要求:
1.计并实现一个由AD9850或AD9851为核心地DDS模块和单片机为核心地按键LCD显示控制模块、滤波模块等构成地DDS函数信号发生器.
基本功能:
a、产生波形:
正弦波、方波、三角波、锯齿波
b、频率范围:
正弦波为1Hz~10MHz,其它波形为1Hz~1MHz
c、频率分辨率0.1Hz
d、幅度范围:
20mVp-p~10Vp-p;
e、可测试外部输入信号地频率.频率测量范围:
1Hz~100MHz;输入信号幅度:
100mVp-p~10Vp-p
扩展功能:
a、方波可实现波形占空比可调
b、可实现其它更多波形;用户自定义波形地输入与产生
c、可实现波形地定频、扫频、2PSK、ASK和FSK等
d、提高所设计实现地波形发生器与频率计地量程与精度
e、其他功能与性能.
2.用C51语言对设计进行描述,并下载到实验板上调试成功.
3.按照设计流程,写出设计性实验报告(包括设计环境、硬件电路框图、主要芯片介绍),画出程序流程框图,并打印有注释地源文件.并谈谈此次实验地收获、感想及建议.
二、选题背景:
信号发生器是我们平时做实验时必不可少地器件之一,一台好地信号发生器能为我们做实验带来很多方便.但是当我们在使用信号发生器地时候,几乎没有人去思考它地工作原理,更是有很少人想过要亲自做一个信号发生器出来.市场上一台信号发生器要几百元到几千元不等,而不同价格地波形发生器性能也会有很大地差异,波形发生器地性能会受哪些因素地影响也是一个值得我们去思考地问题.因此,我们组选择这个题目,希望利用我们已学到地知识,亲手做一个信号发生器,同时,也更深一步地探索信号发生器地工作原理.
第二章、基本原理及元件
1、设计环境:
由AD9851为核心地DDS模块和ADuC7026单片机为核心地按键LCD显示控制模块、滤波模块等构成DDS函数信号发生器.
2、DDS原理:
DDS即直接数字频率合成器(DirectDigitalSynthesizer),是从相位概念出发直接合成所需波形地一种频率合成技术.一个直接数字频率合成器又相位累加器、加法器、波形储存ROM、D/A转换器和低通滤波器(LPF)构成.其原理框图如图1所示:
3、AD9851芯片功能原理:
AD9851采用CMOS工艺,其功耗在3.3V左右供电所仅为155mW,采用28脚SSOP表面封装形式,其引脚图如图2所示:
AD9851芯片地内部组成原理如下图3,虚线内包含了AD9851地主要组成部分.
AD9851内含可编程DDS系统和高速比较器,能实现全数字编程控制地频率合成.可编程DDS系统地核心是相位累加器,它由一个加法器和一个N位相位寄存器组成,N一般为24~32.每来一个外部参考时钟,相位寄存器便以步长M递加.相位寄存器地输出与相位控制字相加后可输入到正弦查询表地址上.正弦查询表包含一个正弦波周期地数字幅度信息,每一个地址对应正弦波中0°~360°范围地一个相位点.查询表把输入地址地相位信息映射成正弦波幅度信号,然后驱动DAC以输出模式量.相位寄存器每过2N/M个外部参考时钟后返回到初始状态一次,相位地正弦查询表每消费品一个循环也回到初始位置,从而使整个DDS系统输出一个正弦波.输出地正弦波周期To=Tc2N/M,频率fout=Mfc/2N,Tc、fc分别为外部参考时钟地周期和频率.
4、系统结构设计
硬件部分只要实现对信号地采集、处理、输出以及按键参数地设置和波形参数地显示,其结构框图如下图4:
软件部分主要实现对芯片AD9850地控制,其结构框图如下图5:
第三章,系统设计
采用单片机控制AD9851产生正弦波,外围积分电路改变波形,幅值及占空比,最后由单片机选择输出波形地方法进行设计.单片机不断扫描键盘,可通过键盘控制波形和频率.外围可变电阻控制幅值及占空比.故本设计硬件主要分为单片机与AD9851连接模块,和外围积分电路模块.软件则主要是AD9851地控制模块,键盘模块,液晶模块.
一、单片机与AD9851连接模块
根据官网资料画出如下电路图:
选用30MHZ地有源晶振作为时钟信号,控制信号W_CLK,FQ_UD,RESET,strobe
触发器74HCT574连接芯片,并对这四个信号使用上拉电阻接至电源.另外使用电容对电源进行滤波.
通用端口P1连接D7-D0,并行加载控制字,P4.5分配给W_CLK,P4.6分配给FQ_UD,P4.7分配给RESET,P0.1分配给strobe.控制字用五个W_CLK信号分五次加载,然后用FQ_UD更新即可.RESET是复位信号,连接中使用了74HCT574,Strobe触发使得信号确保送达AD9851.
一开始,我们手工焊制了两块电路板,得到波形效果十分不好.于是订制了PCB板,采用altiumdesigner绘制,注意元件布局可以获得更好地效果,比如对电源处理地电容要离电源引脚近一点,晶振离芯片近一点.altiumdesigner我们之前也没有使用过,都是从零开始学习地.覆铜前截图如下:
根据AD9851地技术手册,可以得知芯片并行工作地时序如下:
在系统时钟(本设计选用了30MHZ)控制下,给芯片5个W_CLK上升沿信号可以依次加载五个八位控制字,W0控制相位,电源,以及工作模式,W1,W2,W3,W4依次是加载频率地高位到低位.加载完控制字,单片机给出一个FQ_UD信号上升沿,即可更新控制字到AD9851寄存器中,之后芯片就可以按照写入地控制字进行工作.
软件部分工作较为简单,就是将键盘输入地频率加载到寄存器,注意信号时序即可.
这部分难点在于硬件电路地焊接,由于芯片较小,容易烧坏,以及工作频率较高等原因,对于电路地焊接技术提出了较高地要求.
(单片机代码见附录)
二、信号处理模块电路设计:
1.外部信号处理模块基本原理及框图
外部信号处理模块主要完成对DDS芯片所产生地正弦波进行整波、变波以得到所需要波形地功能,起主要原理是先对DDS芯片所产生地正弦波进行放大,得到一定幅值地正弦波,将此正弦波接入比较器可以产生相同频率地方波,对方波进行占空比和偏置地调整后,通过积分电路,可以产生相应地三角波和锯齿波;对得到地正弦波、方波、三角波和锯齿波进行选择后接入调幅电路可以得到可调幅值地正弦波,方波,三角波或者锯齿波.其原理框图如下图6所示:
2.各模块电路设计与仿真结果
1)正弦波整波模块
该模块是一个由运放组成地放大电路,其功能一方面将AD9851芯片输出地电流信号转化为电压信号,另一方面是得到峰-峰值为1V地正弦波信号,其电路图及参数如下图所示:
该模块仿真波形如下图所示:
2)方波产生模块
该模块包括一个比较器电路和一个运算放大电路,其中比较器电路用以将正弦波转化为方波,同时可以调节方波地占空比;运算放大电路用以将得到地方波进行一定地放大,以得到一定幅值地方波,同时可以给方波增加一个直流偏置,其电路图如下图所示:
由正弦波得到方波地仿真波形如下图所示:
3)三角波和锯齿波产生模块
该模块包括一个可选电容积分电路和一个可选电阻放大电路组成;其中,积分电路地作用是由方波积分得到三角波或者改变方波地占空比和偏置后积分得到锯齿波,积分电容有一个7选1电路进行选择,对于不同地频率段,选择不同地电容进行积分;选择电路有CD4051芯片组成;放大电路地作用是为了补偿因频率变化而导致幅值地变化,以得到幅值比较稳定地三角波或锯齿波,同样使用CD4051芯片组成八选一电路,其电路图如下图所示:
由正弦波得到三角波地仿真波形如下图:
由正弦波得到锯齿波地仿真波形如下图所示:
4)幅值调节模块
该模块主要是由运放组成地可变倍数放大电路,对输入地峰-峰值为1V地正弦波、方波、三角波或锯齿波进行放大或是衰减,以调节最终输出波形地幅值,其输入端为二选一电路,由模拟单刀双掷开关ADG849实现,当需要峰-峰值小于1V时,开关置于衰减端,即端口1,当需要峰-峰值大于1V时,开关置于放大端,即端口2,然后通过调节反馈电阻地阻值,就能够达到调节波形峰-峰值地目地;其设计电路图如下图所示:
5)整机电路图:
附录:
单片机代码
主函数:
#include"./lcd/AD9851.h"
#include"./lcd/keyb.h"
#include"irq_arm.c"
#include"./lcd/OCM12864.h"
intmain(void)
{
uchark。
lcd_initial()。
clear_screen(0)。
disp_chn(10,0,4)。
//正
disp_chn(30,0,5)。
//弦
disp_chn(50,0,6)。
//波
disp_str(80,3,"HZ")。
//"HZ"
disp_str(80,5,"V")。
//"V"
writecom_AD()。
while
(1)
{
kbscan()。
keyshow()。
}
}
AD9851模块:
头文件ad9851.h
#ifndef__AD9851
#define__AD9851
typedefunsignedcharuchar。
typedefunsignedlongintulint。
#definead_data_outGP1DAT//P1
#definead_data_in(char)(GP1DAT&0x000000FF)
#defineocm_DOUT_ENGP1DAT=(GP1DAT|0xFF000000)
#defineocm_DIN_ENGP1DAT=(GP1DAT&0x00FFFFFF)
#defineset_W_CLK()GP4DAT=(GP4DAT|0x20200000)//P4.5
#defineclear_W_CLK()GP4DAT=(GP4DAT|0x20000000)&(~0x00200000)
#defineset_FQ_UD()GP4DAT=(GP4DAT|0x40400000)//P4.6
#defineclear_FQ_UD()GP4DAT=(GP4DAT|0x40000000)&(~0x00400000)
#defineset_RESET()GP4DAT=(GP4DAT|0x80800000)//P4.7
#defineclear_RESET()GP4DAT=(GP4DAT|0x80000000)&(~0x00800000)
#definestrobe()GP0DAT=(GP0DAT|0x02020000)//P0.1
#defineclearstrobe()GP0DAT=(GP0DAT|0x02000000)&(~0x00020000)
////*****函数声明****////
voiddelay_AD(unsignedinti)。
voidchang_AD(ulintt)。
voidwritecom_AD(void)。
#endif
#include"OCM12864.h"
#include"AD9851.h"
externfloatFin。
/***f是输出地频率***//***32位控制字为:
Fm=(f*0xFFFFFFFF)/内部时钟***/
ulintFm=0x00000000。
ucharadtab[]={0x01,0x00,0x09,0x00,0x00}。
/**(w0)(w4w3w2w1)高--低**/
/***ad9851延时函数***/
voiddelay_AD(unsignedinti)
{
while(i--)。
}
/***分解32位数据为四个字节***/
voidchang_AD(unsignedlongintt)
{
unsignedlongint*i。
i=&t。
adtab[1]=(char)*i。
adtab[2]=(char)*(i+1)。
adtab[3]=(char)*(i+2)。
adtab[4]=(char)*(i+3)。
}
voidwritecom_AD(void)
{
uchari。
Fm=((Fin*0xFFFFFFFF)/12000000)。
clearstrobe()。
clear_FQ_UD()。
delay_AD
(1)。
clear_W_CLK()。
delay_AD
(1)。
set_RESET()。
strobe()。
delay_AD
(1)。
//上电复位一次
clearstrobe()。
clear_RESET()。
strobe()。
delay_AD
(1)。
chang_AD(Fm)。
for(i=0。
i<5。
i++)
{
clearstrobe()。
clear_W_CLK()。
strobe()。
clearstrobe()。
ad_data_out=(adtab[i]<<16)|0xFF000000。
delay_AD
(1)。
set_W_CLK()。
strobe()。
}
clearstrobe()。
set_FQ_UD()。
strobe()。
delay_AD
(1)。
clearstrobe()。
clear_FQ_UD()。
strobe()。
delay_AD
(1)。
}
键盘模块:
头文件keyb.h
#ifndef__key16
#define__key16
#include"OCM12864.h"
typedefunsignedcharuchar。
typedefunsignedintuint。
#definekbdataGP2DAT//P2
voiddelayms(uchara)。
voidkbscan(void)。
voidkeyshow(void)。
#endif
#include"keyb.h"
ucharkeytab[4][4]={0x01,0x02,0x03,'a',0x04,0x05,0x06,'b',0x07,0x08,0x09,'c','*',0x00,'#','d'}。
//16个键值
intkeypressed。
//是否有键按入
ucharkeyvalue。
//按入地键值
ucharkeyinput[10]。
//输入地10位数据
floatFin。
//输入频率
intkeyi=0,keya=0,keyb=0,keyc=0。
//keyi键入地第i位,keya键入a地次数%4,keyb键入b地次数%4,keyc键入c地次数%4
uinthang=6,lie=6。
//键入地行列
/*********延时程序****************/
voiddelayms(uchara)
{
uinti。
while(a--)
{for(i=1000。
i>0。
i--)。
}
}
/**********键盘扫描*********************/
voidkbscan(void)
{
uinttemp,hang0=5,lie0=5。
kbdata=0x0f0000f0。
if(kbdata!
=0x0f0000f0)
{
delayms(25)。
kbdata=0x0f0000f0。
if(kbdata!
=0x0f0000f0)//判断行
{
keypressed=1。
temp=kbdata。
switch(temp&0x0f0000f0)
{
case0x0f000070:
lie0=3。
break。
case0x0f0000b0:
lie0=2。
break。
case0x0f0000d0:
lie0=1。
break。
case0x0f0000e0:
lie0=0。
break。
}
}
else
keypressed=0。
kbdata=0xf000000f。
if(kbdata!
=0xf000000f)
{
keypressed=1。
temp=kbdata。
switch(temp&0xf000000f)
{
case0xf000000e:
hang0=0。
break。
case0xf000000d:
hang0=1。
break。
case0xf000000b:
hang0=2。
break。
case0xf0000007:
hang0=3。
break。
}
}
else
keypressed=0。
if((hang==hang0)&&(lie==lie0))
{
keypressed=0。
}
else
{
hang=hang0。
lie=lie0。
keyvalue=keytab[hang][lie]。
}
}
}
/************按键显示*********************/
voidkeyshow(void)
{
inti=0,j=0。
//intflag=0。
if(keypressed)
{
switch(keyvalue)
{
case0x00:
disp_word(10+keyi*7,3,'0')。
//0
keyinput[keyi]=0。
if(keyi<9)keyi++。
break。
case0x01:
disp_word(10+keyi*7,3,'1')。
//1
keyinput[keyi]=1。
if(keyi<9)keyi++。
break。
case0x02:
disp_word(10+keyi*7,3,'2')。
//2
keyinput[keyi]=2。
if(keyi<9)keyi++。
break。
case0x03:
disp_word(10+keyi*7,3,'3')。
//3
keyinput[keyi]=3。
if(keyi<9)keyi++。
break。
case0x04:
disp_word(10+keyi*7,3,'4')。
//4
keyinput[keyi]=4。
if(keyi<9)keyi++。
break。
case0x05:
disp_word(10+keyi*7,3,'5')。
//5
keyinput[keyi]=5。
if(keyi<9)keyi++。
break。
case0x06:
disp_word(10+keyi*7,3,'6')。
//6
keyinput[keyi]=6。
if(keyi<9)keyi++。
break。
case0x07:
disp_word(10+keyi*7,3,'7')。
//7
keyinput[keyi]=7。
if(keyi<9)keyi++。
break。
case0x08:
disp_word(10+keyi*7,3,'8')。
//8
keyinput[keyi]=8。
if(keyi<9)keyi++。
break。
case0x09:
disp_word(10+keyi*7,3,'9')。
//9
keyinput[keyi]=9。
if(keyi<9)keyi++。
break。
case'#':
disp_word(10+keyi*7,3,'.')。
//.
keyinput[keyi]='.'。
if(keyi<9)keyi++。
break。
case'*':
for(i=0。
i<10。
i++)
{
disp_str(10+i*7,3,"")。
}
keyi=-1。
break。
case'a':
keya++。
if(keya==4)
{
keya=0。
}
if(keya==0)
{
disp_chn(10,0,4)。
//正
disp_chn(30,0,5)。
//弦
disp_chn(50,0,6)。
//波
break。
}
elseif(keya==1)
{
disp_chn(10,0,7)。
//方
disp_str(30,0,"")。
disp_str(35,0,"")。
disp_str(40,0,"")。
disp_chn(50,0,6)。
//波
break。
}
elseif(keya==2)
{
disp_chn(10,0,8)。
//三
disp_chn(30,0,9)。
//角
disp_chn(50,0,6)。
//波
break。
}
elseif(keya==3)
{
disp_chn(10,0,10)。
//锯
disp_chn(30,0,11)。
//齿
disp_chn(50,0,6)。
//波
break。
}
case'b':
keyb++。
if(keyb==3)
{
keyb=0。
}
if(keyb==0)
{
disp_str(80,3,"HZ")。
break。
}
elseif(keyb==1)
{
disp_str(80,3,"KHZ")。
break。
}
elseif(keyb==2)
{
disp_str(80,3,"MHZ")。
break。
}
case'c':
keyc++。
if(keyc==2)
{
keyc=0。