用基于小脚丫FPGA的电赛训练板制作DDS信号发生器
本项目使用硬禾学堂2022寒假一起练电赛训练板完成制作DDS信号发生器,通过该项目可以理解DDS运行原理,学会应用DDS产生模拟信号;了解OLED的显示逻辑,学会调用OLED屏幕
标签
FPGA
显示
DDS
2022寒假在家练
森派陈醋
更新2022-03-03
北京邮电大学
1745

任务要求

本项目选择2022寒假练项目9:2005年A题-正弦信号发生器,完成其基础部分。同时实现2022寒假练项目3的大部分要求,作为在完成项目基础上的补充

2005年A题-正弦信号发生器要求:

  1. 正弦波输出频率范围:1kHz~10MHz;
  2. 具有频率设置功能,频率步进:100Hz;
  3. 输出信号频率稳定度:优于10-4
  4. 输出电压幅度:在50Ω负载电阻上的电压峰-峰值V opp≥1V;
  5. 失真度:用示波器观察时无明显失真。

根据项目3补充:

  1. 生成波形可调(正弦波、三角波、方波)、频率可调(DC-)的波形
  2. 生成模拟信号的频率范围为DC-20MHz,调节精度为1Hz
  3. 在OLED上显示当前波形的信息

设计思路

整体思路:

信号发生器要实现频率范围是DC-20MHz,外部晶振提供的12MHz是不够用的,利用Diamond中的PLL将频率上升为120MHz,当作DAC使用的时钟。在该程序中,所有的波形都使用了查找表的方式实现,并利用镜像生成,减少占用的存储空间,只需要存储1/4的波形数据就可生成完整的波形。在主程序中,为了提升波形在高频率时的频率精准度,采用了bcd码的10进制各位乘对应的频率调谐字,从而可以将误差控制在1Hz内(具体可看代码部分)。同时bcd码用来在OLED上进行频率的显示,同时进行了消零操作。

波表可以通过EXCEL等简单的方式获得

项目分为以下几个模块:

  1. 主程序模块(包含输入操作的采集与频率的设定)
  2. 波形查找表模块(包含各波形的数据)
  3. 二进制转8421BCD码(将频率转化为8421BCD码的形式)
  4. OLED驱动模块(利用小脚丫的例程进行修改和利用)
  5. 消抖模块(利用延时消抖的原理)
  6. PLL升频模块(利用Lattice内部的IP核)

各模块之间的关系框图如下:

FhDitTq-HGkuOwoxV94YDsVido_g

硬件介绍

本项目使用的是基于小脚丫FPGA的电赛训练平台

其系统框图如下:

采取虚拟U盘的方式进行程序下载

工具介绍

本实验平台FPGA使用的是Lattice公司的MXO2-4000HC芯片,使用Lattice Diamond软件进行程序编写,同时也提供了小脚丫Web IDE的编写功能。

主要代码片段与说明

  • 查找表相位确定
//-------------------------设定频率调谐字(下称步进)----------

/*1Hz	 步进00000024 1Hz的步进累加到100Hz时,和100Hz步进相差21<36,小于1Hz,所以将1Hz的累加最大值为100Hz
  10Hz   00000166
  100Hz	 步进00000DFB  100Hz累加步进到1000Hz时,步进值相差1<36,小于1Hz所以可以将1000Hz作为另一个阶段
  1kHz	 00008BCF
  10kHz  0005761A
  100kHz 00369D03
  1MHz   02222222
*/

always@(posedge clk_120M)  cnt <= cnt + 1'b1;

always @(negedge clk_in)
	begin
		step_num = 
		  32'h15555555 *freq_bcd[31:28]//*10MHz
		+ 32'h02222222 *freq_bcd[27:24]//*1MHz
		+ 32'h00369D03 *freq_bcd[23:20]//*100kHz
		+ 32'h0005761A *freq_bcd[19:16]//*10kHz
		+ 32'h00008BCF *freq_bcd[15:12]//*1kHz 
		+ 32'h00000DFB *freq_bcd[11:8] //*100Hz
		+ 32'h00000166 *freq_bcd[7:4]  //*10Hz
		+ 32'h00000024 *freq_bcd[3:0]; //*1Hz
	end

