原创 【博客大赛】零基础学FPGA (二十八)频、相可调,任意波形信号发生器系统设计

2015-4-22 09:55 5869 16 29 分类: FPGA/CPLD 文集: FPGA/CPLD

        最近接了一个项目吧,是我们学校物理院院长带的研究生搞的,小墨有幸跟他们合作,负责FPGA方面的工作,完成后据说还会申请国家专利,具体到什么时候完成,那可能就是猴年马月了,或者说我已经不在学校了。从今天开始,小墨将开始接触赛灵思公司的FPGA(老师提供的平台),用到的当然是SOPC。其实做做项目也好,让自己锻炼一下,我也好久没有做大一点的项目了,对我来说也是一个机会吧。

        已经买好了去黄山的车票,这个季节的黄山应该很美吧,4月29号出发,又一次令人心潮澎湃的旅行~

20116121218504082.jpg

       信号发生器这个东西相信大家都知道,关于基于DDS信号发生器的技术文档网上也多的是,但是我还是想写一下这部分的教学,因为从我自身的学习来看,这部分内容并不是很难,也很容易实现,代码也就那几行。但是,我发现我当时学这部分的时候,从网上找资料,大部分都是基于原理的讲解,或者说只是做到仿真这一步,而且原理的讲解太过书面化,初学者不怎么好理解。我做了这么一个教程,全方面的做一个这样一个系统。其中包括信号发生器的原理部分,代码实现,仿真,按键控制频率、相位变换,包括正弦波、方波、锯齿波、三角波的产生,以及D/A转换芯片的操作,直到能够在示波器上观察到我们产生的波形,并通过按键控制为止。

       这个系统的设计大概花了我三四天的样子,写的也是蛮用心的,工程不是很大,但还是有些细节需要注意的,下面我们就一步步的开始做。

一、整体框架

        关于原理部分,书上讲的很多,但是总是感觉不尽人意,过于书面化的语言让人看着很头疼,下面就让小墨来给大家解释,希望给读者带来一种眼前一亮的感觉,让你再回去看书,看代码的时候觉得得心应手了。

        首先我们应该先明确要做什么,我们要做的是一个频率,相位可调,任意波形信号发生器,也就是我们常见的正弦波,方波,锯齿波,三角波等。

        其次,我们需要知道我们需要哪些参数。比如,我们要生成一个正弦波,它的初始频率是多少,相位是多少,步进频率、相位是多少,怎么计算这些参数?

        然后我们需要知道如何生成一种波形,怎么样通过按键实现波形与波形之间的转换,例如我按下一个键生成正弦波,再按下一个键生成三角波,怎么实现?

        再然后,我们需要把生成的数字信号送入D/A转换芯片,D/A转换的接口怎么写?怎么保证采集的数字信号完全正确?

        最后,我们可以通过示波器观察我们生成的波形信号,验证我们的频率,相位是否符合我们的设计要求

       下面是我简单的做的一个框架图

360桌面截图20150420114304.jpg
 
      下面我们先来解释一下上面这张框架图。
      首先,我们通过8位按键选择输出何种波形,这时候wave_select信号被赋予相应的值送给DDS模块,DDS模块由wave_select信号,从ROM中选择合适的地址,地址每变化一次,也就是数据每变化一次,DDS模块会告知DAC模块数据发生变化了,让它注意数据的采集,不要采错了!同时可以通过按键控制模块调节输出信号的频率,相位等,DAC模块将采集到的数字信号转化成模拟信号,送到示波器上显示。注意DAC芯片的采样频率,DDS信号的输出频率不能大于采集频率,否则数据就会出错。
 
二、DDS信号发生器原理详解
 
       关于发生器原理这样部分有必要好好讲讲,很多人还是对某系问题不得其解,先来看这张图
