完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
各位FPGA用户大家好,为了给本版块营造更良好的学习氛围,现特邀请我们的版主小梅哥带我们一起做项目【基于DDS技术的信号发生器系统】 欢迎大家一起跟帖交流学习!
题目:设计一个基于DDS技术的信号发生器系统。 要求: 1、有输入设备,能够对系统输出信号的频率和相位进行调整; 2、有输出设备,能够实时显示当前输出信号的频率和相位; 3、频率步进要求最低1Hz; 4、相位步进要求最低0.1°; 5、DA精度要求最低10位,可以没有硬件DA芯片(采用signaltap II来抓取输出波形即可),要求最低一路正弦输出,也可以为两路正弦输出。 6、输出正弦信号最高频率要求大于等于1M。 第一周内容,请各位采用word或者绘图软件绘制出系统的结构图,列出所需要的硬件资源。 同时对频率精度和相位精度要求进行分析,得出实现精度要求需要满足那些条件。 输入设备可采用PS2键盘、独立按键、矩阵键盘、红外遥控、串口之类的; 输出设备可采用数码管、串口、LCD1602、LCD12864、VGA之类的。 欢迎大家跟帖一起学习,每周二我们将定时更新,希望大家都可以通过这次活动学习到更多的知识,同时如果大家希望通过本论坛学到哪方的技术或者赶兴趣的技术,均可跟帖留言,我们将尽全力安排工程师带领大家一起学习交流! |
|
相关推荐
|
|
是的,我这里设置的最高频率为CLK/8,在保证幅度的同时保证了低谐波分量。 |
|
|
|
|
|
DDS原理再解释。 上面的对DDS原理的解释,还是有部分同学反映不够直观,读完之后还是不明白DDS究竟是怎么控制频率和相位的,那么,这里小梅哥再用更加通俗的方式给大家讲解一下。 如图4,为一个完整周期的正弦信号的波形,总共有33个采样点,其中第1点和第33点的 值相同,第33点为下一个周期的起始点,因此,实际一个周期为32个采样点(1~32)。因为是在matlab中生成的,因此起始点为1,而不是我们常见的0,这里对我们理解DDS的原理没有任何影响,因此不必过多纠结。
图4 32个采样点的正弦信号波形 图5 16个采样点的正弦信号波形 我们要使用FPGA控制DAC来输出这样一个周期的正弦信号,每1ms输出一个数值。如果每个点都输出,则总共输出这一个完整的周期信号需要输出32个点,因此输出一个完整的信号需要32ms,则输出信号的频率为1000/32Hz。 假如,我们现在用这一组数据来输出一个2*(1000/32)Hz的正弦信号,因为输出信号频率为2*(1000/32)Hz,那么输出一个完整的周期的正弦波所需要的时间为32/2,即16ms,为了保证输出信号的周期为16ms,那么,我们就需要对我们的输出策略进行更改,上面输出周期为32ms的信号时,我们采用的为逐点输出的方式,以32个点来输出一个完整的正弦信号,而我们FPGA控制DAC输出信号的频率固定为1ms,因此,我们要输出周期为16ms的信号,只能输出16个点来表示一个完整的周期,因此,我们就选择以每隔一个点输出一个数据的方式来输出即可,因此,我们可以选择输出(1、3、5、7……29、31)这些点,因为采用这些点,我们还是能够组成一个完整的周期的正弦信号,而输出时间缩短为一半,则频率提高了一倍。最终结果如上图5所示。 如果我们需要输出频率为(1/2)*(1000/32)Hz,即周期为64ms,则只需要以此组数据为基础,每2ms输出一个数据即可,例如第1ms和第2ms输出第一个点,第3ms和第4ms输出第二个点,以此类推,第63ms和第64ms输出第32个点,即可实现周期加倍,即频率减半的效果。 对于相位的调整,则更加简单,我们只需要在每个取样点的序号上加上一个偏移量,便可实现相位的控制。例如,上面默认的是第1ms时输出第一个点的数据,假如我们现在在第1ms时从第9个点开始输出,则将相位左移了90度,这就是控制相位的原理。 实现DDS输出时,将横坐标上的数据作为ROM的地址,纵坐标上的数据作为ROM的输出,那么指定不同的地址就可实现对应值的输出。而我们DDS输出控制频率和相位,归结到底就是控制ROM的地址。 |
|
|
|
|
|
接下来我们进行DDS信号发生器系统的建模。 小梅哥在图3中已经给出了一个参考的DDS系统的框图,这里,小梅哥就将以此框图为基础进行DDS信号发生器系统顶层模块的搭建。为了实现键盘按键信息的反馈,这里小梅哥临时加上了一个蜂鸣器驱动,在每次检测到键盘按键事件后,产生一个短促的蜂鸣器响声,以反馈给操作者有按键检测成功。修改后的框图如图6所示:
图6 DDS信号发生器系统框图 依据此结构,小梅哥编写了顶层模块和各个子模块的端口部分,经过Quartus II综合后的结构图如下所示: 图7 Quartus II软件综合结果 由图可知,小梅哥设计的代码综合出来的结构与预先设计的架构完全吻合。 以上为架构设计,接下来就该进行各个子模块的设计了,今天先暂时更新到这里,各个子模块的代码,小梅哥将在最近几天里陆续放出,敬请大家关注。 |
|
|
|
|
|
本帖最后由 小梅哥 于 2014-12-22 21:39 编辑 这里,小梅哥先附上我根据DDS的结构编写的DDS代码:
该模块为小梅哥对着DDS编写的代码,还没有经过验证,因此小梅哥也不知道该模块是否能够实现功能,为了对该模块进行验证,小梅哥采用仿真的方式,对该模块进行了仿真测试,以下为小梅哥编写的testbench的代码:
测试文件中通过改变频率控制字和相位控制字,再观察对应的输出,来验证该设计的正确性,仿真时随机选择了三个频率和相位的信号进行输出,50000Hz,200000Hz,800000Hz。以下为仿真结果图: 四个信号的周期分别为:20000ns,4980ns,1250ns,经过换算为频率,与预期一致,表明该设计正确。这里使用了一个位宽为10位,字数为4096字的ROM,具体ROM的中的数据,以附件的形式提供。
ddsrom.mif
(43.55 KB, 下载次数: 220
)
|
|
|
|
|
|
目前为止,小梅哥所有的系统外设中,PS2键盘解码,数码管显示等都已经完成,DAC驱动暂时不需要,在实际观察输出的时候直接使用signaltap II来观察输出波形即可,蜂鸣器驱动在系统完成后最后再来加上。关于PS2键盘解码驱动的,请参看我的帖子:https://bbs.elecfans.com/jishu_463238_1_1.html
关于数码管驱动的,请参看我的帖子: https://bbs.elecfans.com/jishu_463750_1_1.html 到此为止,此DDS信号发生器系统所需的全部子模块中,除了蜂鸣器模块和控制模块,就都已经设计好了。蜂鸣器模块设计很简单,最后再说,最重要也是最复杂的模块也就是控制模块了,那么具体控制模块怎么设计,还得和我们想要实现的效果相联系。今天太晚了,这部分内容,小梅哥就留到明天再更新吧。 |
|
|
|
|
|
接下来,我们将进行人机交互设计: 以前没有开发板没,做起实验来一直因为没有验证平台而感到很头疼,后来,感谢至芯科技赠送了小梅哥两块FPGA开发板,加上小梅哥刚刚下血本购买的de1-soc的板子,以及友人赞助的一块核心板,小梅哥手头已经有4块FPGA开发板了。但是这些开发板上都存在一个问题,即只有6个数码管,我们前面提出过输出信号频率要求大于等于1M,那么问题来了,我们要通过数码管显示当前输出信号频率,当输出信号频率大于999999Hz时,6位数码管已经无法显示频率值了,为了解决这个问题,小梅哥经过反复琢磨,决定采用外扩硬件的方式,某宝上寻得一个采用SPI接口的8位数码管模块,如下图: 该模块只需要3个IO即可外扩出8位7段数码管,模块也不贵,采用经典的MAX7219芯片。咬咬牙,还是买了,我心疼的不是模块贵,而是为了9块钱的模块付出12块的邮费,为了给大家把项目做好,小梅哥也是拼了。 那么,选择了新的模块后,问题又来了,得为该模块开发驱动程序,因此,本项目在正式进入控制模块设计之前,又多出了一个开发MAX7219数码管模块驱动的任务。经过一晚上的努力,MAX7219的驱动已经开发好,可以和使用前面小梅哥开发的普通数码管模块一样使用了,今天颈椎好疼,暂时不写文档了,先把代码贴出来大家看看,顺便放一张显示结果图,文档明天再写了发布吧. 001 module max7219(clk_i,rst_n,data_i,cs_o,sda_o,sclk_o); 002 003 input clk_i; 004 input rst_n; 005 input [34:0]data_i;//data_i[34:32] isthe location of the point 006 007 output reg cs_o; //max7219 chip_select pin 008 output reg sda_o; //max7219 data pin 009 output sclk_o; //max7219 clock pin 010 011 reg clk_1us; 012 reg [6:0]cnt1; 013 reg [3:0]state; 014 reg [4:0]state1; 015 reg done_sig; 016 reg [15:0]dat_send; 017 reg point7,point6,point5,point4,point3,point2,point1,point0; 018 019 wire LOW; 020 021 localparam CLOCK = 24000000; //system clock 022 localparam CNT1_MAX = CLOCK/1000000 - 1, 023 CLK_L2H = CLOCK/2000000 - 1, 024 CLK_L = CLOCK/4000000 - 1; 025 026 localparam s0 = 0, 027 s1 = 1, 028 s2 = 2, 029 s3 = 3, 030 s4 = 4, 031 s5 = 5, 032 s6 = 6, 033 s7 = 7, 034 s8 = 8, 035 s9 = 9, 036 s10 = 10, 037 s11 = 11, 038 s12 = 12, 039 s13 = 13; 040 041 always @(posedge clk_i or negedge rst_n) 042 begin 043 if(!rst_n) 044 cnt1 <= 7'd0; 045 else if(cnt1 == CNT1_MAX) 046 cnt1 <= 7'd0; 047 else 048 cnt1 <= cnt1 + 1'b1; 049 end 050 051 always @(posedge clk_i or negedge rst_n) 052 begin 053 if(!rst_n) 054 clk_1us <= 0; 055 else if(cnt1 == CLK_L2H) 056 clk_1us <= 1; 057 else if(cnt1 == CNT1_MAX) 058 clk_1us <= 0; 059 end 060 061 assign LOW = (cnt1 == CLK_L)?1'b1:1'b0; 062 assign sclk_o = clk_1us; 063 064 //--------decode the location of thepoint------------------ 065 always@(posedge clk_i or negedge rst_n) 066 begin 067 if(!rst_n) 068 {point7,point6,point5,point4,point3,point2,point1,point0} <= 8'd0; 069 else 070 case(data_i[34:32]) 071 0:{point7,point6,point5,point4,point3,point2,point1,point0} <= 8'b00000001; 072 1:{point7,point6,point5,point4,point3,point2,point1,point0} <= 8'b00000010; 073 2:{point7,point6,point5,point4,point3,point2,point1,point0} <= 8'b00000100; 074 3:{point7,point6,point5,point4,point3,point2,point1,point0} <= 8'b00001000; 075 4:{point7,point6,point5,point4,point3,point2,point1,point0} <= 8'b00010000; 076 5:{point7,point6,point5,point4,point3,point2,point1,point0} <= 8'b00100000; 077 6:{point7,point6,point5,point4,point3,point2,point1,point0} <= 8'b01000000; 078 7:{point7,point6,point5,point4,point3,point2,point1,point0} <= 8'b10000000; 079 endcase 080 end 081 082 //-------------get the data todisplay---------------- 083 always@(posedge clk_i or negedge rst_n) 084 begin 085 if(!rst_n) 086 begin 087 state <= s0; 088 dat_send <= 16'h0000; 089 end 090 else if(LOW) 091 begin 092 case(state) 093 s0:begin dat_send <= 16'h0f00;if(done_sig)state <= s1;else state <= s0;end //close the display test mode 094 s1:begin dat_send <= 16'h0c01;if(done_sig)state <= s2;else state <= s1; end //enable thedisplay mode(not shutdown) 095 s2:begin dat_send <= 16'h09ff;if(done_sig)state <= s3;else state <= s2; end //code B decodefor digits 7~0 096 s3:begin dat_send <= 16'h0a04;if(done_sig)state <= s4;else state <= s3; end //set the drivecurrent as 25/32 097 s4:begin dat_send <= 16'h0b07;if(done_sig)state <= s5;else state <= s4; end //display digits7~0; 098 s5:begin dat_send <= {8'h08,point7,3'b000,data_i[31:28]};if(done_sig)state <= s6;else state <= s5; end 099 s6:begin dat_send <= {8'h07,point6,3'b000,data_i[27:24]};if(done_sig)state <= s7;else state <= s6; end 100 s7:begin dat_send <= {8'h06,point5,3'b000,data_i[23:20]};if(done_sig)state <= s8;else state <= s7; end 101 s8:begin dat_send <= {8'h05,point4,3'b000,data_i[19:16]};if(done_sig)state <= s9;else state <= s8; end 102 s9:begin dat_send <= {8'h04,point3,3'b000,data_i[15:12]};if(done_sig)state <= s10;else state <= s9; end 103 s10:begin dat_send <= {8'h03,point2,3'b000,data_i[11:8]};if(done_sig)state <= s11;else state <= s10; end 104 s11:begin dat_send <= {8'h02,point1,3'b000,data_i[7:4]};if(done_sig)state <= s12;else state <= s11; end 105 s12:begin dat_send <= {8'h01,point0,3'b000,data_i[3:0]};if(done_sig)state <= s13;else state <= s12; end 106 s13:state <= s5; //go back to rewrite the registers 107 default:state <= s0; 108 endcase 109 end 110 end 111 112 //-------send one 16bit word----------- 113 always@(posedge clk_i or negedge rst_n) 114 begin 115 if(!rst_n) 116 begin 117 state1 <= 17; 118 sda_o <= 1; 119 cs_o <= 1; 120 end 121 else 122 if(LOW) 123 begin 124 case(state1) 125 0:begin cs_o <= 0;sda_o <= dat_send[15];state1 <= 1;end 126 1:begin sda_o <= dat_send[14];state1 <= 2;end 127 2:begin sda_o <= dat_send[13];state1 <= 3;end 128 3:begin sda_o <= dat_send[12];state1 <= 4;end 129 4:begin sda_o <= dat_send[11];state1 <= 5;end 130 5:begin sda_o <= dat_send[10];state1 <= 6;end 131 6:begin sda_o <= dat_send[9];state1 <= 7;end 132 7:begin sda_o <= dat_send[8];state1 <= 8;end 133 8:begin sda_o <= dat_send[7];state1 <= 9;end 134 9:begin sda_o <= dat_send[6];state1 <= 10;end 135 10:begin sda_o <= dat_send[5];state1 <= 11;end 136 11:begin sda_o <= dat_send[4];state1 <= 12;end 137 12:begin sda_o <= dat_send[3];state1 <= 13;end 138 13:begin sda_o <= dat_send[2];state1 <= 14;end 139 14:begin sda_o <= dat_send[1];state1 <= 15;end 140 15:begin sda_o <= dat_send[0];state1 <= 16;done_sig <= 1;end 141 16:begin done_sig <= 0;state1 <= 0;cs_o <= 1;end 142 17:state1 <= 0; 143 endcase 144 end 145 end 146 147 endmodule |
|
|
|
|
|
楼主,学习了,很有参考价值,不过有没有主控模块?
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1476 浏览 1 评论
1266 浏览 0 评论
矩阵4x4个按键,如何把识别结果按编号01-16(十进制)显示在两个七段数码管上?
1471 浏览 0 评论
920 浏览 0 评论
2272 浏览 0 评论
1450 浏览 35 评论
5637 浏览 113 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-24 14:59 , Processed in 0.807787 second(s), Total 76, Slave 67 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号