找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 2281|回复: 0
打印 上一主题 下一主题
收起左侧

基于单片机+AD9850 DDS的波形发生器设计 附程序

[复制链接]
跳转到指定楼层
楼主
ID:913669 发表于 2021-5-7 23:11 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

频率合成技术是将一个(或多个)基准频率变换成另一个(或多个)合乎质量要求的所需频率的技术。在通信、雷达、导航、电子侦察、干扰与抗干扰等众多领域都有应用。随着各种频率合成器和频率合成方案的出现,频率合成技术得到了不断的发展。

1971年3月美国学者J.Tierncy,C.M.Rader和B.Gold首次提出了直接数字频率合成(DDS__Direct Digital Synthesis)技术。这是一种从相位概念出发直接合成所需要的波形的新的全数字频率合成技术。同传统的频率合成技术相比,DDS技术具有极高的频率分辨率、极快的变频速度,变频相位连续、相位噪声低,易于功能扩展和全数字化便于集成,容易实现对输出信号的多种调制等优点,满足了现代电子系统的许多要求,因此得到了迅速的发展。

目前市面上的DDS芯片,价格昂贵、功能固定单一,应用受到限制。本综合实验项目采用基于FPGA的EDA技术设计实现DDS芯片,并可以根据实际需要对其功能进行灵活地修改,配置。


1.2 DDS 工作原理

   一个纯净的单频信号可表示为:
                            (2-1)
只要它的幅度U和初始相位不变,它的频谱就是位于的一条谱线。为了分析简化起见,可令U=1,=0,这将不会影响对频率的研究。即:
                                    (2-2)
如果对(2-2)的信号进行采样,采样周期为(即采样频率为),则可得到离散的波形序列:
                                (2-3)
相应的离散相位序列为:
                            (2-4)
式中:
                            (2-5)
是连续两次采样之间的相位增量。根据采样定理:
                                       (2-6)
只要从(2-3)出来的离散序列即可唯一的恢复出(2-2)的模拟信号。从(2-2)可知,是相位函数的斜率决定了信号的频率;从(2-5)可知,决定相位函数斜率的是两次采样之间的相位增量。因此,只要控制这个相位增量,就可以控制合成信号的频率。现将整个周期的相位2分成M份,每一份为,若每次的相位增量选择为的K倍,即可得到信号的频率:
                                           (2-7)
相应的模拟信号为:
                                       (2-8)
式中K和M都是正整数,根据采样定理的要求,K的最大值应小于M的1/2。
综上所述,在采样频率一定的情况下,可以通过控制两次采样之间的相位增量(不得大于π)来控制所得离散序列的频率,经保持、滤波之后可唯一的恢复出此频率的模拟信号。
DDS工作原理框图如图2.1所示:
图2.1 DDS原理框图
其实质是以基准频率源(系统时钟)对相位进行等间隔的采样。由图2.1见,DDS 由相位累加器和波形存储器(即,ROM查询表)构成的数控振荡器(NCO_ Numerically Controlled Oscillators)、数模转换器(DAC)以及低通滤波器(LPF)三部分组成。在每一个时钟周期,N位相位累加器与其反馈值进行累加,其结果的高L位作为查询表的地址,然后从ROM中读出相应的幅度值送到DAC。再由DAC将其转换成为阶梯模拟波形,最后由具有内插作用的LPF将其平滑为连续的正弦波形作为输出。因此,通过改变频率控制字K就可以改变输出频率
在这里
由上面的分析可得DDS的输出频率:
                              (2-9)
由上式可知,DDS的最小输出频率为:
                                        (2-10)
DDS的频率分辨率为:
                                    (2-11)
DDS频率输入字的计算:
FW(N-1:0) = 2Nf0/fc                      (2-12)
1.3 DDS基本结构组成
一个基本的DDS系统由数控振荡器(NCO)、数模转换器(DAC)和低通滤波器(LPF)三部分构成,如图3.1所示:
图3.1  DDS的基本结构
数控振荡器(NCO)产生频率可控制的数字正弦载波,通过数模转换器(DAC)得到模拟正弦波,最后经过低通滤波器(LPF)除去各种干扰信号。
本实验项目中的设计主要针对数控振荡器(NCO)部分,DAC部分直接采用实验系统箱提供的数/模转换电路。
1.4 DDS的设计