360桌面截图20150420124752.jpg
 
     DDS基本的结构组成,看图就知道了,包括相位累加器,相位调制器,波形数据表,和DA转换。我们还可以看到有两个输入,即频率控制字输入fword 和相位控制字输入 pword,这两个输入就是我们用来控制输出波形频率和相位的
 
     1、相位累加器

           相位累加器的原理,我们先假设频率控制字fword 为1。相位累加器的原理就是先将fword的值送到相位累加器,然后每来一个时钟,相位累加器的输出值,就跟相位累加器的新输入值相加,之后再送入相位累加器,再来一个时钟,再跟输入值相加送进去,如此循环。例如刚开始fword = 1 ,那么第一个时钟周期相位累加器的输出就是1,第二个时钟周期输出的就是2,第三个时钟周期输出的就是3。再例如,我们的频率控制字fword 刚开始等于2,那么相位累加器输出的就依次是0,2,4.....也就是说频率控制字fword 越大,相位累加器的输出值间隔也就越大,那么我们假设相位累加器的输出是32位的,如果fword越大,那么频率控制字记完到2^32 的时间就越短。这样的话,我们来算一下

      我们使用的是50M的晶振,周期为20ns,假设fword为1,相位累加器的输出为N位的,那么每20ns,相位累加器加1,要加到2^N,需要20ns x 2^N 时间,这个时间就是输出一个完整信号的周期,那么我们可以知道,输出信号的频率为 Fout = Fclk /2^N,其中,Fclk为我们的晶振频率,再假如,fword = B 的时候,相位间隔提高 B 倍,因此计满一个周期的时间缩小了 B 倍,频率提高的 B 倍。综上所述,我们得出了输出信号的频率计算公式

360桌面截图20150420131103.jpg
 
      有了这个公式,那么,如果我们把2^N看成是一个周期波形的相位,也就是说把一个波形的相位平均分成2^N个,每一个相位对应一个数字信号,将这些数字信号送到DA转换芯片,转化成模拟信号,不就是我们的信号发生器了吗?
 
     好,这里我们假设N = 32 ,也就是把一个波形的相位分成了2^32个点,但是位数越多当然占用的资源也就越多,不能取那么多,怎么办呢,我们只取它的高8位即可
 
360桌面截图20150420132004.jpg
 
即取fre_add[31:24] ,将这8位数送给ROM的地址。很多人不明白了,为什么这样取8位,这样取8位不是把一个完整的信号给截断了吗?刚开始我也有这么想过,但是仔细算一下才知道自己是多虑了。用计算器算一下知道,2^32 = 4.3x 10^9 左右,而2^24 = 16 x 10^6 左右,前后差了几个数量级,所以,我们取fre_add[31:24],相当于把一个波形的相位分成了256个点,每个点对应一个数据。
 
二、ROM
 
       上一部分说了,我们将一个波形分成了256个点,每个点对应一个数据,那么我们怎么实现呢,这里就要用到我们的只读存储器ROM了,我们可以先把波形的数据送到ROM里存起来,然后再从中取,取的地址就是我们的fre_add[31:24]这8位数,由此看来,fword越大,这256个地址取址的间隔就越大,相应的波形频率也就越大了,当然,我们的ROM不光存储一个波形,我们可以把ROM设大一点,里面放多种波形,再通过按键进行相应的选址,从而输出各种波形
 
360桌面截图20150420133343.jpg
 
 
    说到这里有些人该问了,怎么将数据送到ROM里去呢?这个的话就设计到mif文件的制作,具体怎么做大家还是自己回去补课了,不一一介绍了,但是我这里有一个mif文件生成器,网上也有下载,可以生成各种波形,也可以进行相关参数的修改,注意保存的时候保存成.mif格式的文件即可,调用的时候在IP核 ROM的配置的时候调用就好了,但是要注意位宽。
 
360桌面截图20150420133810.jpg
 
三 、相位调制器
 
         相位调制器部分就简单了,就是相位累加器取好的的8位ROM地址,我们可以通过按键进行加加减减,从而控制ROM取址的不同,从而控制波形的相位
 
360桌面截图20150420134109.jpg
 
四 、参数计算
 
        由上面的公式我们知道了输出波形的频率计算公式,当fword =1的时候我们算一下
 
输出频率 Fout  =  50_000000 / 2^32   = 0.01,就是个大约值了,也就是说,在fword =1的时候,我们的输出波形的频率为0.01HZ,假设我们让波形初始化的时候输出的是一个100HZ的正弦波,那么,我们应该设定fword = 10000,相位的话可以随便设置,我们就默认为0相位好了。如果要进行频率,相位可调,我们只需要让fword每次加100,就相当于频率步进为1HZ了,当然,由于我们的地址是8位的,那么,步进的相位就是 360 /256,大概等于1.4度吧。
 
     还有,既然频率为100HZ,也就是周期为0.01s,ROM要在0.01s内要输出256个数,那么每输出一个数的时间为0.01 /256 大概为 39us的样子,因此,我们的DAC的采集频率不能比ROM的输出频率小了,否则的话采集到的数据是不准确的
 
综上所述,我们总结一下
 
    输出频率: 100HZ
    输出相位:0
    步进频率:1HZ
    步进相位:1.4度
    DAC采集时间:小于39us
 
