3349|0

172

帖子

3

TA的资源

一粒金砂(高级)

楼主
 

【国产FPGA评测】安路(型号SF1S60CG121I) 04并行DAC的DDS模块设计 [复制链接]

  本帖最后由 EPTmachine 于 2023-3-21 15:02 编辑

1.1 硬件介绍

在Demo板的一侧,共有16个可供输出的IO,并且提供了3.3V的供电输出,用户扩展IO的示意图如图所示,

SF1S60CG121I 管脚位置

SF1S60CG121I 管脚名称

IO电源域

IO名称

IO序号

H9

IO_R1P_0,GCLK3

2.5V

USER_IO0_N

3

G9

IO_R1N_0,GCLK2

2.5V

USER_IO0_P

4

H8

IO_R0N_0,GCLK0

2.5V

USER_IO1_N

5

H7

IO_R0P,GCLK1

2.5V

USER_IO1_P

6

D11

IO_R7N_0

2.5V

USER_IO2_N

7

D10

IO_R7P_0

2.5V

USER_IO2_P

8

C11

IO_R8N_0,GCLK10,D3

2.5V

USER_IO3_N

9

C10

IO_R8P_0,GCLK11,D2

2.5V

USER_IO3_P

10

B11

IO_R9N_0

2.5V

USER_IO4_N

11

B10

IO_R9P_0

2.5V

BUZZER

12

A11

IO_R10N_0,GCLK12,D1

2.5V

USER_IO5_N

13

A10

IO_R10P_0,GCLK13,D0

2.5V

USER_IO5_P

14

B8

IO_R11N_0

2.5V

USER_IO6_N

15

B9

IO_R11P_0

2.5V

USER_IO6_P

16

A8

IO_R12N_0,GCLK14,USRCLK

2.5V

USER_IO7_N

17

A9

IO_R12P_0,GCLK15,SCLK

2.5V

USER_IO7_P

18

以上资源搭配硬禾学堂的电赛训练上的10位告诉DAC芯片,可以实现DDS功能,电赛训练板的原理图如下

 

实物接线图如下,电赛训练板的供电由Demo板的3.3V输出端提供。

 

Demo板上的按键电路和对应的控制引脚如下:

按键外接上拉电阻,默认为高电平。按键控制引脚与FPGA管脚的映射关系为:

SF1S60CG121I 管脚位置

SF1S60CG121I 管脚名称

IO电源域

按键名称

H3

IO_B2N_2,GCLK2

1.2V

KEY0

H2

IO_B4P_2

1.2V

KEY1

G3

IO_B2P_2

1.2V

KEY2

 

1.2 ROM IP核使用和实现正弦波表

SF1器件支持的ERAM9K类型的嵌入式存储器模块(ERAM),可以实现ROM、RAM和FIFO等模块。使用起来的配置还是很方便的。

本次演示的DDS使用四分之一的10位正弦波表,数据位宽选择16位,表的深度选择64,完整的波表可以通过相位变换获得,原理是居于对称性来实现的。

但是在实际使用过程中出现了下述的编译错误,具体原因不明。

这里我选择使用LUT的实现方式。代码如下

module sin_table(address,sin);
output [8:0] sin;         //实际波形表为9位分辨率(1/4周期)
input  [5:0] address;     //64个点来生成1/4个周期的波形,完整的一个周期为256个点
 
reg    [8:0] sin;
 