assign next_phase = step_num +accumulator;	

always @(posedge clk_120M)	accumulator<=next_phase;
	 
assign phase = accumulator[31:24]; //相位phase是高八位的数据

 

本项目要实现频率从DC-20MHz的范围,通过频率来设定频率调谐字,但是固定的一个频率的频率调谐字实现的只是非常接近这一频率的值,存在一定的误差。当我们要实现1Hz可调节,最高到达20MHz时,误差也会累加,产生很大的频率误差。所以采用这种分段计算的方法,使用频率的bcd码,计算得到给定频率下的频率调谐字。但是同时,这种方式也占用了比较多的逻辑门资源。

  • 二进制转8421bcd码
begin
		if(!rst)	//复位时清空所有寄存器
		begin
			state <= 0;
			bcd <= 0;
			regdata <= 0;
			regdata1 <= 0;
			w1 <= 0;
			w2 <= 0;
			w3 <= 0;
			w4 <= 0;
			w5 <= 0;
			w6 <= 0;
			w7 <= 0;
			w8 <= 0;
			q <= 0;
		end
		else
		case(state)
		0:	//初始状态,给寄存器赋初始值
			begin
				regdata <= bin;
				regdata1 <= bin;
				state <= 1;
				w1 <= 0;
				w2 <= 0;
				w3 <= 0;
				w4 <= 0;
				w5 <= 0;
				w6 <= 0;
				w7 <= 0;
				w8 <= 0;
				q <= 0;
			end

		1:	//移位状态,每移位1次计数器q值加1。
			begin
				q <= q + 1;
				regdata[24:0] <= (regdata[24:0] << 1);
				w1 <= {w1[2:0],regdata[24]};
				w2 <= {w2[2:0],w1[3]};
				w3 <= {w3[2:0],w2[3]};
				w4 <= {w4[2:0],w3[3]};
				w5 <= {w5[2:0],w4[3]};
				w6 <= {w6[2:0],w5[3]};
				w7 <= {w7[2:0],w6[3]};
				w8 <= {w8[2:0],w7[3]};
				if(q == 24)	
				begin
					state <= 3;	//转换完成后跳至状态3输出结果并等待
				end
				else
					state <= 2;	//未完成则跳至状态2判断每一位是否大于等于5
			end

		2: 	//判断每一位是否大于等于5,是则自加3,并跳回状态1进行下一次移位
			begin
				state <= 1;
				if(w1 >= 5)
					w1 <= w1 + 3;
				else
					w1 <= w1;
				if(w2 >= 5)
					w2 <= w2 + 3;
				else
					w2 <= w2;
				if(w3 >= 5)
					w3 <= w3 + 3;
				else
					w3 <= w3;
				if(w4 >= 5)
					w4 <= w4 + 3;
				else
					w4 <= w4;
				if(w5 >= 5)
					w5 <= w5 + 3;
				else
					w5 <= w5;
				if(w6 >= 5)
					w6 <= w6 + 3;
				else
					w6 <= w6;
				if(w7 >= 5)
					w7 <= w7 + 3;
				else
					w7 <= w7;
				if(w8 >= 5)
					w8 <= w8 + 3;
				else
					w8 <= w8;
			end

		3:	//完成状态,输出转换完成的BCD码并等待输入的变化
			begin
				bcd <= {w8,w7,w6,w5,w4,w3,w2,w1};//将符号位、个位~万位拼起来
				if(regdata1 != bin)	//regdata1不等于bin说明输入发生变化
					state <= 0;	//跳回初始状态,以进行下一次转换
				else
					state <= 3;	//输入没变化则停留在此状态等待
			end
		endcase
	end

为了进行更为准确的频率调谐字设置和OLED的频率显示,都需要获得频率值在十进制下的各位数字,直接想法是采用除法,但是除法在硬件中占用的资源过多,所以选择更好的二进制转bcd码,直接从各位取出想要的值即可。

