设计要求:
6位8段数码管,低三位显示毫秒计数,最高位显示分钟,其余两位显示秒计数。
开始案件与暂停按键,复位按键直接全部归零。
扩展部分:每计满一次,led移位一次。
框图设计:
思路讲解:
首先按键信号经过消抖再用,然后把产生的标志信号传给控制模块,由于控制逻辑很简单就把这部分控制逻辑放进“数据产生模块中了”;
然后把数码管与led接口模块interface放进去。
按理来讲,应该重新定义个接口模块再把led与nixie放进去,比较规范。
模块讲解:
值得一提就是数据产生模块与数码管接口模块:
数据产生模块:
其实输出端口是几个级联得计数器。
代码奉上:
`include "para.v"
module data_gen (
input wire sys_clk ,
input wire sys_rst_n ,
input wire start_flag ,
input wire stop_flag ,
output reg [3:0] po_data_one ,
output reg [3:0] po_data_two ,
output reg [3:0] po_data_thr ,
output reg [3:0] po_data_fou ,
output reg [3:0] po_data_fiv ,
output reg [3:0] po_data_six ,
output reg minute_flag
);
// localparam
localparam IDLE = 3'b001 ,
WORKING = 3'b010 ,
STOP = 3'b100 ;
// reg signal
reg [15:0] cnt_1ms ;
reg [2:0] state_c ;
reg [2:0] state_n ;
// wire signal
wire add_cnt_1ms ;
wire end_cnt_1ms ;
wire IDLEtoWORKING ;
wire WORKINGtoSTOP ;
wire STOPtoWORKING ;
/******************************************************************************************
********************************************main code**************************************
*******************************************************************************************/
// // reg signal
// reg [2:0] state_c ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
state_c <= IDLE ;
else
state_c <= state_n ;
end
// reg [2:0] state_n ;
always @(*) begin
if(~sys_rst_n)
state_n = IDLE ;
else
case(state_c)
IDLE : if(IDLEtoWORKING)
state_n = WORKING ;
else
state_n = IDLE ;
WORKING: if(WORKINGtoSTOP)
state_n = STOP ;
else
state_n = WORKING ;
STOP : if(STOPtoWORKING)
state_n = WORKING ;
else
state_n = STOP ;
default: state_n = IDLE ;
endcase
end
assign IDLEtoWORKING = (state_c == IDLE ) && (start_flag) ;
assign WORKINGtoSTOP = (state_c == WORKING ) && (stop_flag ) ;
assign STOPtoWORKING = (state_c == STOP ) && (start_flag) ;
// reg [15:0] cnt_1ms ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
cnt_1ms <= 16'd0 ;
else if(add_cnt_1ms) begin
if(end_cnt_1ms)
cnt_1ms <= 16'd0 ;
else
cnt_1ms <= cnt_1ms + 1'b1 ;
end
else if(state_c == IDLE)
cnt_1ms <= 16'd0 ;
else
cnt_1ms <= cnt_1ms ;// 注意这里,是保持还是归零。
end
// wire add_cnt_1ms ;
assign add_cnt_1ms = (state_c == WORKING ) ;
// wire end_cnt_1ms ;
assign end_cnt_1ms = add_cnt_1ms && (cnt_1ms == `MAX_CNT_1MS - 1) ;
// output signal description
// output reg [3:0] po_data_one ,
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
po_data_one <= 4'd0 ;
else if(end_cnt_1ms && po_data_one == `MAX_CNT_NUM - 1)
po_data_one <= 4'd0 ;
else if(end_cnt_1ms)
po_data_one <= po_data_one + 1'b1 ;
else
po_data_one <= po_data_one ;
end
// output reg [3:0] po_data_two ,
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
po_data_two <= 4'd0 ;
else if((end_cnt_1ms && po_data_one == `MAX_CNT_NUM - 1) && po_data_two == `MAX_CNT_NUM - 1)
po_data_two <= 4'd0 ;
else if(end_cnt_1ms && po_data_one == `MAX_CNT_NUM - 1)
po_data_two <= po_data_two + 1'b1 ;
else
po_data_two <= po_data_two ;
end
// output reg [3:0] po_data_thr ,
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
po_data_thr <= 4'd0 ;
else if((((end_cnt_1ms && po_data_one == `MAX_CNT_NUM - 1) && po_data_two == `MAX_CNT_NUM - 1)) && (po_data_thr == `MAX_CNT_NUM - 1))
po_data_thr <= 4'd0 ;
else if(((end_cnt_1ms && po_data_one == `MAX_CNT_NUM - 1) && po_data_two == `MAX_CNT_NUM - 1))
po_data_thr <= po_data_thr + 1'b1 ;
else
po_data_thr <= po_data_thr ;
end
// output reg [3:0] po_data_fou , 显示秒的个位 0 ~ 9
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
po_data_fou <= 4'd0 ;
else if(((((end_cnt_1ms && po_data_one == `MAX_CNT_NUM - 1) && po_data_two == `MAX_CNT_NUM - 1)) && (po_data_thr == `MAX_CNT_NUM - 1)) && (po_data_fou == `MAX_CNT_NUM - 1))
po_data_fou <= 4'd0 ;
else if((((end_cnt_1ms && po_data_one == `MAX_CNT_NUM - 1) && po_data_two == `MAX_CNT_NUM - 1)) && (po_data_thr == `MAX_CNT_NUM - 1))
po_data_fou <= po_data_fou + 1'b1 ;
else
po_data_fou <= po_data_fou ;
end
// output reg [3:0] po_data_fiv , 显示秒的十位 0 ~ 5
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
po_data_fiv <= 4'd0 ;
else if(((((end_cnt_1ms && po_data_one == `MAX_CNT_NUM - 1) && po_data_two == `MAX_CNT_NUM - 1)) && (po_data_thr == `MAX_CNT_NUM - 1)) && (po_data_fou == `MAX_CNT_NUM - 1) && (po_data_fiv == `MAX_CNT_NUM - 5))
po_data_fiv <= 4'd0 ;
else if(((((end_cnt_1ms && po_data_one == `MAX_CNT_NUM - 1) && po_data_two == `MAX_CNT_NUM - 1)) && (po_data_thr == `MAX_CNT_NUM - 1)) && (po_data_fou == `MAX_CNT_NUM - 1))
po_data_fiv <= po_data_fiv + 1'b1 ;
else
po_data_fiv <= po_data_fiv ;
end
// output reg [3:0] po_data_six ,
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
po_data_six <= 4'd0 ;
else if((((((end_cnt_1ms && po_data_one == `MAX_CNT_NUM - 1) && po_data_two == `MAX_CNT_NUM - 1)) && (po_data_thr == `MAX_CNT_NUM - 1)) && (po_data_fou == `MAX_CNT_NUM - 1) && (po_data_fiv == `MAX_CNT_NUM - 5)) && (po_data_six == `MAX_CNT_NUM - 1))
po_data_six <= 4'd0 ;
else if(((((end_cnt_1ms && po_data_one == `MAX_CNT_NUM - 1) && po_data_two == `MAX_CNT_NUM - 1)) && (po_data_thr == `MAX_CNT_NUM - 1)) && (po_data_fou == `MAX_CNT_NUM - 1) && (po_data_fiv == `MAX_CNT_NUM - 5))
po_data_six <= po_data_six + 1'b1 ;
else
po_data_six <= po_data_six ;
end
// reg minute_flag
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
minute_flag <= 1'b0 ;
else if((((((end_cnt_1ms && po_data_one == `MAX_CNT_NUM - 1) && po_data_two == `MAX_CNT_NUM - 1)) && (po_data_thr == `MAX_CNT_NUM - 1)) && (po_data_fou == `MAX_CNT_NUM - 1) && (po_data_fiv == `MAX_CNT_NUM - 5)) && (po_data_six == `MAX_CNT_NUM - 1))
minute_flag <= 1'b1 ;
else
minute_flag <= 1'b0 ;
end
endmodule
状态机设计:
当复位按键按下,现态与次态都需要回到IDLE状态。
在代码层面,给state_n设置一个复位情况。
数码管模块:
创新点,与以往不同的代码设计,这次用了“函数”,function。
代码奉上:
`include "para.v"
module nixie (
input wire sys_clk ,
input wire sys_rst_n ,
input wire [3:0] pi_data_one ,
input wire [3:0] pi_data_two ,
input wire [3:0] pi_data_thr ,
input wire [3:0] pi_data_fou ,
input wire [3:0] pi_data_fiv ,
input wire [3:0] pi_data_six ,
output reg [5:0] sel ,
output reg [7:0] dig
);
// reg
reg dot ; // 数码管上的小数点。
reg [31:0] cnt_time ; // 移位寄存器的移位时间,计数器。
// wire
wire add_cnt_time ;
wire end_cnt_time ;
/******************************************************************************************
********************************************main code**************************************
*******************************************************************************************/
// reg signal description
// reg [31:0] cnt_time ; // 移位寄存器的移位时间。
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
cnt_time <= 32'd0 ;
else if(add_cnt_time) begin
if(end_cnt_time)
cnt_time <= 32'd0 ;
else
cnt_time <= cnt_time + 1'b1 ;
end
else
cnt_time <= 32'd0 ; // 注意这里,是保持还是归零。
end
// reg dot ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
dot <= 1'b1 ;
else
dot <= 1'b1 ;
end
// wire signal description
assign add_cnt_time = 1'b1 ;
assign end_cnt_time = add_cnt_time && (cnt_time == `MAX_CNT_TIES - 1) ;
// task
// task nixie_dig ;
// input [3:0] data_num ;
// output [7:0] po_dig ;
// case (data_num)
// 0 : po_dig = {dot, `ZERO } ;
// 1 : po_dig = {dot, `ONE } ;
// 2 : po_dig = {dot, `TWO } ;
// 3 : po_dig = {dot, `THREE } ;
// 4 : po_dig = {dot, `FOUR } ;
// 5 : po_dig = {dot, `FIVE } ;
// 6 : po_dig = {dot, `SIX } ;
// 7 : po_dig = {dot, `SEVEN } ;
// 8 : po_dig = {dot, `EIGHT } ;
// 9 : po_dig = {dot, `NINE } ;
// default: po_dig = 8'd0 ;
// endcase
// endtask
// function
function [6:0] dig_num;
input [3:0] data_in ;
case (data_in)
0 : dig_num = `ZERO ;
1 : dig_num = `ONE ;
2 : dig_num = `TWO ;
3 : dig_num = `THREE ;
4 : dig_num = `FOUR ;
5 : dig_num = `FIVE ;
6 : dig_num = `SIX ;
7 : dig_num = `SEVEN ;
8 : dig_num = `EIGHT ;
9 : dig_num = `NINE ;
default: dig_num = 7'd0 ;
endcase
endfunction
// Output signal description
// output reg [5:0] sel ,
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
sel <= 6'b111_110 ;
else if(end_cnt_time)
sel <= {sel[4:0],sel[5]} ;
else
sel <= sel ;
end
// output reg [7:0] dig
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
dig <= 8'd0 ;
else
case (sel)
6'b111_110: dig <= {dot, dig_num(pi_data_one)} ;
6'b111_101: dig <= {dot, dig_num(pi_data_two)} ;
6'b111_011: dig <= {dot, dig_num(pi_data_thr)} ;
6'b110_111: dig <= {1'b0, dig_num(pi_data_fou)} ;
6'b101_111: dig <= {dot, dig_num(pi_data_fiv)} ;
6'b011_111: dig <= {1'b0, dig_num(pi_data_six)} ;
default : dig <= {dot, `SIX } ;
endcase
end
endmodule
函数或者任务的使用,是使得代码写起来更方便,设计起来更节省时间。
减少重复劳动。文章来源:https://www.toymoban.com/news/detail-782613.html
要灵活使用。多观察,多分析,多获取信息。找到相关性,相似性。文章来源地址https://www.toymoban.com/news/detail-782613.html
到了这里,关于FPGA巩固基础:秒表的设计的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!