always @(address)
	begin
                  case(address)	
                      6'h0: sin=9'h0;
                      6'h1: sin=9'hC;
                      6'h2: sin=9'h19;
                      6'h3: sin=9'h25;
                      6'h4: sin=9'h32;
                      6'h5: sin=9'h3E;
                      6'h6: sin=9'h4B;
                      6'h7: sin=9'h57;
                      6'h8: sin=9'h63;
                      6'h9: sin=9'h70;
                      6'ha: sin=9'h7C;
                      6'hb: sin=9'h88;
                      6'hc: sin=9'h94;
                      6'hd: sin=9'hA0;
                      6'he: sin=9'hAC;
                      6'hf: sin=9'hB8;
                      6'h10: sin=9'hC3;
                      6'h11: sin=9'hCF;
                      6'h12: sin=9'hDA;
                      6'h13: sin=9'hE6;
                      6'h14: sin=9'hF1;
                      6'h15: sin=9'hFC;
                      6'h16: sin=9'h107;
                      6'h17: sin=9'h111;
                      6'h18: sin=9'h11C;
                      6'h19: sin=9'h126;
                      6'h1a: sin=9'h130;
                      6'h1b: sin=9'h13A;
                      6'h1c: sin=9'h144;
                      6'h1d: sin=9'h14E;
                      6'h1e: sin=9'h157;
                      6'h1f: sin=9'h161;
                      6'h20: sin=9'h16A;
                      6'h21: sin=9'h172;
                      6'h22: sin=9'h17B;
                      6'h23: sin=9'h183;
                      6'h24: sin=9'h18B;
                      6'h25: sin=9'h193;
                      6'h26: sin=9'h19B;
                      6'h27: sin=9'h1A2;
                      6'h28: sin=9'h1A9;
                      6'h29: sin=9'h1B0;
                      6'h2a: sin=9'h1B7;
                      6'h2b: sin=9'h1BD;
                      6'h2c: sin=9'h1C3;
                      6'h2d: sin=9'h1C9;
                      6'h2e: sin=9'h1CE;
                      6'h2f: sin=9'h1D4;
                      6'h30: sin=9'h1D9;
                      6'h31: sin=9'h1DD;
                      6'h32: sin=9'h1E2;
                      6'h33: sin=9'h1E6;
                      6'h34: sin=9'h1E9;
                      6'h35: sin=9'h1ED;
                      6'h36: sin=9'h1F0;
                      6'h37: sin=9'h1F3;
                      6'h38: sin=9'h1F6;
                      6'h39: sin=9'h1F8;
                      6'h3a: sin=9'h1FA;
                      6'h3b: sin=9'h1FC;
                      6'h3c: sin=9'h1FD;
                      6'h3d: sin=9'h1FE;
                      6'h3e: sin=9'h1FF;
                      6'h3f: sin=9'h1FF;
                   endcase
              end
endmodule

1.3 DDS波形频率和类型控制模块

关于波形的频率控制,其核心公式如下,fout为输出频率,fmclk为DDS的主时钟,Nmax为n位计数器的最大值,m为每个时钟周期的相位累加值。文中介绍的DDS发生器采用28位的相位累加器,通过改变步长m,来实现不同频率的输出。

 

关键代码如下:

	reg [27:0] dds_phase;
	wire [27:0] dds_phase_add;
	assign dds_phase_add = wave_step;
	always@(posedge sys_clk or negedge rst_n)begin
		if(!rst_n)begin
			dds_phase <= 28'd0;
		end else begin
			dds_phase <= dds_phase + dds_phase_add;
		end
	end

DDS输出的波形的类型中的方波、三角波、锯齿波通过相位累加器的高十位来控制。

方波使用相位累加器的最高位作为输出数据,在相位累加器从0到上限值的过程中,最高位在一半的时间内为0,在另一半的时间内为1,由于方波只需要DAC的两个量化值,数据全为0为最小值,数据全为1为最大值,对应DAC的数据引脚输出相应的数据位即可。关键代码如下:

	assign square_dac = {10{dds_phase[27]}};

对于锯齿波,其输出电压信号,从DAC的最小值变化到最大值,取相位累加器的高10位作为DAC芯片的数据引脚的数据来源,就可以生成锯齿波,关键代码如下:

assign saw_dac = dds_phase[27:18];

对于三角波,其和锯齿波类似,不过由于其DAC的量化数据在单个周期内由0到1023然后从1023到0,相同周期下,其计数溢出的周期比锯齿波快一倍,而且计数方向会发生变化,所以,在生成锯齿波波形的基础上,使用相位累加器的最高位作为三角波相位的选取标志位,紧随其后的10位作为DAC数据端口的数据来源,根据不同相位对数据进行取反或不取反处理,从而实现三角波的输出。关键代码如下:

assign trig_dac = dds_phase[27] ? ~dds_phase[26:17] : dds_phase[26:17];

而正弦波则通过相位累加器的高八位作为正弦波表的地址,通过查表得到对应的10位DAC的量化值。前面实现了四分之一的正弦波表的地址与输出DAC量化值之间的关系。正弦波与三角波类似,只不过它有四个象限,所以在使用的时候,需要两位数据来确定波形位于正弦波的哪个象限,使用相位累加器的高两位确定象限,其后的六位作为波表的寻址控制位。两者结合而从而得到一个完整的正弦波输出。关键代码如下:

//查找表调用
	lookup_tables u_lut(
	.phase(dds_phase[27:20]),
	.sin_out(sin_dac)
	);


//////////////////////////////
//查找表实现
module lookup_tables(//sys_clk,rst_n,
	phase, sin_out);

//input sys_clk;
//input rst_n;
input  	[7:0] 	phase;
output 	[9:0] 	sin_out;
 
wire     [9:0]   sin_out;
 
reg   	[5:0] 	address;
wire   	[1:0] 	sel;
wire   	[11:0] 	sine_table_out;
 
reg     [9:0]   sine_onecycle_amp;
 
assign sin_out = sine_onecycle_amp[9:0];
 
