1.项目要求:数码管显示范围为0~999 999,当数码管显示999 999,若此时按下加的按键,则数码管显示数清零,若数码管显示为0 ,若按下减的按键时,数码管为999 999
2.编写分频计数模块,用1KHz(系统时钟为5Mhz)作为驱动时钟。将分频出来的时钟输出给key_jitter模块
点击此处添加图片说明文字
代码如下:(仿真时可以把T设小一点,不然跑不完)
module freq( //系统时钟是50M,产生一个1KHZ的慢时钟
input clk,
input rst_n,
output reg clk_1KHz
);
parameter T = 50000; //50000 仿真时要选一个小一点的值
reg [19:0] counter;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
counter <= 0;
clk_1KHz <= 1;
end
else
begin
if(counter < (T/2-1))
counter <= counter+1;
else
begin
counter <= 0;
clk_1KHz <= ~clk_1KHz;
end
end
end
endmodule
//系统时钟是50M,产生一个1KHZ的慢时钟
3.按键消抖模块,并将加/减的信号传输给数据处理模块
点击此处添加图片说明文字
点击此处添加图片说明文字
点击此处添加图片说明文字
代码如下
module key_jitter( //按键消抖
input clk,
input rst_n,
input key_add, //表示加的按键
input key_sub, //表示减的按键
output reg pos_add_flag, //当加的按键按下之后,该标志位为1
output reg pos_sub_flag //当减的按键按下之后,该标志位为1
// output reg [3:0] flag_sub, //记录减按键按下的次数
// output reg [3:0] flag_add //记录加按键按下的次数
);
reg [10:0] counter1; //加按下计数寄存器
reg [10:0] counter2; //减按下计数寄存器
//reg pos_add_flag; //按键按下成功标志位
//reg pos_sub_flag; //按键按下成功标志位
reg [3:0] sum_sub; //记录减按键按下的次数
reg [3:0] sum_add;
reg state1; //状态寄存器
reg state2;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n) //复位时,参数置0
begin
counter1 <= 11'b0;
counter2 <= 11'b0;
pos_add_flag <= 0;
pos_sub_flag <= 0;
sum_sub <= 0;
sum_add <= 0;
state1 <= 0;
state2 <= 0;
end
else
begin
case(state1)
0:begin //当为起始状态时 先判断按键按下的状态计数是否超过10
if(counter1<10)
begin
if(!key_add)
counter1 <= counter1 + 1;
else
counter1 <= 11'b0; //如果按键没有按下,则清零
end
else
begin //当按键按下的状态计数超过10,说明按键已经按下
pos_add_flag <= 1; //尖峰脉冲到达
sum_add <= sum_add + 1;
counter1 <= 11'b0;
state1 <= 1;
end
end
1: begin
pos_add_flag <= 0;
if(key_add)
state1 <= 0;
end
default: state1 <= 0;
endcase
case(state2)
0:begin //当为起始状态时 先判断按键按下的状态计数是否超过10
if(counter2<10)
begin
if(!key_sub)
counter2 <= counter2 + 1;
else
counter2 <= 11'b0; //如果按键没有按下,则清零
end
else
begin //当按键按下的状态计数超过10,说明按键已经按下
pos_sub_flag <= 1; //尖峰脉冲到达
sum_sub <= sum_sub + 1;
counter2 <= 11'b0;
state2 <= 1;
end
end
1: begin
pos_sub_flag <= 0;
if(key_sub)
state2 <= 0;
end
default: state2 <= 0;
endcase
end
end
endmodule
4. data_ctrl用于实现数据处理功能,当加按下时,显示的数字加一,当减时,显示的数字减一
点击此处添加图片说明文字
module data_ctrl( //用于实现数据处理功能,当加按下时,显示的数字加一,当减时,显示的数字减一
input clk,
input rst_n,
// input flag_add,
// input flag_sub,
input pos_add_flag,
input pos_sub_flag,
output reg[19:0] data
// input data_en //数据使能信号
);
reg [19:0] data_temp; //数据暂存器
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
data_temp <= 19'd0;
data <= 19'd0;
end
else
begin
// if (data_en)
// begin
if(pos_add_flag) //当加按键按下时
begin
if(data_temp == 19'd999999) //如果加按键按下后,但之前的数据已经是999999了,就要清零
data_temp <= 0;
else
data_temp <= data_temp + 1; //否则就加一
// data <= data_temp;
end
if(pos_sub_flag)
begin
if(data_temp == 0) //如果减按键按下后,但之前的数据已经是0了,就要变为999999
data_temp <= 999999;
else
data_temp <= data_temp - 1; //否则就减一
end
data <= data_temp;
// end
end
end
endmodule
5.实现二进制转化为BCD码的功能
点击此处添加图片说明文字
点击此处添加图片说明文字
代码如下
module bin_bcd( //实现二进制转化为BCD码的功能
input clk,
input rst_n,
input [19:0] data, //因为数码管能显示的数最大为999999,故该为20位
// output reg data_en, //数据使能信号
output reg [23:0] bcd_out //6位数的bcd输出(因为最大的十进制数为6位,每一位需要4位表示bcd,所以需要24位)
);
reg [43:0] bcd_out_r; //bcd转换码移位寄存器
reg [1:0] state3; //状态寄存器
reg [5:0] shift_cnt; //移位计数器
//reg data1 =1;
//assign data_en = data1;
/*always @(*)
begin
data1 <= data;
end*/
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
bcd_out_r <= 0;
state3 <= 0;
shift_cnt <= 0;
bcd_out <= 0;
// data_en = 1;
// data_en = data1;
end
else
begin
case(state3)
2'd0:begin //刚开始时,将数据赋值给bcd码的移位寄存器
bcd_out_r <= {24'b0,data};
state3 <= state3 + 1;
shift_cnt <= 0;
// data_en = 0;
end
2'd1:begin //移位
if(shift_cnt < 20) //如果移位次数小于数据长度的话,就将bcd码进行移位
begin
bcd_out_r <= bcd_out_r << 1;
shift_cnt <= shift_cnt + 1;
state3 <= state3 + 1;
end
else
begin
state3 <= 0;
shift_cnt <= 0;
end
end
/* 2'd2:begin
bcd_out_r <= {bcd_out_r[],data[19:0],1'b0};
end */
2'd2:begin //移位之后就进行大四加 *****是对BCD码进行大四加三,不能所有进行大四加三
if(bcd_out_r[43:40] > 4)
bcd_out_r[43:40] <= bcd_out_r[43:40] + 3;
if(bcd_out_r[39:36] > 4)
bcd_out_r[39:36] <= bcd_out_r[39:36] + 3;
if(bcd_out_r[35:32] > 4)
bcd_out_r[35:32] <= bcd_out_r[35:32] + 3;
if(bcd_out_r[31:28] > 4)
bcd_out_r[31:28] <= bcd_out_r[31:28] + 3;
if(bcd_out_r[27:24] > 4)
bcd_out_r[27:24] <= bcd_out_r[27:24] + 3;
if(bcd_out_r[23:20] > 4)
bcd_out_r[23:20] <= bcd_out_r[23:20] + 3;
/* if(bcd_out_r[19:16] > 4)
bcd_out_r[19:16] <= bcd_out_r[19:16] + 3;
if(bcd_out_r[15:12] > 4)
bcd_out_r[15:12] <= bcd_out_r[15:12] + 3;
if(bcd_out_r[11:8] > 4)
bcd_out_r[11:8] <= bcd_out_r[11:8] + 3;
if(bcd_out_r[7:4] > 4)
bcd_out_r[7:4] <= bcd_out_r[7:4] + 3;
if(bcd_out_r[3:0] > 4)
bcd_out_r[3:0] <= bcd_out_r[3:0] + 3;*/
state3 <= 3;
end
2'd3:begin
state3 <= 1; //继续进行移位,直到把数据全部移位
end
default : state3 <= 0;
endcase
bcd_out = (state3 == 3) && (shift_cnt == 20) ? bcd_out_r[43:20] : bcd_out; //如果移位和比较都完成之后就把值给输出
// data_en = (state3 == 3) && (shift_cnt == 20) ? 1 : 0;
end
end
endmodule
6.实现数码管的译码
点击此处添加图片说明文字
点击此处添加图片说明文字
代码如下:
module seg_driver( //实现数码管的译码
input clk,
input rst_n,
input [23:0] bcd_out, //输入的数据
output reg[7:0] seg, //数码管段选信号
output reg[5:0] sel //数码管位选信号
);
reg [3:0] data_temp; //数码管显示的数值,因为一个数码管显示的最大数为9,就需要4位
reg [2:0] state4; //状态寄存器
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
sel <= 0;
data_temp <= 0;
state4 <= 0;
// data <= 24'h234567;
end
else
begin
case (state4)
0:begin
sel <= 0; //将最高位显示在第一个数码管上
data_temp <= bcd_out[23:20];
state4 <= 1;
end
1:begin
sel <= 1; //将最高位显示在第二个数码管上
data_temp <= bcd_out[19:16];
state4 <= 2;
end
2:begin
sel <= 2; //将最高位显示在第3个数码管上
data_temp <= bcd_out[15:12];
state4 <= 3;
end
3:begin
sel <= 3; //将最高位显示在第4个数码管上
data_temp <= bcd_out[11:8];
state4 <= 4;
end
4:begin
sel <= 4; //将最高位显示在第5个数码管上
data_temp <= bcd_out[7:4];
state4 <= 5;
end
5:begin
sel <= 5; //将最高位显示在第6个数码管上
data_temp <= bcd_out[3:0];
state4 <= 0;
end
default : state4 <= 0;
endcase
end
end
always @(*)
begin
if(!rst_n)
begin
seg = 8'b0000_0000; //复位时数码管熄灭
end
else
begin
case(data_temp)
0:seg =8'b1100_0000; //数码管显示0
1:seg =8'b1111_1001;
2:seg =8'b1010_0100;
3:seg =8'b1011_0000;
4:seg =8'b1001_1001;
5:seg =8'b1001_0010;
6:seg =8'b1000_0010;
7:seg =8'b1111_1000;
8:seg =8'b1000_0000;
9:seg =8'b1001_0000;
10:seg =8'b1000_1000;
11:seg =8'b1000_0011;
12:seg =8'b1100_0110;
13:seg =8'b1010_0001;
14:seg =8'b1000_1110;
15:seg =8'b1000_1110; 数码管显示F
default : seg = 8'b1111_1111;
endcase
end
end
endmodule
7.顶层模块
点击此处添加图片说明文字
代码如下:
module key_seg( //顶层模块
input clk,
input rst_n,
input key_add, //表示加的按键
input key_sub,
// input [23:0] data,
output [7:0] seg, //数码管段选信号
output [5:0] sel
);
wire clk_1KHz;
wire [19:0] data;
wire [23:0] bcd_out;
freq freq(
.clk(clk),
.rst_n(rst_n),
.clk_1KHz(clk_1KHz)
);
key_jitter key_jitter(
.clk(clk_1KHz),
.rst_n(rst_n),
.key_add(key_add),
.key_sub(key_sub),
.pos_sub_flag(pos_sub_flag),
.pos_add_flag(pos_add_flag)
);
data_ctrl data_ctrl(
.clk(clk_1KHz),
.rst_n(rst_n),
.data(data),
.pos_sub_flag(pos_sub_flag),
// .data_en(data_en),
.pos_add_flag(pos_add_flag)
);
bin_bcd bin_bcd(
.clk(clk_1KHz),
.rst_n(rst_n),
.data(data),
// .data_en(data_en),
.bcd_out(bcd_out)
);
seg_driver seg_driver(
.clk(clk_1KHz),
.rst_n(rst_n),
.bcd_out(bcd_out),
.seg(seg),
.sel(sel)
);
endmodule
7.测试文件及仿真图:
点击此处添加图片说明文字
代码如下:
`timescale 1 ns/ 1 ns
module key_seg_tb();
parameter T = 20;
/********************系统输入**********************/
reg clk;
reg rst_n;
reg key_add;
reg key_sub;
/*********************系统输出*****************/
wire [7:0] seg;
wire [5:0] sel;
wire [19:0] data;
wire [23:0] bcd_out;
wire data_en;
initial
begin
clk = 1'b1;
rst_n = 1'b0;
key_add = 1;
key_sub = 1;
#1000 rst_n = 1'b1;
#10000000 key_add = 0;
#10000000 key_add = 1;
/* #100000000000000 key_add = 0;
#100000000000000 key_add = 1;
#100000000000000 key_sub = 0;
#100000000000000 key_sub = 1;
#10000000 key_add = 0;
#10000000 key_add = 1;
#10000000 key_add = 0;
#10000000 key_add = 1;
#10000000 key_add = 0;
#10000000 key_add = 1;*/
$stop;
end
always #(T/2) clk = ~clk;
freq freq(
.clk(clk),
.rst_n(rst_n),
.clk_1KHz(clk_1KHz)
);
key_jitter key_jitter(
.clk(clk_1KHz),
.rst_n(rst_n),
.key_add(key_add),
.key_sub(key_sub),
.pos_sub_flag(pos_sub_flag),
.pos_add_flag(pos_add_flag)
);
data_ctrl data_ctrl(
.clk(clk_1KHz),
.rst_n(rst_n),
.data(data),
.pos_sub_flag(pos_sub_flag),
// .data_en(data_en),
.pos_add_flag(pos_add_flag)
);
bin_bcd bin_bcd(
.clk(clk_1KHz),
.rst_n(rst_n),
.data(data),
// .data_en(data_en),
.bcd_out(bcd_out)
);
seg_driver seg_driver(
.clk(clk_1KHz),
.rst_n(rst_n),
.bcd_out(bcd_out),
.seg(seg),
.sel(sel)
);
key_seg key_seg(
.rst_n(rst_n),
.clk(clk),
.key_add(key_add),
.seg(seg),
.sel(sel),
.key_sub(key_sub)
);
endmodule
仿真图:
点击此处添加图片说明文字
ps:
1)各个模块之间的连线一定要在顶层文件和testbench文件定义申明wire类型,因为input默认是wire类型,而大多数output是reg类型,连接的起来相当于是一根线,即要定义为wire类型,还要标明宽度,因为默认好像是2,如果不定义就不能接受宽度大的数据了。比如:我这里就是data_ctrol可以输出data,但是bin_bcd 里面就接收不了,如下图:
点击此处添加图片说明文字
2)在转化为bcd码时,本来想的是在移位完成和比较完成之后就可以把值给输出,但是看程序可以看出,在当移位完成之后,还没有比较完成时就已经把值给输出了,所以会出错。
点击此处添加图片说明文字