上位机控制的可调波形、幅度、频率输出的信号发生器
一、项目需求
综合性题目 - 制作一个可以上位机控制的可调波形、幅度、频率输出的信号发生器
- 能够产生正弦波、三角波、方波,可以通过小脚丫上的拨码开关控制波形的切换。
- 产生信号的幅度0-3Vpp之间可调,调节分辨率精确到10mV,可以通过电位计进行调节。
- 产生信号的频率100Hz - 200KHz之间可调,频率调节分辨率可达10Hz。
- 将1KHz - 10KHz频率范围的信号同时送到蜂鸣器,驱动蜂鸣器发出声音,调节频率和信号的幅度会改变蜂鸣器的声音输出。
- 产生的波形(图示)、波形的幅度、波形的频率都实时显示在OLED屏幕上在PC上编写控制界面,可以采用LabView、Matlab或其它工具,在PC上通过界面改变波形、波形的幅度和频率参数,并通过UART将设置传输到FPGA板上对波形进行调节。
- PC的控制和板卡上的控制同时有效。
二、整体模块示意图
- 板载数据处理模块:处理板载的按键、拨码开关、和电位计等数据,输出板载控制的信号类型board_signal、信号频率(3个参数board_x10_n、board_1k_n、board_10k_n)、信号幅度board_range、和串口准备发送标志信号board_change_flag等数据到顶层模块。
- 串口收发模块:负责串口的收发,绝大多数采用ASCII码收发。
- OLED显示模块:负责当前波形的类型、频率、幅度的显示。
- 波形驱动模块:负责将当前标定控制的信号类型、频率和幅度以10bits的数字信号输出。并且输出当前频率所对应的三角波(未调幅)到蜂鸣器驱动模块。
- 蜂鸣器驱动模块:负责将当前频率在1k~10k的范围时候来驱动控制蜂鸣器音调,同时引入幅度值来调节蜂鸣器音高低。
- 顶层模块:负责各个模块的调度和标志控制信号。
三、板载数据处理模块和实物展示
主要包括按键、拨码开关、旋转电位计的数据处理。板载控制示意图如下:
按键从左到右分别定义为①号按键±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视图。
本设计是信号发生器,自定义了串口协议。
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数模转换模块。