基于小脚丫FPGA开发板设计DDS任意波形发生器
本设计通过FPGA结合高速DAC实现了DDS任意波形发生器,实现波形可调、频率可调、幅值可调、OLED显示、按键控制、旋转编码器调节等功能。
标签
FPGA
DDS
2022寒假在家练
子盟FM
更新2022-03-03
上海理工大学
1553

内容介绍

项目介绍:

本设计通过FPGA结合高速DAC实现了DDS任意波形发生器,实现波形可调、频率可调、幅值可调、OLED显示、按键控制、编码器调节等功能。

本设计主要实现了以下功能:

  1. 波形类型可调(正弦波、锯齿波、三角波、方波)。

  2. 频率可调0-20MHz,最低调节精度1Hz,可选调节累加值。

  3. 幅度可调0-1V,累加值为0.1v。

  4. 通过OLED显示当前输出波形的类型、频率、幅度;显示频率调节累加值;显示当前可调模式。

  5. 通过按键实现三种调节模式切换,通过旋转编码器调节波形、频率、频率累加值、幅度。

使用说明:

通过按键K1控制模式切换(调节波形、频率、幅值),OLED的*用于指示当前选择的调节模式。

OLED第一行显示波形类型,第二行显示幅值,第三行显示频率,第四行显示频率调节累加值。

通过旋转编码器左右转实现波形切换、幅值改变(每次0.1V)、频率改变。

当处在修改频率模式时,可通过按下旋转编码器修改频率累加值(ACC),可按照1Hz、10Hz、100Hz、1KHZ、10KHZ、100KHZ、1MHZ修改频率。

FqQcdOrsblJycfAZLn45mOpGPkAl

硬件介绍:

  1. FPGA:Lattice LCMXO2-4000HC-4MG132

  2. 基于3Peaks的3PD5651制作的高速DAC模块

  3. OLED显示屏168*64,SPI三线通信

  4. 按键和旋转编码器

设计思路:

本次设计采用了自上而下的设计方法,首先定义顶层模块功能,然后将需要实现的功能设计成多个子模块,分别对多个子模块进行设计验证,验证无误后将子模块汇总,最终实现整个设计。

FoHFzNUk8Li9lF9V7_XKzr-iCnsT

其中时钟模块通过IP核生成,输出120MHz时钟。

按键消抖和编码器驱动模块移植官方参考例程,未作修改。

DDS生成和OLED显示模块基于官方例程进行了修改。

参数控制模块独立编写。

FsLrTCftE6HT55gIMzCdUTxIFHND

资源使用情况:

Design Summary
Number of registers: 551 out of 4635 (12%)
PFU registers: 551 out of 4320 (13%)
PIO registers: 0 out of 315 (0%)
Number of SLICEs: 1225 out of 2160 (57%)
SLICEs as Logic/ROM: 1225 out of 2160 (57%)
SLICEs as RAM: 0 out of 1620 (0%)
SLICEs as Carry: 500 out of 2160 (23%)
Number of LUT4s: 2441 out of 4320 (57%)
Number used as logic LUTs: 1441
Number used as distributed RAM: 0
Number used as ripple logic: 1000
Number used as shift registers: 0
Number of PIO sites used: 39 + 4(JTAG) out of 105 (41%)
Number of block RAMs: 0 out of 10 (0%)
Number of GSRs: 1 out of 1 (100%)
EFB used : No
JTAG used : No
Readback used : No
Oscillator used : No
Startup used : No
POR : On
Bandgap : On
Number of Power Controller: 0 out of 1 (0%)
Number of Dynamic Bank Controller (BCINRD): 0 out of 6 (0%)
Number of Dynamic Bank Controller (BCLVDSO): 0 out of 1 (0%)
Number of DCCA: 0 out of 8 (0%)
Number of DCMA: 0 out of 2 (0%)
Number of PLLs: 1 out of 2 (50%)
Number of DQSDLLs: 0 out of 2 (0%)
Number of CLKDIVC: 0 out of 4 (0%)
Number of ECLKSYNCA: 0 out of 4 (0%)
Number of ECLKBRIDGECS: 0 out of 2 (0%)

主要代码片段:

1.DDS参数控制模块