在DDS的设计中其最基本的构件是相位累加器和波形存贮器。通常也可在波形存贮器前面加一个相位调制器,使其具有相位调制的功能,为了防止频率控制字、相位控制字改变时干扰相位累加器和相位调制器的正常工作,分别在这两个模块前面加入了两组寄存器,从而灵活且稳定地控制频率字和相位字的输入。如图4.1所示:

图中相位累加器(phasea)是整个DDS的核心,在这里完成相位累加功能,其输入是相位增量,又可称为频率控制字,由于与输出频率是简单的线性关系:
                            (4-1)
事实上当基准时钟时, 就等于

相位调制器(phasemod)接收相位累加器的相位输出,在这里加一个相位偏移值,主要用于实现信号的相位调制,如PSK(相移键控)等,在不使用时可以去掉该部分,或加一个固定的相位控制字。

波形存储器(即,正弦ROM查找表)(sinlup)把存储在相位累加器中的抽样值转换成正弦波幅度的数字量函数,可理解为相位到幅度的转换。它的输入是相位调制器输出的高M位(而并非全部N位)值,将其作为正弦ROM查找表的地址值;查询表把输入的地址相位信息映射成正弦波幅度信号;输出送往DAC,转化为模拟信号。


2    AD9850简介

2.1芯片性能

随着数字技术的飞速发展,用数字控制方法从一个参考频率源产生多种频率的技术,即直接数字频率合成(DDS)技术异军突起。美国AD公司推出的高集成度频率合成器AD9850便是采用DDS技术的典型产品之一。AD9850采用先地蝗CMOS工艺,其功耗在3.3V供电时仅为155mW,扩展工业级温度范围为-40~80℃,采用28脚SSOP表面封装形式。AD9850的引脚排列,图2为其组成框图。图2中层虚线内是一个完整的可编程DDS系统,外层虚线内包含了AD9850的主要组成部分。AD9850内含可编程DDS系统和高速比较器,能实现全数字编程控制的频率合成。可编程DDS系统的核心是相位累加器,它由一个加法器和一个N位相位寄存器组成,N一般为24~32。每来一个外部参考时钟,相位寄存器便以步长M递加。相位寄存器的输出与相位控制字相加后可输入到正弦查询表地址上。正弦查询表包含一个正弦波周期的数字幅度信息,每一个地址对应正弦波中0°~360°范围的一个相位点。查询表把输入地址的相位信息映射成正弦波幅度信号,然后驱动DAC以输出模式量。相位寄存器每过2N/M个外部参考时钟后返回到初始状态一次,相位地正弦查询表每消费品一个循环也回到初始位置,从而使整个DDS系统输出一个正弦波。输出的正弦波周期To=Tc2N/M,频率fout=Mfc/2N,Tc、fc分别为外部参考时钟的周期和频率。AD9850采用32位的相位累加器将信号截断成14位输入到正弦查询表,查询表的输出再被截断成10位后输入到DAC,DAC再输出两个互补的电流。DAC满量程输出电流通过一个外接电阻RSET调节,调节关系为ISET=32(1.148V/RSET),RSET的典型值是3.9kΩ。将DAC的输出经低通滤波后接到AD9850内部的高速比较器上即可直接输出一个抖动很小的方波。AD9850在接上精密时钟源和写入频率相位控制字之间后就可产生一个频率和相位都可编程控制的模拟正弦波输出,此正弦波可直接用作频率信号源或经内部的高速比较器转换为方波输出。在125MHz的时钟下,32位的频率控制字可使AD9850的输出频率分辨率达0.0291Hz;并具有5位相位控制位,而且允许相位按增量180°、90°、45°、22.5°、11.25°或这些值的组合进行调整。流程如图(2)。