采用的是左移加三算法,利用有限状态机进行控制。状态0:初始化状态;状态1:左移状态;状态2:加三状态;状态3:比较与等待状态。在状态三中,出现输入的二进制数发生变化时,重新返回状态0,开始下一次的转化

  • lookup_tables的镜像处理
begin
	case(sel)
	2'b00: 	begin
			sine_onecycle_amp = 9'h1ff + sine_table_out[8:0];
			address = phase[5:0];
	     	end
  	2'b01: 	begin
			sine_onecycle_amp = 9'h1ff + sine_table_out[8:0];
			address = ~phase[5:0];
	     	end
  	2'b10: 	begin
			sine_onecycle_amp = 9'h1ff - sine_table_out[8:0];
			address = phase[5:0];
     		end
  	2'b11: 	begin
			sine_onecycle_amp = 9'h1ff - sine_table_out[8:0];
			address = ~ phase[5:0];
     		end
	endcase
end
  • OLED显示频率
5'd0:	begin state <= INIT; end
5'd1:	begin y_p <= 8'hb0; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= wave_decide;state <= SCAN; end
5'd2:	begin y_p <= 8'hb1; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "                           ";state <= SCAN; end
5'd3:	begin y_p <= 8'hb2; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "Frequency       ";state <= SCAN; end
5'd4:	begin y_p <= 8'hb3; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "                           ";state <= SCAN; end
5'd5:	begin y_p <= 8'hb3; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; 
char <= {2'b0,ww,2'b0,qw,2'b0,bw,2'b0,sw,2'b0,gw,8'd46,4'b0,sfw,4'b0,bfw,4'b0,qfw,8'd32,8'd107,8'd72,8'd122,8'd32,8'd32,8'd32}; 
state <= SCAN; end

 

 

顶层RTL视图

FqJFCk2E2CULeW0oc3ipKIVifZMO

该程序资源占用报告如下

FobfEgQhNpcc7BKf15tGXflnz9-g

实现的功能及图片展示

波形显示使用的是之前在某购物平台购买的DSO150贝壳示波器,参数如下:最高实时取样率1Msps,模拟带宽0-200KHz,取样缓冲深度1024字节,输入阻抗1MΩ,

正弦信号输出

Fhf_64Y5mc4rr19O2f9ztnU5WYYc

展示100Hz正弦波信号输出

方波信号输出

Ft5rhmPuw9vk828rZpdnKlva20S0

展示100Hz方波

三角波信号输出

Foy6PCJ_rZZgE9MKjtI-J7XzYG7Q

展示100Hz三角波

DC输出

FmpFkw8TlWtZLG6TkG8q8leTiDNL

直流信号输出

频率可调

FmxpvseHxVxNbYgSCf6__wNq9BHn

正弦信号调整位1000Hz

波形信息显示

FnuGZIv7KvfXMc90Dow7vFo5Po39

遇到的主要难题及解决方法

问题1:无法有效的调用OLED显示信息

在系统的了解了OLED显示的具体方式后,明白写内容时是按照固定的位置进行,写固定的位数,对例程进行针对性的修改后实现了信息的显示。

问题2:使用除法获取频率各位数字占用资源过多

转变思路,从之前使用FPGA制作的简易计算器获取思路,可以转化为二进制转bcd码,可以大大的减少资源占用,时间也会大大减少

问题3:对DDS等知识的首次接触

多利用平台上的资料和例程进行理解,利用示波器看到波形显示可以更快的理解

未来的计划或建议

通过继续完成其他项目来熟练运用开发板上的元件,包括旋转编码器和ADC。

同时能够更加透彻的理解各元件内部运作的逻辑。

本项目中的三角波也是使用查找表实现的,但是这种较为简单的波形可以通过函数实现,占用的寄存器会更少,可以进行改进。频率调谐字的设定也占用比较多的资源,应该可以通过计算比对,找到满足精度的计算方式,比如可以每百位作为一个分段。

附件下载
generator.zip
程序完整代码
generator_impl1.jed
jed可烧录文件
团队介绍
张茂森 北京邮电大学
团队成员
森派陈醋
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号