assign sel = phase[7:6];
 
sin_table u_sin_table(address,sine_table_out);

always @(sel or sine_table_out)
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
 
endmodule

1.4 DDS幅度控制模块

通过DAC芯片输出波形时,在DAC芯片的输入端,其输入位数决定了输出信号的量化值范围,上面的输出信号使用0变化到1023的,也就是在DAC芯片的满量程上进行的。如果要控制输出信号的峰峰值,这时需要给输出的数据进行除法操作,直接除的话会有损失很多精度。这里先将输出的量化值乘一个值x(0-255),然后除以255,就可以得到一个按照比例衰减的输出信号。测试中使用的DAC的输出范围时2.56V-0V,上面的x每个值可以对应一个单位0.01V的电压值。幅度控制的关键代码如下:

module dds_amp_adjust(
	clk,
	rst_n,
	wave_amp,
	signal_dat,
	
	dac_dat
);

	input clk,rst_n;
	input [7:0] wave_amp;//用于调幅的因数
	input [9:0] signal_dat;//未调幅的波形数据
	
	output [9:0] dac_dat;//输出给DAC的数据
	
	reg [17:0] amp_dat;//调幅后的波形数据
		
	always @(posedge clk) begin
		amp_dat<=signal_dat*wave_amp;//波形数据乘以调幅系数
	end

	assign dac_dat=amp_dat[17:8];//取高十位,相当于右移8位,除256

endmodule

1.5 按键输入检测模块

Demo板上共有三个按键,这里正好可以实现对信号的类型、频率、幅度的控制。按键的消抖程序如下:

module debounce(
    //input
	clk,
    rst_n,
    key,
    //output
    key_pluse);
    
    parameter N=1;			//要消除的按键数量

	input		clk;
    input 		rst_n;
    input [N-1:0]	key;		//输入的按键
    output [N-1:0]	key_pluse;	//按键动作产生的脉冲
    
    reg		[N-1:0] key_signal_pre;
    reg		[N-1:0]	key_signal;
    
    wire 	[N-1:0]	key_edge;
    

    always @(posedge clk or negedge rst_n)
    	begin
        	if(!rst_n)begin
            	key_signal<={N{1'b1}};
                key_signal_pre<={N{1'b1}};
            end	
        	else begin
            	key_signal<=key;
                key_signal_pre<=key_signal;
            end
        end	

	assign key_edge = key_signal_pre&(~key_signal);

	reg [17:0] cnt;
    
    always @(posedge clk or negedge rst_n)
    	begin
        	if(!rst_n)
            	cnt<=18'h0;
			else if(key_edge)
            	cnt<=18'h0;
            else
            	cnt<=cnt+1'b1;	
        end

	reg [N-1:0] key_sec_pre;
    reg [N-1:0] key_sec;

	always @(posedge clk or negedge rst_n)
    	begin
        	if(!rst_n)
            	key_sec<={N{1'b1}};
            else if(cnt==18'h3ffff)
            	key_sec<=key;
        end
        
   	always @(posedge clk or negedge rst_n)
       begin
			if(!rst_n)
            	key_sec_pre<={N{1'b1}};
            else
            	key_sec_pre<=key_sec;
       end

	assign	key_pluse = key_sec_pre & (~key_sec);

endmodule

消抖模块在确定一次有效输入后,发出一个脉冲信号,用于其他模块检测按键的上升沿,在检测到上升沿后,切换不同的输出类型、频率和幅度。关键代码如下:

module dds_waveparactrl(
    input clk,
    input rst_n,
    input wave_type_ctrl,
    input wave_freq_ctrl,
    input wave_amp_ctrl,
    output reg [2:0]    wave_type,
    output reg [27:0] wave_freqm,
    output reg [7:0] wave_amp
);


//波形输出形态控制
reg wave_type_ctrlr;//滤波后输出的按键脉冲为低电平

always@(posedge clk,negedge rst_n)begin
    if(!rst_n)begin
		wave_type_ctrlr <= 1'b0;
    end      
    else begin
    	wave_type_ctrlr<=wave_type_ctrl;
    end            
end

wire wave_type_ctrl_pos;
assign wave_type_ctrl_pos=!wave_type_ctrlr&&wave_type_ctrl;

reg [1:0]type_cnt;

always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin					   
		wave_type <= 3'b000;
		type_cnt<=2'd1;
	end
    else if(wave_type_ctrl_pos)begin
		case(type_cnt)
			2'd0:begin wave_type <= 3'b000; type_cnt<=type_cnt+1'b1;end
			2'd1:begin wave_type <= 3'b001; type_cnt<=type_cnt+1'b1;end
			2'd2:begin wave_type <= 3'b010; type_cnt<=type_cnt+1'b1;end
			2'd3:begin wave_type <= 3'b011; type_cnt<=type_cnt+1'b1;end
		endcase
		
	end
end

//波形输出频率控制
reg wave_freq_ctrlr;//滤波后输出的按键脉冲为低电平

always@(posedge clk,negedge rst_n)begin
    if(!rst_n)begin
		wave_freq_ctrlr <= 1'b0;
    end      
    else begin
    	wave_freq_ctrlr<=wave_freq_ctrl;
    end            
end

wire wave_freq_ctrl_pos;
assign wave_freq_ctrl_pos=!wave_freq_ctrlr&&wave_freq_ctrl;

reg [2:0]freqm_cnt;

always@(posedge clk or negedge rst_n)
begin
    if(!rst_n)begin				   
    	wave_freqm <= 28'd5369;	//50M时钟频率下,1KHz信号的步进值m
		freqm_cnt <=3'd0;
    end		
    else if(wave_freq_ctrl_pos)begin
        case(freqm_cnt)
		3'd0:begin wave_freqm <= 28'd5369;	freqm_cnt<=freqm_cnt+1'd1;end					//1KHz
        3'd1:begin wave_freqm <= 28'd53690;	freqm_cnt<=freqm_cnt+1'd1;end					//10KHz
        3'd2:begin wave_freqm <= 28'd268450;freqm_cnt<=freqm_cnt+1'd1;end					//50KHz
        3'd3:begin wave_freqm <= 28'd536900;freqm_cnt<=freqm_cnt+1'd1;end					//100KHz
        3'd4:begin wave_freqm <= 28'd5369000;	freqm_cnt<=freqm_cnt+1'd1;end				//1MHz
        3'd5:begin wave_freqm <= 28'd10738000;	freqm_cnt<=freqm_cnt+1'd1;end					//2MHz
        3'd6:begin wave_freqm <= 28'd26845000;	freqm_cnt<=freqm_cnt+1'd1;end					//5MHz
		3'd7:begin wave_freqm <= 28'd10738;	freqm_cnt<=freqm_cnt+1'd1;end					//2KHz
        default:;
        endcase
	end
end

//波形输出幅度控制
reg wave_amp_ctrlr;//滤波后输出的按键脉冲为低电平

always@(posedge clk,negedge rst_n)begin
    if(!rst_n)begin
		wave_amp_ctrlr <= 1'b0;
    end      
    else begin
    	wave_amp_ctrlr<=wave_amp_ctrl;
    end            
end

wire wave_amp_ctrl_pos;
assign wave_amp_ctrl_pos=!wave_amp_ctrlr&&wave_amp_ctrl;

reg [2:0] amp_cnt;

always@(posedge clk or negedge rst_n)
begin
    if(!rst_n)begin				   
    	wave_amp <= 8'd255;	//48M时钟频率下,1KHz信号的步进值m
		amp_cnt <=3'd0;
    end		
    else if(wave_amp_ctrl_pos)begin
        case(amp_cnt)
		3'd0:begin wave_amp <= 8'd255;	amp_cnt<=amp_cnt+1'd1;end					//2.56V
        3'd1:begin wave_amp <= 8'd200;	amp_cnt<=amp_cnt+1'd1;end					//2.00V
        3'd2:begin wave_amp <= 8'd180;	amp_cnt<=amp_cnt+1'd1;end					//1.8V
        3'd3:begin wave_amp <= 8'd170;	amp_cnt<=amp_cnt+1'd1;end					//1.7V
        3'd4:begin wave_amp <= 8'd150;	amp_cnt<=amp_cnt+1'd1;end				//1.5V
        3'd5:begin wave_amp <= 8'd140;	amp_cnt<=amp_cnt+1'd1;end					//1.4V
        3'd6:begin wave_amp <= 8'd130;	amp_cnt<=amp_cnt+1'd1;end					//1.3V
		3'd7:begin wave_amp <= 8'd100;	amp_cnt<=amp_cnt+1'd1;end					//1.0V
        default:;
        endcase
	end
end

endmodule

1.6 实验演示

dds演示

1.7 总结

这次实验一共占用了11个用户IO,而Demo板上的用户IO一共16个,本来计划是使用LCD,配合来进行参数设置的,当然,板上自带一个128x32的OLED屏幕,设置DDS参数还是可以的,但是要进行ADC采样的话可能不够,不过实验中的电赛训练板上有一块128x64的OLED,用来显示波形还是够的,后面的信息显示就使用OLED屏幕了。

工程附件 DDS_Parallel_DAC.7z (966.22 KB, 下载次数: 5)

 

 

 

点赞 关注
 
 

回复
举报
您需要登录后才可以回帖 登录 | 注册

查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/8 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表