2 .2AD9850 的控制字及控制时序
AD9850 的控制字有 40 位 ,其中 32 位是频率控制位 ,5 位是相位控制位 ,1 位是电源休眠控制位 ,2 位是工作方式选择控制位。在应用中 ,工作方式选择位设为00 ,因为 01 ,10 ,11 已经预留作为工厂测试用。频率控制位可通过下式计算得到:f out = ( f r ×W) / 232其中: f out 要输出的频率值; f r 为参考时钟频率;W 为相应的十进制频率控制字, 然后转换为十六进制即可。AD9850 有串行和并行两种控制命令字写入方式。其中串行写入方式是采用 D7 作为数据输入端 ,每次W_CL K的上升沿把一个数据串行移入到输入寄存器40 位数据都移入后 ,FQ_UD 上升沿完成输出信号频率和相位的更新。串行控制字的写入时序如图 3 所示。但是要注意的是 ,此时数据输入端的三个管脚不可悬空 ,其中D0 ,D1 脚接高电平 ,D2脚要接地。


                           图(3)
2.3管脚定义

3 硬件部分
3.1基于AD9850的模块原理图

3.2硬件电路设计
AD9850 控制字的写入方式有串行和并行两种。并行写入方式的优点是数据传输的速度快 ,能够提升整个系统的处理速度 ,但占用的单片机的 I/ O 口资源太多。与并行方式相比 ,串行写入方式在数据传输的速度上要慢些 ,但它更大优点是能节省很多 I/ O 口资源[8 ]。所以 ,本系统采用A T89S52 单片机作为控制核心 ,通过串行写入控制字的方式控制 AD9850 芯片 ,加上键盘和L ED 显示部分等外围电路 ,构成整个系统电路。为了详细介绍 AD9850 的用法 ,这里重点给出本系统中A T89S52 单片机与 AD9850 芯片连接电路 ,如图 4 所示 ,其中 R1 = 1 kΩ, R2 = 10kΩ, R3 =1kΩ,单片机晶振选用12 MHz ,电容采用 30 p F 经典值。单片机采用12 MHz晶振时 ,它的高电平时间能够满足 AD9850 。


            图(4)
4软件部分
4.1 软件部分设计
软件程序的功能就是通过程序使整个系统按照人们的设想要求工作起来 ,本系统中最主要的部分就是将AD9850 的 40 位控制字通过单片机写入到 AD9850 芯片内 ,系统的程序流程图如图 4 所示。要根据写入控制字方式的不同严格按照 AD9850 的时序图来编写控制字写入子程序。本文主要给出串行写入方式的C源程序以供读者调试参考。