module dds_ctrl
(
	input 				clk,		//时钟
	input 				rst,		//复位
	input 				Left_pulse,	//旋转编码器
	input 				Right_pulse,//旋转编码器
	input 				OK_pulse,	//旋转编码器
	input 				key_pulse,	//按键
	
	output reg [1:0] 	fun_sel,	//功能选择,0调类型,1调幅值,2调频率,3调原始波形
	
	output reg [1:0]  	wave_sel,	//波形类型
	output reg [2:0] 	pulse_cnt,	//频率调节步进
	output reg [31:0] 	phase_fo,	//频率
	output reg [3:0]   	amp_sel,	//幅度可调范围 0.1-1  
	output reg 			raw_sel		//调节波形输出,0:经调幅之后的波形 ,1:原始波形
);

localparam WAVE = 2'h0, AMP = 2'h1, FRE = 2'h2,SEL = 2'h3;
always @(posedge key_pulse  or  negedge rst )//功能选择
begin
	if (!rst)
		begin
			fun_sel <= 2'd0;
		end
	else if(fun_sel>=4)
		begin
			fun_sel <= 2'd0;
		end
	else 
		begin
			fun_sel <= fun_sel + 2'd1;	
		end				
end

reg [19:0] 	phase_add [6:0]; //频率调节范围
initial
begin // 调节宽度
	phase_add[0] = 20'd1;			//1Hz
	phase_add[1] = 20'd10;			//10Hz
	phase_add[2] = 20'd100;			//100Hz
	phase_add[3] = 20'd1_000;		//1KHZ
	phase_add[4] = 20'd10_000;		//10KHZ
	phase_add[5] = 20'd100_000;		//100KHZ
	phase_add[6] = 20'd1_000_000;	//1MHZ