当然在一般情况下 DAC的采集频率是远大于ROM的输出频率的,只要信号输出频率不是特别快,采集正确还是有保证的。为了保证采集的正确性,我特别加了一个数据变化检测部分,一但数据发生变化,就告知DAC模块,做相应处理
 
360桌面截图20150420135836.jpg
 
     一切准备就绪,我们可以做一下仿真,具体的仿真调试过程不做详解,还是大家自己动手去做的过程
 
360桌面截图20150420141736.jpg
 
 
五、DAC接口电路
 
       DA芯片我用的是TLC5620这块芯片,这块芯片是四路输出,8位的的数模准换芯片,芯片的操作不是很难,我们还是先来看一下芯片的datasheet
 
360桌面截图20150420142026.jpg
 
DA芯片有这几个管脚需要用到,分别是
 
    dac_clk,用于产生DAC的工作时钟,说明文档上说时钟最大为1M,而我们的晶振为50M因此我们需要做个分频,我做了个64分频,当然是为了稳定,如果可以大家也可以试试更高的频率
 
   dac_data , 是串行输入的接口,用来接收数字信号
 
   dac_load ,将接收来的数字信号开始转化成模拟信号的使能端,低电平有效,要持续最低250ns
 
   dac_ldac , dac信号刷新控制端,当dac为低电平的时候一直有模拟信号输出,否则不再刷新
 
 上面那个时序图大体解释一下,就是在load信号是高电平期间,每来一个DAC工作时钟的下降沿,就将1位数据通过dac_data端口送到DAC芯片内部的移位寄存器中,寄存器是11位的,前两位为输出模拟信号的通道号,第三位是用来计算输出电压用的一个参数吧,低8位是输入的数字信号,当移位寄存器存满之后,给一个load信号,工作时钟处于不工作状态,注意,这一点很重要,我当时就是不知道这一点,一直调不好,就是在转换期间,让工作时钟处于非工作状态,等待250ns之后,将load拉高,进行下一次采集。
 
关于代码的分析这里也不做详细分析了,大家还是自行消化,这里只传一部分,关于数据的发送部分的代码
 
360桌面截图20150420144736.jpg
 
每来一个时钟,送一位数据给dac_data端口,送满移位寄存器为止,当移位寄存器计满的时候,load拉低,同时时钟停止工作,bit_counter 清零,等待下一次采集。当然当数据发生变化的时候,也就是data_change 信号来的时候,bit_counter 也清零,重新采集,避免采集错误
 
   全部模块编译完成之后,我们就可以用示波器来检测我们的结果了
 
360桌面截图20150420145943.jpg
 
由示波器的结果我们可以看到,输出电压峰值为2.5V,当然这是由芯片决定的,输出频率为116HZ,大体符合我们的要求,毕竟我们很多参数做了近似,再通过按键进行波形的变换,发现各波形输出正常,只有方波和锯齿波,在电压下降的时候变化有点缓慢,当然这也得考虑我们芯片的因素,毕竟电压不能变化的那么快,也属于正常情况
 
initpintu_副本.jpg
 
 
波形显示正常,下面通过按键来调节波形的频率和相位,可是实现实时显示的功能,发现频率,相位都有所改变
 
initpintu_副本2.jpg
     当然,这个工程也有做的不好的地方,例如,再进行相位调节的时候,由于各种波形的地址在ROM里面是挨在一起的,这样进行相位调节会侵犯其他波形的地址,这一点我当时也考虑过了,短时间内没有想出更好的办法,可以用多个ROM,其他的办法还请大家自己动脑子想一下吧,想出来了可以给我留言,大家一起交流嘛
 
     好了,这一次主要还是讲了一下工程的全过程,没有仔细讲代码编写的具体过程,也没有讲仿真时候的操作流程,这些部分还是留给读者慢慢去摸索,毕竟这也是一个学习的过程,今天就到这里吧,由于小墨手上也有了项目,平时还要上课,更新速度方面应该不是很快!谢谢大家的支持~
 
    以后小墨同学的文章将在EDNchina和北京至芯科技官方技术论坛同时更新,至芯科技的官方技术论坛也有很多的资料供大家下载,里面也有小墨的专题板块,希望广大网友和爱好者的大力支持~
http://www.fpgaw.com/
小墨同学《零基础学FPGA》学习专版  一个大三学生学习之路的真实记录
http://www.fpgaw.com/thread-78527-1-1.html
下面是至芯科技官方网站,夏宇闻教授和那里的老师都超好的~有意愿接受FPGA培训的可以找工作人员咨询
http://www.zxopen.com/
       以后小墨同学的教程会跟这款开发板配套,想跟小墨同学一起学习,一起进步的的可以考虑一下购买一块开发板,毕竟学习要舍得投资嘛,下面是开发板淘宝链接
