2021暑假一起练平台1-上位机控制的可调波形、幅度、频率的信号发生器
基于小脚丫FPGA的DDS信号发生器-可以上位机控制的可调波形、幅度、频率输出的信号发生器
标签
FPGA
DDS
大写DDG
更新2021-10-08
1227

上位机控制的可调波形、幅度、频率输出的信号发生器

 

一、项目需求

综合性题目 - 制作一个可以上位机控制的可调波形、幅度、频率输出的信号发生器

  1. 能够产生正弦波、三角波、方波,可以通过小脚丫上的拨码开关控制波形的切换。
  2. 产生信号的幅度0-3Vpp之间可调,调节分辨率精确到10mV,可以通过电位计进行调节。
  3. 产生信号的频率100Hz - 200KHz之间可调,频率调节分辨率可达10Hz。
  4. 将1KHz - 10KHz频率范围的信号同时送到蜂鸣器,驱动蜂鸣器发出声音,调节频率和信号的幅度会改变蜂鸣器的声音输出。
  5. 产生的波形(图示)、波形的幅度、波形的频率都实时显示在OLED屏幕上在PC上编写控制界面,可以采用LabView、Matlab或其它工具,在PC上通过界面改变波形、波形的幅度和频率参数,并通过UART将设置传输到FPGA板上对波形进行调节。
  6. PC的控制和板卡上的控制同时有效。

二、整体模块示意图

FsUCNW1KsppSAQchSbUCvTz4xNUd

  • 板载数据处理模块:处理板载的按键、拨码开关、和电位计等数据,输出板载控制的信号类型board_signal、信号频率(3个参数board_x10_n、board_1k_n、board_10k_n)、信号幅度board_range、和串口准备发送标志信号board_change_flag等数据到顶层模块。
  • 串口收发模块:负责串口的收发,绝大多数采用ASCII码收发。
  • OLED显示模块:负责当前波形的类型、频率、幅度的显示。
  • 波形驱动模块:负责将当前标定控制的信号类型、频率和幅度以10bits的数字信号输出。并且输出当前频率所对应的三角波(未调幅)到蜂鸣器驱动模块。
  • 蜂鸣器驱动模块:负责将当前频率在1k~10k的范围时候来驱动控制蜂鸣器音调,同时引入幅度值来调节蜂鸣器音高低。
  • 顶层模块:负责各个模块的调度和标志控制信号。

 

三、板载数据处理模块和实物展示

主要包括按键拨码开关旋转电位计的数据处理。板载控制示意图如下:

Fja7pPFalkMW6K0bOEuQlQ0vaC6m

按键从左到右分别定义为①号按键±10Hz、②号按键±1kHz、③号按键±10kHz和复位按键。控制如上图所示。