4.2参考程序


  1. /*******************************************
  2. 函数:关于AD9850的DDS调频
  3. ********************************************/
  4. #include <reg52.h>
  5. #include <intrins.h>
  6. #include <string.h>
  7. #define uchar unsigned char
  8. #define uint unsigned int
  9. #define WRITE_DATA_IO P1                               //定义AD9850数据端口
  10. #define LED_DATA_IO P0                                             //定义LED灯数据端口
  11. Unsigned char code duanma[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x80};                            //设置数码管段码0-9
  12. unsigned char code weima[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};                                                                                    //设置数码管位码
  13. uchar WRITE_DATA[5];   //9850频率和控制字,WRITE_DATA[5]为控制字,其他32位为频率字
  14. uchar display_data[8];                            //8位数码管的数值
  15. unsigned long frequence;              //无符号长整形数值,我们需要输入的频率
  16. uint MHZ, KHZ , HZ;                                          //将需要的频率分解为MHZ,KHZ,HZ
  17. uchar AD9850_temp;                                                                      //AD9850工作标志位

  18. sbit W_SLK = P2^0;                                          //AD9850字节输入,上升沿有效
  19. sbit DQ_VD = P2^1;                                          //AD9850四字节输入后,上升沿有效
  20. sbit key_unit = P2^2;              //按键的个位
  21. sbit key_decade = P2^3;              //按键的十位
  22. sbit key_power = P2^4;              //按键的十次幂
  23. sbit key_sure = P2^5;   //按键确认
  24. sbit weila = P2^6;                            //LED数码管的位码
  25. sbit duanla = P2^7;                            //LED数码管的段码
  26. /*******************************************
  27. 函    数:延时函数,延时tms
  28. 入口函数:无
  29. 出口频率:无
  30. ********************************************/
  31. void delay1ms(uchar t)
  32. {
  33.               uchar tt;
  34.               while(t--)
  35.               {            
  36.                             tt= 125;
  37.                             while(tt--);
  38.               }
  39. }
  40. /*************************LED显示函数**************************************/

  41. /*******************************************
  42. 函    数:初始化定时器0
  43. 入口函数:无
  44. 出口频率:无
  45. ********************************************/
  46. void INIT_time0()
  47. {
  48.               TMOD |= 0x01;
  49.               TH0 = -2000 / 256;
  50.               TL0 = -2000 % 256;
  51.               EA = 1;
  52.               ET0 = 1;
  53.               TR0 = 1;
  54. }
  55. /*******************************************
  56. 函    数:LED数码管在start位开始显示num数据
  57. 入口函数:start是数据从第几位显示,num是显示多少位
  58. 出口频率:无
  59. ********************************************/
  60. void LED_display(uchar start, uchar num)
  61. {
  62.               static unsigned char i = 0;
  63.               uchar delay = 15;
  64.   if(AD9850_temp == 1)
  65.   {
  66.               display_data[0] = MHZ / 10;                                          //将频率分解让数码管显示
  67.               display_data[1] = MHZ % 10;
  68.               display_data[2] = KHZ / 100;
  69.               display_data[3] = KHZ % 100 / 10;
  70.               display_data[4] = KHZ % 10;
  71.               display_data[5] = HZ / 100;
  72.               display_data[6] = HZ % 100 / 10;
  73.               display_data[7] = HZ % 10;
  74.   }
  75.               LED_DATA_IO = 0xed;                                                        //显示两个小数点
  76.               weila = 1;                   //位码锁存
  77.               weila = 0;
  78.             
  79.               LED_DATA_IO = duanma[10];
  80.               duanla = 1;
  81.               duanla = 0;            
  82.               while(delay--);
  83.               LED_DATA_IO = weima[i+start];
  84.               weila = 1;
  85.               weila = 0;
  86.             
  87.               LED_DATA_IO = duanma[display_data[i]];
  88.               duanla = 1;
  89.               duanla = 0;
  90.               i++;
  91.               if(i == num)
  92.                             i = 0;               
  93. }
  94. /*******************************************
  95. 函    数:中断函数
  96. 入口函数:无
  97. 出口频率:无
  98. ********************************************/
  99. void INT_time0() interrupt 1
  100. {
  101.                  TH0 = -2000 / 256;
  102.               TL0 = -2000 % 256;
  103.               LED_display(0, 8);
  104. }
  105. /*************************按键函数*************************************/
  106. /*******************************************
  107. 函    数:按键初始化,确定频率的大小
  108. 入口函数:无
  109. 出口频率:无
  110. ********************************************/
  111. void INIT_key()              reentrant                 //可从入函数
  112. {
  113.               uchar unit = 0,decade = 0, power = 0, i;
  114.               AD9850_temp = 0;                                                                                                                //AD9850开始标志位关闭
  115.               memset(display_data, 0, 8);
  116.               while(1)
  117.               {
  118.                             if(key_unit == 0)                                                                      //个位键
  119.                             {
  120.                                           delay1ms(20);                                                           //延时,防抖动
  121.                                           if(key_unit == 0)
  122.                                           {
  123.                                                         while(key_unit == 0);
  124.                                                         unit++;
  125.                                                         if(unit == 10 )
  126.                                                                       unit = 0;

  127.                                                                       display_data[6] = unit;
  128.                                           }
  129.                             }

  130.                             if(key_decade == 0)                                                                                    //十位键
  131.                             {
  132.                                           delay1ms(20);
  133.                                           if(key_decade == 0)
  134.                                           {
  135.                                                         while(key_decade == 0);
  136.                                                         decade++;
  137.                                                         if(decade == 10)
  138.                                                                       decade = 0;

  139.                                                                       display_data[5] = decade;
  140.                                           }
  141.                             }

  142.                             if(key_power == 0)                                                                                       //10次幂键
  143.                             {
  144.                                           delay1ms(20);
  145.                                           if(key_power == 0)
  146.                                           {
  147.                                                         while(key_power == 0);
  148.                                                         power++;
  149.                                                         if(power == 7)
  150.                                                                       power = 0;
  151.                                                                      
  152.                                                                       display_data[7] = power;
  153.                                           }
  154.                             }

  155.                             if(key_sure == 0)                                                                                      //确定键
  156.                             {
  157.                                           delay1ms(20);
  158.                                           if(key_sure == 0)
  159.                                           {
  160.                                                         while(key_sure == 0);
  161.                                                         break;
  162.                                           }
  163.                             }                           
  164.               }

  165.               frequence = unit + 10*decade;                                                          //将按键得到的数值进行变换,得到我们需要的频率
  166.               for(i = 1; i <= power; i++)
  167.                             frequence *= 10;
  168.                            
  169.                             if(frequence >= 20000000)                                                        //由于AD9850的频率范围是1——20MHZ,所以超过了就重新输入
  170.                             {
  171.                                           frequence = 0;
  172.                                           INIT_key();
  173.                             }

  174.                             MHZ = frequence / 1000000;                                                        //将频率分解位MHZ,KHZ,HZ,主要是为了在中断中计算量少些,以免影响中断效果
  175.                             KHZ = frequence % 1000000 / 1000;
  176.                             HZ = frequence %1000;
  177.             
  178.               AD9850_temp = 1;                                                                                      //AD9850开始标志位开启
  179. }

  180. /*************************AD9850函数**************************************/
  181. /*******************************************
  182. 函    数:进行9850的DDS函数发生,写入控制字,写入频率字
  183.           从高字往低字写
  184. 入口函数:需要的频率
  185. 出口频率:无
  186. ********************************************/
  187. void AD9850_WRITE()
  188. {
  189.               unsigned long temp;
  190.               char i;                           
  191.               W_SLK = 0;
  192.               DQ_VD = 0;

  193.                 temp = (unsigned long)(34.359738368 * frequence);
  194.               for(i = 0; i < 4; i++)
  195.               WRITE_DATA[i] = temp >> (i * 8);

  196.               for(i = 4; i >= 0; i--)
  197.               {
  198.                             W_SLK = 0;
  199.                             WRITE_DATA_IO = WRITE_DATA[i];
  200.                             W_SLK = 1;
  201.                             _nop_();
  202.               }
  203.               DQ_VD = 1;
  204. }
  205. /*************************主函数**************************************/
  206. /*******************************************
  207. 函    数:主函数,完成对全局的操控
  208. 入口函数:无
  209. 出口频率:无
  210. ********************************************/
  211. void main()
  212. {
  213.               INIT_time0();                                          //初始化时间中断
  214.               while(1)
  215.               {
  216.                             if(key_sure == 0)
  217.                             {
  218.                                           delay1ms(20);
  219.                                           if(key_sure == 0)
  220.                                           {
  221.                                                         while(key_sure == 0);
  222.                                               INIT_key();
  223.                                                         AD9850_WRITE();
  224.                                           }
  225.                             }
  226.               }
  227. }
复制代码

5波形图
  1.9MHZ正弦波
   


1HZ方波
  

1HZ正弦波

1MHZ方波
1MHZ实物
1MHZ正弦波
  

6结束语
    本模块学习是基于AD9850的DDS信号发生器的基础上,通过了解AD9850的工作原理和工作要求来完成正弦波.矩形波等波形在系统时钟125M的条件下的学习。
   本次学习得到了精度很高的正弦波和矩形波,其占空比为46.5%已经很接近预期的结果。

以上的Word格式文档51黑下载地址:
10基于DDS的波形发生器设计 1005014107刘艳珍.doc (1.47 MB, 下载次数: 29)

1005014117-张志超.doc

595 KB, 下载次数: 18, 下载积分: 黑币 -5

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏4 分享淘帖 顶 踩
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表