http://item.taobao.com/item.htm? ... ;abbucket=13#detail
 
 
 

文章评论17条评论)

登录后参与讨论

用户1846578 2016-6-4 15:54

很好

用户3671879 2016-6-3 08:10

大LZ,还是取fre_add[31:24] ,将这8位数送给ROM的地址这个地方不明白了,为什么这样取8位? 2^32和2^24差几个数量级是说地位对高位影响太小了??

用户3671879 2016-6-2 21:41

请问取fre_add高8位那儿是什么意思呢? 还是每明白为什么可以用高8位代替32位,2^32和2^24差几个数量级,有什么影响?

用户3671879 2016-6-2 21:38

你好,我还是太理解为什么取高8位就行了,大神再给说的详细一点吧。 2^32和2^24相差几个数量级,是说地位对高位影响太小吗? 就这儿不太懂 “即取fre_add[31:24] ,将这8位数送给ROM的地址。很多人不明白了,为什么这样取8位,这样取8位不是把一个完整的信号给截断了吗?刚开始我也有这么想过,但是仔细算一下才知道自己是多虑了。用计算器算一下知道,2^32 = 4.3x 10^9 左右,而2^24 = 16 x 10^6 左右,前后差了几个数量级,所以,我们取fre_add[31:24],相当于把一个波形的相位分成了256个点,每个点对应一个数据。”

用户3822254 2016-5-30 17:16

写的很好,小墨!

用户3822254 2016-5-30 17:04

用户1858735 2015-10-20 15:30

之前用过DDS的IP核,没有代码实现过,多谢LZ

用户1703049 2015-6-23 08:23

很好

用户1724563 2015-6-13 13:20

ok

用户377235 2015-6-8 11:22

很实际,喜欢这样的好文!
相关推荐阅读
小墨同学 2015-09-10 10:28
零基础学FPGA( 三十二) 写在京城,多级FIR半带滤波器的FPGA实现
        每次到京城来总不能忘了出去逛逛吧,偌大的北京城去哪呢?炙热的大太阳烤的哪都不想去了,幸好这次有亲戚来北京旅游,搭个顺风车便出去转了一下。这次的闲逛可没有上次那么感叹,上次主要是去的...
小墨同学 2015-08-29 15:19
零基础学FPGA(三十三)多相结构抽取滤波器笔记
        前两篇文章已经介绍过了,在多速率信号处理中,CIC滤波器和FIR半带滤波器应用的非常广泛,由于CIC滤波器的特殊结构,使得它非常适合采样速率远远大于信号速率的情况时的抽取跟内插滤波...
小墨同学 2015-08-29 15:18
零基础学FPGA(三十一)写在京城,Hogenauer CIC抽取滤波器的FPGA实现笔记
         实习完还没几天,突然接到北京至芯雷总的电话,让我赶往北京来做一些事情,就这样,我的又一趟北京之旅开始了。         上次来北京还是今年1月份,冬天嘛,京城光秃秃的一片,...
小墨同学 2015-08-05 20:50
【博客大赛】零基础学FPGA (二十六)必会! 从静态时序分析到SDRAM时序收敛(上篇)
    好像小墨有这么一个习惯啊,就是每篇文章的开头总喜欢叨叨几句啊~既然这样,那我们今天也聊几句好了,总感觉直接就开始学习专业知识有点让人看不进去~     今天我们就说说生活吧~时间很快啊,...
小墨同学 2015-07-26 21:57
[博客大赛]零基础学FPGA(三十) IIR数字滤波器的FPGA实现笔记
      然而暑假已经过了快一半,想想也是挺快的,前一段时间学校安排实习,在长沙待了一段时间,说是实习,感觉却是像是度假,住着酒店,100多号人,想想都觉的热闹。实习搞得是Java安卓,当然我也...
小墨同学 2015-06-17 09:50
[博客大赛] 零基础学FPGA (二十九)滤波器开篇,线性相位FIR滤波器的FPGA实现
        也是有一段时间不写博客了啊,主要是这学期的实验课太多,每天都是在写预习报告,实验报告中度过,也快到考试月了啊,感觉又要忙起来了,今天就抽点时间来开个头,开什么头呢?对!就如标题所示...
我要评论
17
16
关闭 站长推荐上一条 /2 下一条