其中电位计和ADS7868的驱动代码我直接参考了项目页面的示例代码(简易电压表设计

本模块的重点有两个,分别是:

1.三个按键和拨码开关4控制波形的频率。

2.获得板载数据改变标志位(用于触发串口发送)

代码如下

module board_data(
	input clk,
	input rst,
	input [2:0]key,
	input  [2:0]sw,
	
	input	    dat,	//SPI总线SDA
	output		 cs,	//SPI总线CS
	output	   sclk,	//SPI总线SCK
	
	output  reg [1:0]board_signal,
	output  reg [6:0]board_x10_n,
	output  reg [3:0]board_x1k_n,
	output  reg [4:0]board_x10k_n,
	output  reg [7:0]board_range,
	
	output  reg board_change_flag
);
	
	wire [2:0]key_pulse;
	//例化按键消抖模块
	debounce #(.N(3)) u0(
		.clk(clk),
		.rst(rst),
		.key(key),
		.key_pulse(key_pulse)
	);
	
	
	wire       adc_done;
	wire  [7:0]adc_data;
	wire adc_cs,adc_clk;	  
	assign	cs   =  adc_cs; 
	assign	sclk =  adc_clk;
	ADC7868_driver uadc(
		.clk(clk),
		.rst_n(rst),
		.adc_cs(adc_cs),
		.adc_clk(adc_clk),
		.adc_dat(dat),
		.adc_done(adc_done),
		.adc_data(adc_data)
	);
	
	//----------------板载数据改变标志判断--------------------
	reg [2:0]key_pulse_pre;
	always @(posedge clk)
		key_pulse_pre <= key_pulse;
	
	reg [2:0]sw_pre;
	always @(posedge clk)
		sw_pre <= sw;
	
	reg [16:0]cnt10ms;
	always @(posedge clk or negedge rst) begin
		if(!rst) 
			cnt10ms <= 17'd0;
		else if(cnt10ms == 17'd120_000)
			cnt10ms <= 17'd0;
		else 
			cnt10ms <= cnt10ms + 1'b1;
	end
	
	reg [7:0]board_range_gap;
	reg [7:0]board_range_pre;
	always @(posedge cnt10ms[16])
		board_range_pre <= board_range;
	
	always @(posedge clk) begin
		if(board_range_pre >= board_range)
			board_range_gap <= board_range_pre - board_range;
		else 
			board_range_gap <= board_range - board_range_pre;
	end
			
			
	always @(posedge clk or negedge rst) begin
		if(!rst) 
			board_change_flag <= 1'b0;		
		else if(key_pulse_pre != key_pulse)
			board_change_flag <= 1'b1;
		else if(sw_pre != sw)
			board_change_flag <= 1'b1;
		//else if(board_range_gap >= 8'd3)
			//board_change_flag <= 1'b1;
		else 
			board_change_flag <= 1'b0;
	end
	


	//波形类型选择(板载)
	//reg [1:0]board_signal;
	always @(*)
		case(sw[1:0])
			2'b00: board_signal <= 2'b00;
			2'b01: board_signal <= 2'b01;
			2'b10: board_signal <= 2'b10;
			default:;
		endcase
		
		
	//波形调幅因数(板载)
	//reg [7:0]board_range; 

	//板载按键调频逻辑进位借位
	//reg [6:0]board_x10_n;
	//reg [3:0]board_x1k_n;
	//reg [4:0]board_x10k_n;
	always @(posedge clk or negedge rst)
		if(!rst) begin
			board_x10_n <= 7'd10;
			board_x1k_n <= 4'd0;
			board_x10k_n <= 5'd0;
		end
		else begin
			case({sw[2],key_pulse})
				4'b0100: begin
					if(board_x10_n == 7'd99) begin        // + 10						
						if(board_x1k_n == 4'd9)begin							
							if(board_x10k_n < 5'd20) begin
								board_x10_n <= 7'd0;
								board_x1k_n <= 4'd0;
								board_x10k_n <= board_x10k_n + 1'b1;
							end
						end
						else begin
							board_x10_n <= 7'd0;
							board_x1k_n <= board_x1k_n + 1'b1;
						end
					end
					else if(board_x10k_n < 5'd21)
						board_x10_n <= board_x10_n + 1'b1;
				end
						
				4'b0010: begin                            //+ 1k
					if(board_x1k_n == 4'd9)	 begin						
						if(board_x10k_n < 5'd21) begin
							board_x1k_n <= 4'd0;
							board_x10k_n <= board_x10k_n + 1'b1;
						end
					end
					else if(board_x1k_n < 4'd9) begin
						if(board_x10k_n < 5'd21)
							board_x1k_n <= board_x1k_n + 1'b1;
					end
				end
					
				4'b0001: 
					if(board_x10k_n < 5'd20) 
						board_x10k_n <= board_x10k_n + 1'b1; 
						
				4'b1100: begin
					if(board_x10_n > 7'd10)
						board_x10_n <= board_x10_n - 1'b1;
					else begin
						if(board_x10_n == 7'd0)
							if(board_x1k_n == 4'd0)
								if(board_x10k_n == 5'd0)
									;
								else begin
									board_x10k_n <= board_x10k_n - 1'b1;
									board_x1k_n <= 4'd9;
									board_x10_n <= 7'd99;
								end
							else begin
								board_x1k_n <= board_x1k_n - 1'b1;
								board_x10_n <= 7'd99;
							end
						else 
							if(board_x1k_n == 4'd0 && board_x10k_n == 5'd0)
								;
							else
								board_x10_n <= board_x10_n - 1'b1;					
					end
				end
						
				4'b1010: begin
					if(board_x1k_n == 4'd0) begin
						if(board_x10k_n == 5'd0) 
							;
						else if(board_x10k_n > 5'd0)begin
							board_x10k_n <= board_x10k_n - 1'b1;
							board_x1k_n <= 4'd9;
						end
					end
					else if(board_x1k_n == 4'd1) begin
						if(board_x10k_n == 5'd0) begin
							if(board_x10_n >= 7'd10)
								board_x1k_n <= board_x1k_n - 1'b1;
						end
						else if(board_x10k_n > 5'd0)begin
							board_x1k_n <= board_x1k_n - 1'b1;
						end
					end
					else if(board_x1k_n >= 4'd2) begin
						board_x1k_n <= board_x1k_n - 1'b1;
					end
				end
						
				4'b1001: begin
					if(board_x10k_n == 5'd1) begin
						if(board_x10_n >= 7'd10)
							board_x10k_n <= board_x10k_n - 1'b1;
						else if(board_x1k_n != 4'd0)
							board_x10k_n <= board_x10k_n - 1'b1;							
					end
					else if(board_x10k_n >= 5'd2) begin
						board_x10k_n <= board_x10k_n - 1'b1;
					end
				end
						
				default: ;
			endcase
		end
	
	//波形幅度选择

	always @(posedge clk or negedge rst) begin
		if(adc_done == 1'b1)   	
			board_range <= adc_data;
	end
	
	
endmodule

 

四、串口收发模块

  串口收发1个字节的驱动代码直接参考项目页面的示例代码(参考串口监视系统设计),下图为串口收发一个字节数据的rtl视图。

FplI4_OmTSk43J8DkkkDXhu9dm4O

本设计是信号发生器,自定义了串口协议。

1.字符 “s” / “t” / “q”  分别代表正弦波/三角波/方波

2.字符 “F”  代表 频率

3/4/5/6/7. 字符“0”~“9” 代表频率值前五位

8.字符“U” 代表 幅值

9/10/11/12.字符“0”~“3” 代表 幅值因数 “——” “——” “——” “——” (高~低,两位代表一个字符,共8位,也就是4个字符)

13.结束字符 “$”

代码如下:

module uart(
	input clk,
	input rst,
	input uart_rxd_pin,
	input txd_ready_flag,          //串口准备发送标志
	input [1:0]uart_t_signal,      //波形名称
	input [6:0]uart_t_x10_n,
	input [3:0]uart_t_x1k_n,
	input [4:0]uart_t_x10k_n,
	input [7:0]uart_t_range,       //幅度因数
	
	output uart_txd_pin,
	output reg rxd_finish_flag,    //串口接收完成标志
	output [1:0]uart_r_signal, //波形名称
	output [6:0]uart_r_x10_n,
	output [3:0]uart_r_x1k_n,
	output [4:0]uart_r_x10k_n,
	output [7:0]uart_r_range   //幅度因数
);	

	//------------------串口发送和接收状态机---------------------
	//-----------------------------------------------------------
	reg [7:0]txd_state,txd_next_state;
	localparam  //串口发送状态
		txd_s1  = 8'b0000_0001,
		txd_s2  = 8'b0000_0010,
		txd_s3  = 8'b0000_0100,
		txd_s4  = 8'b0000_1000,
		txd_s5  = 8'b0001_0000,
		txd_s6  = 8'b0010_0000,
		txd_s7  = 8'b0100_0000,
		txd_s8  = 8'b1000_0000;

		
		
	reg [4:0]rxd_state;		
	localparam  //串口接收状态
		rxd_s1  = 4'b0001,
		rxd_s2  = 4'b0010,
		rxd_s3  = 4'b0100,
		rxd_s4  = 4'b1000;
	//-----------------------------------------------------------
	
	
	
	//----------------串口发送字节间隔计数器---------------------
	//-----------------------------------------------------------
	reg    [7:0]byteinterval_cnt;  //串口发送字节间隔计数器
	reg         byteinterval_flag; //串口发送字节间隔标志信号
	localparam  byte_interval = 8'd80;
	
		//串口发送间隔计数器计数 控制
	always @(posedge clk or negedge rst) begin
		if(!rst)
			byteinterval_cnt <= 0;
		else if(byteinterval_flag == 1'b1) 
			byteinterval_cnt <= byteinterval_cnt + 1'b1;
		else if(byteinterval_flag == 1'b0)
			byteinterval_cnt <= 0;
	end
	//------------------------------------------------------------
	
	
	//--------------------例化数据转换模块-----------------------
	//-----------------------------------------------------------
	reg [1:0]rxd_signal;
	reg [19:0]rxd_frequency;
	reg [7:0]rxd_range;
	
	wire [7:0]txd_signal;
	wire [19:0]txd_frequency;
	wire [15:0]txd_range; 
	
	wire [1:0]uart_r_signal0;
	wire [6:0]uart_r_x10_n0;
	wire [3:0]uart_r_x1k_n0;
	wire [4:0]uart_r_x10k_n0;
	wire [7:0]uart_r_range0;
	assign uart_r_signal = uart_r_signal0;
	assign uart_r_x10_n = uart_r_x10_n0;
	assign uart_r_x1k_n = uart_r_x1k_n0;
	assign uart_r_x10k_n = uart_r_x10k_n0;
	assign uart_r_range = uart_r_range0;
	
	serial_data_conversion uconversion(
	//串口发送数据(转换前)
		.uart_t_signal(uart_t_signal),      //波形名称
		.uart_t_x10_n(uart_t_x10_n),
		.uart_t_x1k_n(uart_t_x1k_n),
		.uart_t_x10k_n(uart_t_x10k_n),
		.uart_t_range(uart_t_range),       //幅度因数
	//串口接收数据(转换前)
		.rxd_signal(rxd_signal),
		.rxd_frequency(rxd_frequency),
		.rxd_range(rxd_range),
	
	//串口发送数据(转换后)
		.txd_signal(txd_signal),
		.txd_frequency(txd_frequency),
		.txd_range(txd_range),  
	
	//串口接收数据(转换后)
		.uart_r_signal(uart_r_signal0), //波形名称
		.uart_r_x10_n(uart_r_x10_n0),
		.uart_r_x1k_n(uart_r_x1k_n0),
		.uart_r_x10k_n(uart_r_x10k_n0),
		.uart_r_range(uart_r_range0)   //幅度因数
	);
	//-----------------------------------------------------------

	
	
	//------------------例化串口收发1byte模块---------------------
	//-----------------------------------------------------------
	wire [7:0]rx_1byte_data;
	reg  [7:0]tx_1byte_data;
	reg  tx_data_valid;
	wire rx_data_valid;
	wire uart_tx_pin;
	wire tx1byte_finish_flag;     //发送过程中=0  发送完成和空闲时间=1
	assign uart_txd_pin = uart_tx_pin;
	uart_1byte uart_1byte0(
		.clk(clk),
		.rst(rst),
		.uart_rxd_pin(uart_rxd_pin),      //串口接收引脚
		
		.rx_data_out(rx_1byte_data),	  //串口接收的数据
		.rx_data_valid(rx_data_valid),    //串口接收一段数据的标志位
		
		.tx_data_in(tx_1byte_data),       //串口要发送的数据
		.tx_data_vaild(tx_data_valid),    //串口准备发送送数据脉冲标志信号 
		
		.uart_txd_pin(uart_tx_pin),        //串口发送引脚		
		.tx1byte_finish_flag(tx1byte_finish_flag)
	);
	//-----------------------------------------------------------
	
	
	//---------------------------串口发送协议模块------------------------------------
	//-------------------------------------------------------------------------------
	reg [2:0]nX4_flag;
	reg tx_data_valid;
	reg [7:0]tx_1byte_data;
	reg [19:0]txd_f;
	reg [15:0]txd_r;
	always @(posedge clk or negedge rst) begin
		if(!rst) begin
			txd_state <= txd_s1;
			tx_data_valid <= 1'b0;
			nX4_flag <= 3'd0;
		end
		else begin
			case(txd_state)
				txd_s1: begin
					if(txd_ready_flag) begin
						txd_r <= txd_range;
						txd_f <= txd_frequency;
						
						tx_1byte_data <= txd_signal;  // s/t/q 波形类型
						tx_data_valid <= 1'b1;
						txd_state      <= txd_s2;
						txd_next_state <= txd_s3;
					end
					else begin
						tx_data_valid <= 1'b0;
						txd_state <= txd_s1;	
					end
				end
				
				txd_s2: txd_state <= txd_next_state;  //串口发送缓冲状态
				txd_s3: begin
					if(tx1byte_finish_flag) begin
						byteinterval_flag <= 1'b1; //开启间隔计数器标志信号 (可以理解为延时)
						if(byteinterval_cnt >= byte_interval) begin  //检测字节间隔计数器是否达到要求 (计数器在另一个always块中计数)
							tx_1byte_data <= 8'd70;        //将下一个要发送的数据赋值'F'
							tx_data_valid <= 1'b1;                   //发送标志信号 置1
							txd_state <= txd_s2;
							txd_next_state <= txd_s4;
							byteinterval_flag <= 1'b0;                //关闭 字节间隔计数器计数 标志信号
						end	
						else
							txd_state <= txd_s3;
					end
					else begin
						tx_data_valid <= 1'b0;
						txd_state <= txd_s3;
					end
				end
				
				txd_s4: begin
					if(nX4_flag >= 3'd5) begin
						txd_state <= txd_s5;
						nX4_flag  <= 3'd0;
					end
					else begin
						if(tx1byte_finish_flag) begin
							byteinterval_flag <= 1'b1; //开启间隔计数器标志信号 (可以理解为延时)
							if(byteinterval_cnt >= byte_interval) begin  //检测字节间隔计数器是否达到要求 (计数器在另一个always块中计数)
								tx_1byte_data <= {4'd3,txd_f[19:16]};    //将下一个要发送的数据赋值
								txd_f <= txd_f << 4;
								tx_data_valid <= 1'b1;                   //发送标志信号 置1
								txd_state <= txd_s2;							
								byteinterval_flag <= 1'b0;                //关闭 字节间隔计数器计数 标志信号					
								txd_next_state <= txd_s4;
								nX4_flag <= nX4_flag + 1'b1;
								
							end	
							else
								txd_state <= txd_s4;
						end
						else begin
							tx_data_valid <= 1'b0;
							txd_state <= txd_s4;
						end
					end
				end
				
				txd_s5: begin
					if(tx1byte_finish_flag) begin
						byteinterval_flag <= 1'b1; //开启间隔计数器标志信号 (可以理解为延时)
						if(byteinterval_cnt >= byte_interval) begin  //检测字节间隔计数器是否达到要求 (计数器在另一个always块中计数)
							tx_1byte_data <= 8'd85;        //将下一个要发送的数据赋值‘U’
							tx_data_valid <= 1'b1;                   //发送标志信号 置1
							txd_state   <= txd_s2;
							txd_next_state <= txd_s6;
							byteinterval_flag <= 1'b0;                //关闭 字节间隔计数器计数 标志信号
						end	
						else
							txd_state <= txd_s5;
					end
					else begin
						tx_data_valid <= 1'b0;
						txd_state <= txd_s5;
					end
				end
				
				txd_s6: begin
					if(nX4_flag >= 3'd4) begin
						txd_state <= txd_s7;
						nX4_flag  <= 3'd0;
					end
					else begin
						if(tx1byte_finish_flag) begin
							byteinterval_flag <= 1'b1; //开启间隔计数器标志信号 (可以理解为延时)
							if(byteinterval_cnt >= byte_interval) begin  //检测字节间隔计数器是否达到要求 (计数器在另一个always块中计数)
								tx_1byte_data <= {4'd3,txd_r[15:12]};    //将下一个要发送的数据赋值
								txd_r <= txd_r << 4;
								tx_data_valid <= 1'b1;                   //发送标志信号 置1
								txd_state <= txd_s2;							
								byteinterval_flag <= 1'b0;                //关闭 字节间隔计数器计数 标志信号					
								txd_next_state <= txd_s6;
								nX4_flag <= nX4_flag + 1'b1;
							end	
							else
								txd_state <= txd_s6;
						end
						else begin
							tx_data_valid <= 1'b0;
							txd_state <= txd_s6;
						end
					end
				end
				
				txd_s7: begin
					if(tx1byte_finish_flag) begin
						byteinterval_flag <= 1'b1; //开启间隔计数器标志信号 (可以理解为延时)
						if(byteinterval_cnt >= byte_interval) begin  //检测字节间隔计数器是否达到要求 (计数器在另一个always块中计数)
							tx_1byte_data <= 8'd36;        //将下一个要发送的数据赋值 '$'
							tx_data_valid <= 1'b1;                   //发送标志信号 置1
							txd_state   <= txd_s2;
							txd_next_state <= txd_s8;
							byteinterval_flag <= 1'b0;                //关闭 字节间隔计数器计数 标志信号
						end	
						else
							txd_state <= txd_s7;
					end
					else begin
						tx_data_valid <= 1'b0;
						txd_state <= txd_s7;
					end
				end
				
				txd_s8: begin
					if(tx1byte_finish_flag) begin
						byteinterval_flag <= 1'b1; //开启间隔计数器标志信号 (可以理解为延时)
						if(byteinterval_cnt >= byte_interval)begin   //检测字节间隔计数器是否达到要求 (计数器在另一个always块中计数)	
							byteinterval_flag <= 1'b0;                //关闭 字节间隔计数器计数 标志信号
							txd_state <= txd_s1;
						end
						else
							txd_state <= txd_s8;
					end
					else begin
						tx_data_valid <= 1'b0;
						txd_state <= txd_s8;
					end
				end
				
				default: txd_state <= txd_s1;
			endcase
				
		end
	end
	//-------------------------------------------------------------------------------
	
	
	//---------------------------串口接收协议模块------------------------------------
	//-------------------------------------------------------------------------------
	reg [19:0]rxd_f;
	reg [ 7:0]rxd_r;
	reg [2:0]yn_flag;
	reg yi_flag;
	always @(posedge clk or negedge rst) begin
		if(!rst) begin
			rxd_state <= rxd_s1;
			rxd_finish_flag <= 1'b0;
			yn_flag <= 3'd0;
			yi_flag <= 1'b0;
		end
		else begin
			case(rxd_state)
				rxd_s1:begin
					if(rx_data_valid) begin
						rxd_finish_flag <= 1'b0;
						rxd_state <= rxd_s2;
						if(rx_1byte_data == 8'd115)      // ‘s’
							rxd_signal <= 2'b00;  
						else if(rx_1byte_data == 8'd116) // ‘t’
							rxd_signal <= 2'b01; 
						else if(rx_1byte_data == 8'd113) // ‘q’
							rxd_signal <= 2'b10; 
					end
					else begin
						rxd_state <= rxd_s1;
						rxd_finish_flag <= 1'b0;	
					end
				end
				
				rxd_s2: begin  //接收整数位
					if(rx_data_valid ) begin
						if(rx_1byte_data == 8'd70)    //确保接收到的是字符'F'
							rxd_state <= rxd_s3;   
						else
							rxd_state <= rxd_s1;                                          
					end
					else
						rxd_state <= rxd_s2;                                             //继续等待接收
				end
				
				rxd_s3: begin  
					if(rx_data_valid ) begin
						if(rx_1byte_data >= 8'd48 && rx_1byte_data < 8'd58) begin   //确保接收到的是数字字符
							rxd_f[3:0] <= {rx_1byte_data[3:0]};  //频率放到寄存器
							yi_flag <= 1'b1;
							yn_flag <= yn_flag + 1'b1;
							rxd_state <= rxd_s3;
						end
						else if(rx_1byte_data == 8'd85)begin     //确保接收到的是字符'U'
							rxd_state <= rxd_s4; 
							yn_flag <= 3'd0;  //移位次数
							yi_flag <= 1'b0;  //移位标志
							rxd_frequency <= rxd_f;							
						end
					end
					else begin
						if(yi_flag == 1'b1 && yn_flag < 3'd5) begin	 //确保接收到高4位后 在下一个时钟脉冲后 再移位						
							yi_flag <= 1'b0;
							rxd_f <= rxd_f << 4;
						end
						rxd_state <= rxd_s3;                   //继续等待接收
					end
				end
				
				rxd_s4: begin  
					if(rx_data_valid ) begin
						if(rx_1byte_data >= 8'd48 && rx_1byte_data < 8'd58) begin   //确保接收到的是数字字符
							rxd_r[1:0] <= {rx_1byte_data[1:0]};  //频率放到寄存器							
							yi_flag <= 1'b1;
							yn_flag <= yn_flag + 1'b1;
							rxd_state <= rxd_s4;
						end
						else if(rx_1byte_data == 8'd36)begin     //确保接收到的是结束字符'$'
							rxd_state <= rxd_s1; 
							rxd_range <= rxd_r;	
							rxd_finish_flag <= 1'b1;
							yn_flag <= 3'd0;  //移位次数
							yi_flag <= 1'b0;  //移位标志
						end
					end
					else begin
						if(yi_flag == 1'b1 && yn_flag < 3'd4) begin	 //确保接收到高4位后 在下一个时钟脉冲后 再移位						
							yi_flag <= 1'b0;
							rxd_r <= rxd_r << 2;
						end
						rxd_state <= rxd_s4;                   //继续等待接收
					end
				end
				
				default:rxd_state <= rxd_s1;
			endcase
				
		end
	end
	
	
endmodule

 

五、波形驱动模块

DDS信号发生器原理(可以直接参考DDS生成任意频率波形原理及示例代码)。

主要用于驱动并且产生三种波形的数字信号(调幅后)和当前频率的三角波数字信号(调幅前)。代码如下:

module waveform_point_drive(
	input clk,
	input rst,
	input [1:0]signal,
	input [6:0]x10_n,	
	input [3:0]x1k_n,
	input [4:0]x10k_n,
	input [7:0]range,
	
	output  [9:0]buzzer_use,
	output  [9:0]signal_out

);
	pll_clk120M  uclk120m(
		.CLKI(clk),
		.CLKOP(clk_120M)
	);

	//频率调节累加器
	reg [31:0]phase_acc;
	reg [23:0]add;
	always @(posedge clk_120M) 
			phase_acc <= phase_acc + add;

	//三种波形数据产生
	wire [9:0] sin_dat; 	
	lookup_sin u1(
		.phase(phase_acc[31:24]),
		.sin_out(sin_dat)
	);
	
	wire [9:0] tri_dat;
	assign buzzer_use = tri_dat;
	lookup_tri u2(
		.phase(phase_acc[31:24]),
		.triangle_out(tri_dat)
	);
	
	wire [9:0] squ_dat;
	lookup_squ u3(
		.phase(phase_acc[31:24]), 
		.square_out(squ_dat)
	);
	
	
	//波形类型数据选择
	reg [9:0]signal_dat;
	always @(*)
		case(signal)
			2'b00: signal_dat <= sin_dat;
			2'b01: signal_dat <= tri_dat;
			2'b10: signal_dat <= squ_dat;
			default:;
		endcase
	
	
	//根据频率设定
	reg [15:0]add1;
	reg [18:0]add2;
	reg [22:0]add3;
	
	always @(x10_n or x1k_n or x10k_n) begin
		add1 <= x10_n*9'd358;
		add2 <= x1k_n*16'd35791;
		add3 <= x10k_n*19'd357914;
	end
	
	always @(posedge clk or negedge rst)
		if(!rst) 
			add <= 24'd0;
		else
			add <= add1 + add2 + add3;
			
			
	reg [17:0] amp_dat; //调幅后的波形数据
	always @(posedge clk) amp_dat = signal_dat * (range + 1'b1);  //波形数据乘以调幅因数	
	assign signal_out = amp_dat[17:8]; //取高十位输出,相当于右移8位
	
endmodule

 

六、OLED显示模块

   这里直接参考了示例代码OLED驱动说明及Verilog代码实例

      在这里显示,有待改动······

七、蜂鸣器驱动模块

频率 控制蜂鸣器的 音调

幅度 控制蜂鸣器的 音高

本设计没有直接产生对应频率的pwm波,而是利用波形驱动模块的三角波(调频后、调幅前)输出到本模块。然后利用输入的幅度参数-调幅因子(range)与之对比,输出pwm波(这样对应的频率和占空比都可以保证)。

代码如下:

module buzzer(
	input clk,
	input rst,
	input buzzer_en,
	input [7:0]range,
	input [9:0]buzzer_use,
	output reg buzzer
);

	reg [15:0] liuliu; //调幅后的波形数据
	always @(posedge clk) liuliu = range * 96;  //波形数据乘以调幅因数	

	always @(posedge clk or negedge rst) begin
		if(buzzer_en == 1'b1) begin
			if(buzzer_use[9:3] >= liuliu[14:8])
				buzzer <= 1'b0; 
			else
				buzzer <= 1'b1;
		end
		else if(buzzer_en == 1'b0)
			buzzer <= 1'b0;
	end

endmodule 

 

八、顶层模块

本模块的重点主要是以下几个方面:

声明:波形的输出和oled的显示是取决于同一状态的参数,两者在任何时候是完全一致的。

背景1:波形的输出和oled的显示 受控于两个模块,分别是板载模块上位机模块。当前波形参数显示也有两个地方,oled显示和上位机显示。

重点1:当前波形的输出和oled的显示 应该取决于哪个输入模块?

解决办法1:设置有板载参数改变标志位board_change_flag和串口接收完成标志位rxd_finish_flag两个标志信号。任意标志位为1,当前就执行那个模块的输出;空闲状态下(两个标志位都为0),输出上次标志位为1时的对应模块,直至有标志位再次为1时,改变到对应的模块输出。

 

重点2:当切换模块进行相关参数输入时,实际波形的输出和oled的显示可能发生较大改变。

解决办法2:当前在进行板载模块的参数输入时,不仅将相关参数输出到波形输出模块,而且将相关参数要输入到上位机,让上位机和当前的信号输出同步。同理,当前在进行上位机参数调节时,不仅要将相关参数输出到波形输出模块,而且将相关参数要输入到板载参数的存储寄存器中。这样就可以保证多个模块的同步和协调问题。

 

现存问题

1.由于板载的参数输入,如电位计和拨码开关只能通过外力来调节,使得幅度和波形类型变化无法同步。而按键是通过积累脉冲输入来调节的,所以频率参数可以同步。总结以下,就是板载的部分参数(波形类型和幅度)是无法和上位机同步的(频率可以同步);而上位机是可以和板载的全部参数同步的。

2.蓝色电位计调节波形的幅度,在上位机和板载同步的时候。仍然会出现数据丢失无法同步的问题。

3.频率达到高频的时候,部分波形失真明显,特别是调幅的时候非常明显,应该在硬件上改进,添加DAC数模转换模块。

 

 

 

 

 

 

附件下载
网盘链接.txt
上位机(百度网盘连接,需要安装后使用)
DDS_signal_impl1.jed
直接烧录的文件
mstudy6_project2.zip
工程文件
团队介绍
中北大学 信息与通信工程学院 2020级研究生
团队成员
董殿国
中北大学信息与通信工程学院电子信息
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号