end
always@(posedge clk or negedge rst) 
begin
	if(!rst)
		begin
			phase_fo <= 32'd5;
			pulse_cnt<= 3'd0;
			wave_sel <= 2'd0;
			amp_sel  <= 3'd5;
			raw_sel	 <= 1'd1;
		end 
	else 
		begin
			case (fun_sel)
				WAVE: begin //调节波形
						if(Right_pulse)
							wave_sel <= wave_sel + 1'b1;
						else if(Left_pulse)						
							wave_sel <= wave_sel - 1'b1;
						else
							wave_sel <= wave_sel;
				end

				AMP: begin//调节幅值
						if(Right_pulse&&(amp_sel<4'd10))		//限制幅度调节范围
							amp_sel <= amp_sel + 1'b1;
						else if(Left_pulse&&(amp_sel>4'd1))		//限制幅度调节范围				
							amp_sel <= amp_sel - 1'b1;
						else
							amp_sel <= amp_sel;
				end

				FRE: begin//调节频率
					if(Right_pulse) 
					begin
						
						if(phase_fo>=32'd20_000_000) phase_fo<=32'd20_000_000; 
						//限制频率调节范围
							else  phase_fo <= phase_fo + phase_add[pulse_cnt];
							
					end 
					else if(Left_pulse&&(phase_fo>phase_add[pulse_cnt])) 		
					//限制频率调节范围,防止过零
					begin									
						phase_fo <= phase_fo - phase_add[pulse_cnt];
					end 
					else if(OK_pulse)
					begin
						pulse_cnt <= pulse_cnt + 3'd1;
						if(pulse_cnt>=3'd7)pulse_cnt <= 3'd0;
					end
					else
					begin
						phase_fo <= phase_fo;
					end
				end
				SEL:begin
					if(Right_pulse)
							raw_sel <= ~raw_sel;
						else if(Left_pulse)							
							raw_sel <= ~raw_sel;
						else
							raw_sel <= raw_sel;
				end
				default: begin
						if(Right_pulse)
							wave_sel <= wave_sel + 1'b1;
						else if(Left_pulse)							
							wave_sel <= wave_sel - 1'b1;
						else
							wave_sel <= wave_sel;
				end
			endcase
		end
end
endmodule

2.DDS生成模块-频率控制原理

通过修改累加值控制实际波形输出频率

phase_fo即为实际输出的波形频率,phase_fo已知求phase_m公式为:

m = fo*232/fc

其中fc为时钟频率120MHz,因此公式可以简化为:

m = fo*35.791394125

在FPGA中进行浮点运算会出现问题,此处使用右移的方式来实现,35.791394125 转化为二进制位100011.11001010100110001101

/*dds_wave中 主要的修改部分,实现频率调节*/
reg [31:0] phase_acc;		//32位相位累加器 频率可调范围:0.028Hz ~ 20MHz
reg	[31:0] phase_m;			//频率累加值
//reg	[31:0] phase_fo = 2;	//可调频率范围0-20Mhz

always @(posedge clk_120m) 
begin	
	//phase_m <= phase_fo * 35.791394125;
	phase_m <= (phase_fo<<5)+(phase_fo<<1)+(phase_fo)+(phase_fo>> 1)
	+(phase_fo>> 1)+(phase_fo>> 2)+(phase_fo>> 5)+(phase_fo>> 7)+(phase_fo>> 9)
	+(phase_fo>> 12)+(phase_fo>> 13)+(phase_fo>> 17)+(phase_fo>> 18)+(phase_fo>> 20); 	//fo = (m*fc) / (2^32)// 35.791394125  //100011.11001010100110001101
	phase_acc <= phase_acc + phase_m;  		//m = fo/fc * 2^32					
end

3.DDS生成模块-幅度控制原理

此处用于调幅和控制波形输出为原始波形还是调幅波形,已知波形输出原始幅值为2.7V左右,结合公式:

OUT = 2.7*X/K

需要控制OUT输出范围为0.1-1VX为输入,K为固定系数,可以设定X输入范围为1-10,当X=1时,OUT=0.1VX=10OUT=1V,则K=1/27=0.037109375

//控制波形输出:0经调幅之后的波形 ,1原始波形
always @(*) begin
    case(raw_sel)
        1'b0: dac_data = amp_sel*((dac_dat>>5)+(dac_dat>>8)+(dac_dat>>9));//*0.037109375  二进制 0.000010011
        1'b1: dac_data  = dac_dat;
        default: dac_data = dac_dat;
    endcase
end

4.OLED显示模块

OLED主要修改部分如下,8421BCD码提取频率和幅值的值。通过case进行判断,然后进行字符串拼接。

//显示波形类型
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			type_buff <= "Type:Sin";
		end else begin
			case(wave_sel)
				2'b00: type_buff <= "Type:Sin";
				2'b01: type_buff <= "Type:Saw";
				2'b10: type_buff <= "Type:Trg";
				2'b11: type_buff <= "Type:Squ";
			endcase
		end
	end
	
	//显示输出频率
	wire [3:0]fre_unit,fre_ten,fre_hun,fre_tho,fre_t_tho,fre_h_hun,fre_m,fre_mm;
	bcd_8421 u1_bcd_8421
	(
		.sys_clk	(clk), 
		.sys_rst_n	(rst_n), 
		.data		(phase_fo),
		
		.unit		(fre_unit),	//个位
		.ten		(fre_ten),	//十位
		.hun		(fre_hun),	//百位
		.tho		(fre_tho), 	//千位
		.t_tho		(fre_t_tho),//万位
		.h_hun		(fre_h_hun) //十万
	);	
	bcd_8421 u2_bcd_8421
	(
		.sys_clk	(clk), 
		.sys_rst_n	(rst_n), 
		.data		(phase_fo/1000_000),
	
		.unit		(fre_m), //百万位
		.ten		(fre_mm) //千万位
	);
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			fre_buff <= "Fre:   1Hz";
		end else begin
			if(phase_fo<32'd1000) 
				fre_buff <= {"Fre:",4'd0,fre_hun,4'd0,fre_ten,4'd0,fre_unit,"Hz "};
			else if(phase_fo>32'd1_000_000) 
				fre_buff <= {"Fre:",4'd0,fre_mm,4'd0,fre_m,"MHz "};//此处显示有bug
            
			else fre_buff <= {"Fre:",4'd0,fre_h_hun,4'd0,fre_t_tho,4'd0,fre_tho,"KHz"};
		end
	end
	
	//显示输出幅度峰峰值vpp
	wire [3:0]amp_unit,amp_ten;
	bcd_8421 u3_bcd_8421
	(
		.sys_clk	(clk), 
		.sys_rst_n	(rst_n), 
		.data		(amp_sel),
		
		.unit		(amp_unit),	//个位
		.ten		(amp_ten)	//十位
	);
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			amp_buff <= "Amp:0.5v";
		end else begin
			amp_buff <= {"Amp:",4'd0,amp_ten,".",4'd0,amp_unit,"v"};
		end
	end

	//显示频率调节步进值
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			cnt_buff <= "Acc:  1Hz";
		end else begin
			case(pulse_cnt)
				5'd0: cnt_buff <= "Acc:1Hz  ";
				5'd1: cnt_buff <= "Acc:10Hz ";
				5'd2: cnt_buff <= "Acc:100Hz";
				5'd3: cnt_buff <= "Acc:1KHz ";
				5'd4: cnt_buff <= "Acc:10KHz";
				5'd5: cnt_buff <= "Acc:100K ";
				5'd6: cnt_buff <= "Acc:1MHz ";
			endcase
		end
	end

	/*此处省略官方案例代码,以下仅列出修改的代码片段*/
	5'd13:	begin y_p <= 8'hb0; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd8;  char <= type_buff;state <= SCAN; end
	5'd14:	begin y_p <= 8'hb2; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd8;  char <= amp_buff;state <= SCAN; end	
	5'd15:	begin y_p <= 8'hb4; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd10; char <= fre_buff;state <= SCAN; end		
	5'd16:	begin y_p <= 8'hb6; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd9;  char <= cnt_buff;state <= SCAN; end
							
	5'd17 :	begin y_p <= 8'hb0; x_ph <= 8'h16; x_pl <= 8'h04; num <= 5'd1;  char <= " ";state <= SCAN; end
	5'd18:	begin y_p <= 8'hb2; x_ph <= 8'h16; x_pl <= 8'h04; num <= 5'd1;  char <= " ";state <= SCAN; end	
	5'd19:	begin y_p <= 8'hb4; x_ph <= 8'h16; x_pl <= 8'h04; num <= 5'd1;  char <= " ";state <= SCAN; end		
							
								
	5'd20:	begin y_p <= {4'hb,(fun_sel<<1)}; x_ph <= 8'h16; x_pl <= 8'h04; num <= 5'd1;  char <= "*";state <= SCAN; end
	5'd21:	begin y_p <= 8'hb6; x_ph <= 8'h16; x_pl <= 8'h04; num <= 5'd1;  char <= " ";state <= SCAN; end								
	default: state <= IDLE;   //如果你需要动态刷新一些信息,此行应该取消注释

实验结果:

1.四种类型波形

FopHz2XZGWbdgRQc9K4c5ZGXhJMC

2.不同频率的正弦波信号

FqyR46bp3az2viW3eqTxSndF3Me2

3.实物图片

FvQKJpwGJ22wrz0tZnVTNwoFmNTN

设计心得:

第一次使用FPGA完成的项目,本次项目耗时大约一周的时间。因为具有STM32的软硬件开发经验,Verilog语言从语法规则上和C语言十分相似,所以使用Verilog编写代码上手很快。但也带来一些坏处,总是跳不出用软件编程的思想编写Verilog代码。

在整个设计过程中主要出现了以下几个问题:

1.提示旋转编码器三个输入接口未连接问题

ERROR - Port 'encoder_a' is unconnected.

问题分析:没有在顶层指定dds_ctrl子模块输出位宽,导致生成的硬件链路出现问题。

解决方法:顶层模块调用子模块时一定要声明输出数据的位宽,否则默认当作1bit。

2.DAC模块CLK时钟短路

问题分析:使用探针测量CLK引脚时不小心将焊盘附近的绝缘层划开,铜箔露出导致CLK与GND短路。

解决方法:使用烙铁将露出铜箔稍微刮几下。

3.训练板DAC输出引脚无信号输出

问题分析:刚开始测量时习惯将示波器探针插到Aout的中间位置,次数多了导致导线断路。

解决方法:从DAC模块输出引脚飞线。

以上问题解决起来都很简单,但是找到问题所在需要不断试错。当代码找不出bug但是还是没有输出时就不要死磕软件了,看看硬件是不是出现了什么问题。

本次设计还有几个问题没有解决:

  1. OLED显示问题,调节频率到MHz以上时OLED显示的频率出现bug。

  2. 波形失真问题,当进行调幅时输出的波形有明显的失真。频率调整到10MHz左右,不进行调幅也会失真(此处提高PLL频率应该可以解决)。调幅失真还没有找到好的解决办法。

分享一个简单的脚本,实现自动将.jed文件复制到小脚丫开发板,能自动化解决的事情就绝不自己动手。注意:需要修改磁盘编号

copy *.jed K:\
pause
::https://zhidao.baidu.com/question/513072753.html?qbl=relate_question_3&word=%C5%FA%B4%A6%C0%ED%CE%C4%BC%FE%D0%DE%B8%C4%CA%B1%BC%E4

链接:https://pan.baidu.com/s/1cIHFOxkQ9hn8O0rkTIdSYw?pwd=ck80
提取码:ck80

附件下载

DDS_V2.zip
链接:https://pan.baidu.com/s/1cIHFOxkQ9hn8O0rkTIdSYw?pwd=ck80 提取码:ck80

团队介绍

团队成员
子盟FM

评论

0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号