使用开发板完成毫秒级的计时器。范围从0.000s ~ 9.999s, 之后自动溢出回到0.000s。用4位7段数码管显示计时时间,秒单位要有小数点。用1个开关控制计时开始和停止。停止时,触发inc的button一次,对应时间增加1ms。Reset按钮点击后,时间恢复到0.000s。
原理
1 状态转换图
2 计时器加1计算显示数字的电路逻辑:
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 16:35:38 11/30/2014
// Design Name:
// Module Name: ms_timer
// Project Name:
// Target Devices:
// Tool versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module ms_timer(
input clk,
input inc,
input wire switch, // 1 为start, 0 为stop
input rst, // 复位,上升沿触发,异步
output reg [7:0] num, // 选择显示的数据,用parameter参数赋值
output reg [3:0] loc// 选择哪一个数码管位输出
);
parameter SEG_NUM0 = 8'b00000011, // abcdefg dp
SEG_NUM1 = 8'b10011111,
SEG_NUM2 = 8'b00100101,
SEG_NUM3 = 8'b00001101,
SEG_NUM4 = 8'b10011001,
SEG_NUM5 = 8'b01001001,
SEG_NUM6 = 8'b01000001,
SEG_NUM7 = 8'b00011111,
SEG_NUM8 = 8'b00000001,
SEG_NUM9 = 8'b00001001,
SEGD_NUM0 = 8'b00000010, // 秒单位,需要小数点 abcdefg dp
SEGD_NUM1 = 8'b10011110,
SEGD_NUM2 = 8'b00100100,
SEGD_NUM3 = 8'b00001100,
SEGD_NUM4 = 8'b10011000,
SEGD_NUM5 = 8'b01001000,
SEGD_NUM6 = 8'b01000000,
SEGD_NUM7 = 8'b00011110,
SEGD_NUM8 = 8'b00000000,
SEGD_NUM9 = 8'b00001000;
// 位选
parameter DUAN_3 = 4'b0111,
DUAN_2 = 4'b1011,
DUAN_1 = 4'b1101,
DUAN_0 = 4'b1110;
reg [20:0] count = 0;
reg [3:0] ms1 = 0; // 毫秒单位
reg [3:0] ms10 = 0; // 10毫秒单位
reg [3:0] ms100 = 0; // 100毫秒单位
reg [3:0] s1 = 0; // 1秒单位
reg [17:0] fenpin = 0;
reg clk_1ms = 0;
reg cen = 0;
integer fsm = 1; //判断状态,一开始为停止状态
always @(posedge clk) begin
if(fenpin == 100000)
fenpin <= 0;
else
fenpin <= fenpin + 1'b1;
end
always @ (posedge clk) begin
if(fenpin < 50000)
clk_1ms <= 0;
else
clk_1ms <= 1;
end
// 扫描位选
always @ (posedge clk) begin
count <= count + 1'b1;
end
always @ (posedge clk_1ms or posedge rst) begin
if (rst) begin
fsm = 1;
cen = 0;
end
else begin
case(fsm)
0: begin // 在start的自加状态下
cen = 1;
if(!switch)// 触发stop
fsm = 1;
else
fsm = 0;
end
1: begin // stop状态
cen = 0;
if(switch) //触发start
fsm = 0;
else if(inc)
fsm = 2;
else
fsm = 1;
end
2: begin // inc函数
cen = 1;
fsm = 3;
end
3: begin
cen = 0;
if(inc)
fsm = 3;
else
fsm = 1;
end
endcase
end
end
// 计时器加1,实现4个数字的加法
always @ (posedge clk_1ms) begin
if(!rst) begin
if(cen) begin
case(ms1)
4'b1001: ms1 <= 4'b0000;
default: ms1 <= ms1 + 1'b1;
endcase
case(ms10)
4'b1001: begin
if(ms1 == 4'b1001)
ms10 <= 4'b0000;
end
default: begin
if(ms1 == 4'b1001)
ms10 <= ms10 + 1'b1;
end
endcase
case(ms100)
4'b1001: begin
if(ms1 == 4'b1001 && ms10 == 4'b1001)
ms100 <= 4'b0000;
end
default: begin
if(ms1 == 4'b1001 && ms10 == 4'b1001)
ms100 <= ms100 + 1'b1;
end
endcase
case(s1)
4'b1001: begin
if(ms1 == 4'b1001 && ms10 == 4'b1001 && ms100 == 4'b1001)
s1 <= 4'b0000;
end
default: begin
if(ms1 == 4'b1001 && ms10 == 4'b1001 && ms100 == 4'b1001)
s1 <= s1 + 1'b1;
end
endcase
end
end
//复位清零
else begin
ms1 <= 0;
ms100 <= 0;
ms10 <= 0;
s1 <= 0;
end
end
// 扫描显示,位选信号。扫描到对应数码管位就显示那一位数字
always @ (posedge clk) begin
case(count[19:18])
2'b00: begin
loc <= DUAN_3;
case(s1)
4'b0000: num <= SEGD_NUM0;
4'b0001: num <= SEGD_NUM1;
4'b0010: num <= SEGD_NUM2;
4'b0011: num <= SEGD_NUM3;
4'b0100: num <= SEGD_NUM4;
4'b0101: num <= SEGD_NUM5;
4'b0110: num <= SEGD_NUM6;
4'b0111: num <= SEGD_NUM7;
4'b1000: num <= SEGD_NUM8;
4'b1001: num <= SEGD_NUM9;
endcase
end
2'b01: begin
loc <= DUAN_2;
case(ms100)
4'b0000: num <= SEG_NUM0;
4'b0001: num <= SEG_NUM1;
4'b0010: num <= SEG_NUM2;
4'b0011: num <= SEG_NUM3;
4'b0100: num <= SEG_NUM4;
4'b0101: num <= SEG_NUM5;
4'b0110: num <= SEG_NUM6;
4'b0111: num <= SEG_NUM7;
4'b1000: num <= SEG_NUM8;
4'b1001: num <= SEG_NUM9;
endcase
end
2'b10: begin
loc <= DUAN_1;
case(ms10)
4'b0000: num <= SEG_NUM0;
4'b0001: num <= SEG_NUM1;
4'b0010: num <= SEG_NUM2;
4'b0011: num <= SEG_NUM3;
4'b0100: num <= SEG_NUM4;
4'b0101: num <= SEG_NUM5;
4'b0110: num <= SEG_NUM6;
4'b0111: num <= SEG_NUM7;
4'b1000: num <= SEG_NUM8;
4'b1001: num <= SEG_NUM9;
endcase
end
2'b11: begin
loc <= DUAN_0;
case(ms1)
4'b0000: num <= SEG_NUM0;
4'b0001: num <= SEG_NUM1;
4'b0010: num <= SEG_NUM2;
4'b0011: num <= SEG_NUM3;
4'b0100: num <= SEG_NUM4;
4'b0101: num <= SEG_NUM5;
4'b0110: num <= SEG_NUM6;
4'b0111: num <= SEG_NUM7;
4'b1000: num <= SEG_NUM8;
4'b1001: num <= SEG_NUM9;
endcase
end
endcase
end
endmodule
//test 使用代码
/*
module ms_timer(
input clk,
input inc,
input wire switch, // 1 为start, 0 为stop
input rst, // 复位,上升沿触发,异步
output reg [7:0] num, // 选择显示的数据,用parameter参数赋值
output reg [3:0] loc// 选择哪一个数码管位输出
);
parameter SEG_NUM0 = 8'b00000011, // abcdefg dp
SEG_NUM1 = 8'b10011111,
SEG_NUM2 = 8'b00100101,
SEG_NUM3 = 8'b00001101,
SEG_NUM4 = 8'b10011001,
SEG_NUM5 = 8'b01001001,
SEG_NUM6 = 8'b01000001,
SEG_NUM7 = 8'b00011111,
SEG_NUM8 = 8'b00000001,
SEG_NUM9 = 8'b00001001,
SEGD_NUM0 = 8'b00000010, // 秒单位,需要小数点 abcdefg dp
SEGD_NUM1 = 8'b10011110,
SEGD_NUM2 = 8'b00100100,
SEGD_NUM3 = 8'b00001100,
SEGD_NUM4 = 8'b10011000,
SEGD_NUM5 = 8'b01001000,
SEGD_NUM6 = 8'b01000000,
SEGD_NUM7 = 8'b00011110,
SEGD_NUM8 = 8'b00000000,
SEGD_NUM9 = 8'b00001000;
// 位选
parameter DUAN_3 = 4'b0111,
DUAN_2 = 4'b1011,
DUAN_1 = 4'b1101,
DUAN_0 = 4'b1110;
reg [20:0] count = 0;
reg [3:0] ms1 = 0; // 毫秒单位
reg [3:0] ms10 = 0; // 10毫秒单位
reg [3:0] ms100 = 0; // 100毫秒单位
reg [3:0] s1 = 0; // 1秒单位
reg cen = 0;
integer fsm = 1; //判断状态,一开始为停止状态
// 扫描位选
always @ (posedge clk) begin
count <= count + 1'b1;
end
always @ (posedge clk or posedge rst) begin
if (rst) begin
fsm = 1;
cen = 0;
end
else begin
case(fsm)
0: begin // 在start的自加状态下
cen = 1;
if(!switch)// 触发stop
fsm = 1;
else
fsm = 0;
end
1: begin // stop状态
cen = 0;
if(switch) //触发start
fsm = 0;
else if(inc)
fsm = 2;
else
fsm = 1;
end
2: begin
cen = 1;
fsm = 3;
end
3: begin
cen = 0;
if(inc)
fsm = 3;
else
fsm = 1;
end
endcase
end
end
always @ (posedge clk) begin
if(!rst) begin
if(cen) begin
case(ms1)
4'b1001: ms1 <= 4'b0000;
default: ms1 <= ms1 + 1'b1;
endcase
case(ms10)
4'b1001: begin
if(ms1 == 4'b1001)
ms10 <= 4'b0000;
end
default: begin
if(ms1 == 4'b1001)
ms10 <= ms10 + 1'b1;
end
endcase
case(ms100)
4'b1001: begin
if(ms1 == 4'b1001 && ms10 == 4'b1001)
ms100 <= 4'b0000;
end
default: begin
if(ms1 == 4'b1001 && ms10 == 4'b1001)
ms100 <= ms100 + 1'b1;
end
endcase
case(s1)
4'b1001: begin
if(ms1 == 4'b1001 && ms10 == 4'b1001 && ms100 == 4'b1001)
s1 <= 4'b0000;
end
default: begin
if(ms1 == 4'b1001 && ms10 == 4'b1001 && ms100 == 4'b1001)
s1 <= s1 + 1'b1;
end
endcase
end
end
else begin
ms1 <= 0;
ms100 <= 0;
ms10 <= 0;
s1 <= 0;
end
end
// 扫描显示
always @ (posedge clk) begin
case(count[2:1])
2'b00: begin
loc <= DUAN_3;
case(s1)
4'b0000: num <= SEGD_NUM0;
4'b0001: num <= SEGD_NUM1;
4'b0010: num <= SEGD_NUM2;
4'b0011: num <= SEGD_NUM3;
4'b0100: num <= SEGD_NUM4;
4'b0101: num <= SEGD_NUM5;
4'b0110: num <= SEGD_NUM6;
4'b0111: num <= SEGD_NUM7;
4'b1000: num <= SEGD_NUM8;
4'b1001: num <= SEGD_NUM9;
endcase
end
2'b01: begin
loc <= DUAN_2;
case(ms100)
4'b0000: num <= SEG_NUM0;
4'b0001: num <= SEG_NUM1;
4'b0010: num <= SEG_NUM2;
4'b0011: num <= SEG_NUM3;
4'b0100: num <= SEG_NUM4;
4'b0101: num <= SEG_NUM5;
4'b0110: num <= SEG_NUM6;
4'b0111: num <= SEG_NUM7;
4'b1000: num <= SEG_NUM8;
4'b1001: num <= SEG_NUM9;
endcase
end
2'b10: begin
loc <= DUAN_1;
case(ms10)
4'b0000: num <= SEG_NUM0;
4'b0001: num <= SEG_NUM1;
4'b0010: num <= SEG_NUM2;
4'b0011: num <= SEG_NUM3;
4'b0100: num <= SEG_NUM4;
4'b0101: num <= SEG_NUM5;
4'b0110: num <= SEG_NUM6;
4'b0111: num <= SEG_NUM7;
4'b1000: num <= SEG_NUM8;
4'b1001: num <= SEG_NUM9;
endcase
end
2'b11: begin
loc <= DUAN_0;
case(ms1)
4'b0000: num <= SEG_NUM0;
4'b0001: num <= SEG_NUM1;
4'b0010: num <= SEG_NUM2;
4'b0011: num <= SEG_NUM3;
4'b0100: num <= SEG_NUM4;
4'b0101: num <= SEG_NUM5;
4'b0110: num <= SEG_NUM6;
4'b0111: num <= SEG_NUM7;
4'b1000: num <= SEG_NUM8;
4'b1001: num <= SEG_NUM9;
endcase
end
endcase
end
endmodule
*/
.v