数码管时间显示模块
背景
在学习完小梅哥的串口通信以及数码管显示教程后,他留下了一个课后作业,也就是本次的数码管时间显示模块。作为一个FPGA新人,这也算是第一个比较完整的练手小项目了,也推荐和我一样的新人花时间去完成一下。总体功能虽然比较简单,但是我也花了小两天的时间去编写加调试,其中遇到的具体问题会在后续进行阐述。
代码功能介绍
1、使用数码管完成时间显示功能(即手表时间);
2、通过串口通讯,完成时间的上传(每秒传一次);
3、可通过串口通讯设置时间;
具体代码
TOP模块
`timescale 1ns / 1ps
//
//
// 设计顶层 需完成时钟显示以及串口通信
//
//
module TOP(
input clk,
input rst_n,
input rx_data,
output tx_data,
//output [7:0]smg_sel,
output [3:0]smg_sel,
output [7:0]smg_display
);
//
//
// 时钟计时与更新功能
//
//
wire clk_10mhz;
wire [23:0]final_time;
pll_10mhz pll_inst(
.inclk0(clk),
.c0(clk_10mhz));
timer_and_update(
.clk(clk_10mhz),
.rst_n(rst_n),
.rx_data(rx_data),
.final_time(final_time)
);
//
//
// 数码管显示
//
//
LED_TOP SMG_inst(
.clk(clk_10mhz),
.rst_n(rst_n),
.real_time(final_time),
.smg_display(smg_display),
.smg_sel(smg_sel)
);
//
//
// 串口通信--每一秒将时间上传
//
//
time_send(
.clk_10mhz(clk_10mhz),
.rst_n(rst_n),
.time_rec(final_time),
.tx_data(tx_data)
);
endmodule
在顶层模块中,我们将此次功能代码主要分成3个部分:
1、时间计算及修改模块:通过该模块主要输出数码管最终显示的时间数据
2、数码管显示模块:通过该模块实现时间数据到数码管显示的转换
3、时间上传模块:通过串口进行时间上传
对于代码中的串口发送、接收代码则沿用之前博客中已验证的代码。
timer_and_update模块
`timescale 1ns / 1ps
//
//
// 时钟计时功能--可通过串口接收数据更改时间
//
//
module timer_and_update(
input clk,
input rst_n,
input rx_data,
output [23:0]final_time
);
wire rx_done;
wire [7:0]save_rx_data;
uart_rx_v2 rx_inst(
.clk(clk),
.rst_n(rst_n),
.rx_data(rx_data),
.rx_done(rx_done),
.save_rx_data(save_rx_data)
);
reg [23:0]TEM_final_time;
reg rx_done_3byte;
reg [1:0]rx_cnter;
always @(posedge clk, negedge rst_n) begin
if(!rst_n) begin
rx_done_3byte <= 'd0;
TEM_final_time <= 'd0;
rx_cnter <= 2'd0;
end else begin
if(rx_done) begin
rx_cnter <= rx_cnter + 'd1;
if(rx_cnter <= 2) begin
case(rx_cnter)
'd0: begin
//TEM_final_time[7:0] <= save_rx_data;
TEM_final_time[23:16] <= save_rx_data;
rx_done_3byte <= 0;
end
'd1: begin
TEM_final_time[15:8] <= save_rx_data;
rx_done_3byte <= 0;
end
'd2: begin
//TEM_final_time[23:16] <= save_rx_data;
TEM_final_time[7:0] <= save_rx_data;
rx_cnter <= 'd0;
rx_done_3byte <= 1;
end
default: TEM_final_time <= TEM_final_time;
endcase
end else begin
rx_cnter <= 'b0;
end
end else begin
rx_done_3byte <= 0;
end
end
end
time_cal cal_inst(
.clk_10mhz(clk),
.rst_n(rst_n),
.set_time(TEM_final_time),
.valid(rx_done_3byte),
.final_time(final_time)
);
endmodule
timer_and_update模块–uart_rx_v2(设置时间接收)子模块
//
//
// UART 接收模块
// 此代码中将额外考虑通讯链路稳定问题
// 接收数据将进行7次采样,出现大于4次的信号数据将作为最终采样结果
//
/
module uart_rx_v2(
input clk,
input rst_n,
input rx_data,
output reg rx_done,
output reg [7:0]save_rx_data
);
//
//
// 检测开始接收标志位
//
/
wire RX_START;
reg RX_data_0;
reg RX_data_1;
always @(posedge clk, negedge rst_n)begin
if(!rst_n) begin
RX_data_0 <= 0;
RX_data_1 <= 0;
end else begin
RX_data_0 <= rx_data;
RX_data_1 <= RX_data_0;
end
end
assign RX_START = !RX_data_0 && RX_data_1;
//
//
// bps设置以及计时器
//
/
parameter CLK_HZ = 10_000_000,
BPS = 9600,
BPS_CNT = CLK_HZ / BPS, //每BPS
BPS_CNT_7 = BPS_CNT / 7; //1BPS的7次采样
//计数7分之1个bps的中间位置--更加精确采集数据
reg [31:0]bps_cnter;
reg [7:0]receive_num;
reg START_CNT;
reg half_bps_flag;
always @(posedge clk, negedge rst_n)begin
if(!rst_n) begin
bps_cnter <= 32'b0;
receive_num <= 8'b0;
half_bps_flag <= 1'b0;
end else begin
if(START_CNT) begin
if(bps_cnter > BPS_CNT_7 - 1) begin
receive_num <= receive_num + 1'b1;
half_bps_flag <= 1'b1;
bps_cnter <= 1'b0;
end else begin
bps_cnter <= bps_cnter + 1'b1;
half_bps_flag <= 1'b0;
receive_num <= receive_num;
end
end else begin
bps_cnter <= 32'b0;
half_bps_flag <= 1'b0;
receive_num <= 8'b0;
end
end
end
//产生开始计数标志位
always @(posedge clk, negedge rst_n) begin
if(!rst_n) begin
START_CNT <= 1'b0;
end else begin
if(RX_START) begin
START_CNT <= 1'b1;
end else begin
if(rx_done) begin
START_CNT <= 1'b0;
end
end
end
end
//
//
// 数据接收
//
/
parameter IDLE = 3'd1,
STRAT_JUDGE = 3'd2,
RECEIVE_BEGIN = 3'd3,
FINISH = 3'd4;
reg [2:0]current_state;
reg [2:0]each_data_7[9:0];
always @(posedge clk, negedge rst_n) begin
if(!rst_n) begin
current_state <= IDLE;
end else begin
case(current_state)
IDLE: if(RX_START) current_state <= STRAT_JUDGE;
STRAT_JUDGE: begin
if(each_data_7[0][2] == 0 && receive_num > 7 - 1) current_state <= RECEIVE_BEGIN;
else begin
if(receive_num > 7 - 1) begin
current_state <= IDLE;
end
end
end
RECEIVE_BEGIN: if(receive_num > 69 - 1) current_state <= FINISH;
FINISH: current_state <= IDLE;
default: current_state <= IDLE;
endcase
end
end
always @(posedge clk, negedge rst_n) begin
if(!rst_n) begin
save_rx_data <= 8'b0;
each_data_7[0] <= 'd0;
each_data_7[1] <= 'd0;
each_data_7[2] <= 'd0;
each_data_7[3] <= 'd0;
each_data_7[4] <= 'd0;
each_data_7[5] <= 'd0;
each_data_7[6] <= 'd0;
each_data_7[7] <= 'd0;
each_data_7[8] <= 'd0;
each_data_7[9] <= 'd0;
end else begin
case(current_state)
IDLE: begin
rx_done <= 1'b0;
each_data_7[0] <= 'd0;
each_data_7[1] <= 'd0;
each_data_7[2] <= 'd0;
each_data_7[3] <= 'd0;
each_data_7[4] <= 'd0;
each_data_7[5] <= 'd0;
each_data_7[6] <= 'd0;
each_data_7[7] <= 'd0;
each_data_7[8] <= 'd0;
each_data_7[9] <= 'd0;
end
STRAT_JUDGE: begin
if(half_bps_flag) begin
case(receive_num)
'd0,'d1,'d2,'d3,'d4,'d5, 'd6: each_data_7[0] <= each_data_7[0] + rx_data;
endcase
end
end
RECEIVE_BEGIN: begin
if(half_bps_flag) begin
case(receive_num)
'd7,'d8,'d9,'d10,'d11,'d12, 'd13: each_data_7[1] <= each_data_7[1] + rx_data;
'd14,'d15,'d16,'d17,'d18,'d19, 'd20: each_data_7[2] <= each_data_7[2] + rx_data;
'd21,'d22,'d23,'d24,'d25,'d26, 'd27: each_data_7[3] <= each_data_7[3] + rx_data;
'd28,'d29,'d30,'d31,'d32,'d33, 'd34: each_data_7[4] <= each_data_7[4] + rx_data;
'd35,'d36,'d37,'d38,'d39,'d40, 'd41: each_data_7[5] <= each_data_7[5] + rx_data;
'd42,'d43,'d44,'d45,'d46,'d47, 'd48: each_data_7[6] <= each_data_7[6] + rx_data;
'd49,'d50,'d51,'d52,'d53,'d54, 'd55: each_data_7[7] <= each_data_7[7] + rx_data;
'd56,'d57,'d58,'d59,'d60,'d61, 'd62: each_data_7[8] <= each_data_7[8] + rx_data;
'd63,'d64,'d65,'d66,'d67,'d68, 'd69: each_data_7[9] <= each_data_7[9] + rx_data; //停止位
endcase
end
end
FINISH: begin
rx_done <= 1'b1;
save_rx_data <= {each_data_7[8][2], each_data_7[7][2],each_data_7[6][2],each_data_7[5][2],each_data_7[4][2],
each_data_7[3][2],each_data_7[2][2],each_data_7[1][2]};
end
endcase
end
end
endmodule
timer_and_update模块–time_cal(时间计算)子模块
`timescale 1ns / 1ps
//
//
// 时钟计数功能
// 1秒--10MHZ 100ns 10_000_000;
//
//
//
module time_cal(
input clk_10mhz,
input rst_n,
input [23:0]set_time, //串口设置时间
input valid, //串口时间有效
output [23:0]final_time
);
reg [31:0]sec_cnt;
reg sec_flag;
always @(posedge clk_10mhz, negedge rst_n) begin
if(!rst_n) begin
sec_cnt <= 32'd0;
sec_flag <= 1'b0;
end else begin
if(sec_cnt <= 10_000_000 - 1) begin
sec_cnt <= sec_cnt + 1'b1;
sec_flag <= 1'b0;
end else begin
sec_cnt <= 32'd0;
sec_flag <= 1'b1;
end
end
end
reg [3:0]time_sec_1; //0-9
reg sec_1_flag;
always @(posedge clk_10mhz, negedge rst_n) begin
if(!rst_n) begin
time_sec_1 <= 'd0;
sec_1_flag <= 'd0;
end else begin
if(valid) begin
time_sec_1 <= set_time[3:0];
end else begin
if(sec_flag) begin
if(time_sec_1 <= 'd8) begin
time_sec_1 <= time_sec_1 + 1'b1;
sec_1_flag <= 1'b0;
end else begin
time_sec_1 <= 'd0;
sec_1_flag <= 1'b1;
end
end else begin
sec_1_flag <= 1'b0;
end
end
end
end
reg [3:0]time_sec_2; //0-5
reg sec_2_flag;
always @(posedge clk_10mhz, negedge rst_n) begin
if(!rst_n) begin
time_sec_2 <= 'd0;
sec_2_flag <= 'd0;
end else begin
if(valid) begin
time_sec_2 <= set_time[7:4];
end else begin
if(sec_1_flag) begin
if(time_sec_2 <= 4) begin
time_sec_2 <= time_sec_2 + 1'b1;
sec_2_flag <= 'd0;
end else begin
time_sec_2 <= 'd0;
sec_2_flag <= 'd1;
end
end else begin
sec_2_flag <= 1'b0;
end
end
end
end
reg [3:0]time_min_1; //0-9
reg min_1_flag;
always @(posedge clk_10mhz, negedge rst_n) begin
if(!rst_n) begin
time_min_1 <= 'd0;
min_1_flag <= 'd0;
end else begin
if(valid) begin
time_min_1 <= set_time[11:8];
end else begin
if(sec_2_flag) begin
if(time_min_1 <= 8) begin
time_min_1 <= time_min_1 + 1'b1;
min_1_flag <= 'd0;
end else begin
time_min_1 <= 'd0;
min_1_flag <= 'd1;
end
end else begin
min_1_flag <= 1'b0;
end
end
end
end
reg [3:0]time_min_2; //0-5
reg min_2_flag;
always @(posedge clk_10mhz, negedge rst_n) begin
if(!rst_n) begin
time_min_2 <= 'd0;
min_2_flag <= 'd0;
end else begin
if(valid) begin
time_min_2 <= set_time[15:12];
end else begin
if(min_1_flag) begin
if(time_min_2 <= 4) begin
time_min_2 <= time_min_2 + 1'b1;
min_2_flag <= 'd0;
end else begin
time_min_2 <= 'd0;
min_2_flag <= 'd1;
end
end else begin
min_2_flag <= 1'b0;
end
end
end
end
reg [3:0]time_h_1; //0-9
reg h_1_flag;
reg reverse_flag;
always @(posedge clk_10mhz, negedge rst_n) begin
if(!rst_n) begin
time_h_1 <= 'd0;
h_1_flag <= 'd0;
reverse_flag <= 'd0;
end else begin
if(valid) begin
time_h_1 <= set_time[19:16];
//if(set_time[23:21] == 'b101) begin
//if(set_time[23:16] > 'd19) begin 不行
//if(set_time[23:16] > 'b10011) begin 不行
if(set_time[23:16] > 'b0001_0011) begin
reverse_flag <= 1;
end else begin
reverse_flag <= 0;
end
end else begin
if(min_2_flag) begin
if(time_h_1 <= 8 && !reverse_flag) begin
time_h_1 <= time_h_1 + 1'b1;
h_1_flag <= 'd0;
end else begin
if(!reverse_flag) begin
reverse_flag <= ~ reverse_flag;
time_h_1 <= 'd0;
h_1_flag <= 'd1;
end else begin
if(time_h_1 <= 2 && reverse_flag) begin
time_h_1 <= time_h_1 + 1'b1;
h_1_flag <= 'd0;
end else begin
if(reverse_flag) begin
reverse_flag <= ~ reverse_flag;
time_h_1 <= 'd0;
h_1_flag <= 'd1;
end
end
end
end
end else begin
h_1_flag <= 'd0;
end
end
end
end
reg [3:0]time_h_2; //0-2
always @(posedge clk_10mhz, negedge rst_n) begin
if(!rst_n) begin
time_h_2 <= 'd0;
end else begin
if(valid) begin
time_h_2 <= set_time[23:20];
end else begin
if(h_1_flag) begin
if(time_h_2 <= 1) begin
time_h_2 <= time_h_2 + 1'b1;
end else begin
time_h_2 <= 'd0;
end
end
end
end
end
assign final_time = {time_h_2, time_h_1, time_min_2, time_min_1, time_sec_2, time_sec_1};
endmodule
数码管显示模块(LED_TOP)
该模块主要实现将上述模块得到的时间数据用于数码管驱动
`timescale 1ns / 1ps
//
//
// 该模块实现时钟的4位数码管显示
//
//
module LED_TOP(
input clk,
input rst_n,
input [23:0]real_time,
output reg [7:0]smg_display,
//output reg [7:0] smg_sel
output reg [3:0] smg_sel
);
wire [47:0]led_ctrl;
LED_DECODE LED_second_1(
.led_num(real_time[3:0]),
.led_show(led_ctrl[7:0])
);
LED_DECODE LED_second_2(
.led_num(real_time[7:4]),
.led_show(led_ctrl[15:8])
);
LED_DECODE LED_min_1(
.led_num(real_time[11:8]),
.led_show(led_ctrl[23:16])
);
LED_DECODE LED_min_2(
.led_num(real_time[15:12]),
.led_show(led_ctrl[31:24])
);
LED_DECODE LED_h_1(
.led_num(real_time[19:16]),
.led_show(led_ctrl[39:32])
);
LED_DECODE LED_h_2(
.led_num(real_time[23:20]),
.led_show(led_ctrl[47:40])
);
//
//
// 数码管切换
//
//
reg [32:0]smg_sel_cnt;
reg flag;
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
smg_sel_cnt <= 'b0;
flag <= 0;
end else begin
if(smg_sel_cnt <= 'd100) begin
smg_sel_cnt <= smg_sel_cnt + 1;
flag <= 0;
end else begin
smg_sel_cnt <= 0;
flag <= 1;
end
end
end
reg [1:0]smg_cnt;
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
smg_cnt <= 2'b0;
end else begin
if(flag) begin
smg_cnt <= smg_cnt + 1'b1;
end else begin
smg_cnt <= smg_cnt;
end
end
end
//由于开发板只有四个数码管,故此处只写了4个
always @(smg_cnt) begin
case(smg_cnt)
'b00: smg_sel = 'b1110;
'b01: smg_sel = 'b1101;
'b10: smg_sel = 'b1011;
'b11: smg_sel = 'b0111;
default: smg_sel = 'b1111;
endcase
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
smg_display <= 'd0;
end else begin
case(smg_sel)
//4'b1110:smg_display <= led_ctrl[7:0];
//4'b1101:smg_display <= led_ctrl[15:8];
4'b0111:smg_display <= led_ctrl[23:16];
4'b1011:smg_display <= led_ctrl[31:24];
4'b1101:smg_display <= led_ctrl[39:32];
4'b1110:smg_display <= led_ctrl[47:40];
default:smg_display <= 8'b1111_1111;
endcase
end
end
endmodule
数码管显示模块(LED_TOP)–(数码管解码子模块)LED_DECODE
`timescale 1ns / 1ps
//
//
// 该模块主要实现将接收到的数据转换成LED对应显示(8段数码管)
//
//
module LED_DECODE(
input [3:0] led_num,
output reg [7:0] led_show
);
//将接收到的数据转换成数码管形式
parameter NUM_ZERO = 8'b0000_0011,
NUM_ONE = 8'b1001_1111,
NUM_TWO = 8'b0010_0101,
NUM_THREE = 8'b0000_1101,
NUM_FOUR = 8'b1001_1001,
NUM_FIVE = 8'b0100_1001,
NUM_SIX = 8'b0100_0001,
NUM_SEVEN = 8'b0001_1111,
NUM_EIGHT = 8'b0000_0001,
NUM_NINE = 8'b0000_1001;
always @(*) begin
case(led_num)
4'd0: led_show = NUM_ZERO;
4'd1: led_show = NUM_ONE;
4'd2: led_show = NUM_TWO;
4'd3: led_show = NUM_THREE;
4'd4: led_show = NUM_FOUR;
4'd5: led_show = NUM_FIVE;
4'd6: led_show = NUM_SIX;
4'd7: led_show = NUM_SEVEN;
4'd8: led_show = NUM_EIGHT;
4'd9: led_show = NUM_NINE;
default: led_show = NUM_ZERO;
endcase
end
endmodule
时间数据上传模块(time_send)
`timescale 1ns / 1ps
//
//
// 串口通信--每一秒将时间上传
// 需将时间数据拆成3包 分别发送
//
//
module time_send(
input clk_10mhz,
input rst_n,
input [23:0]time_rec,
output tx_data
);
reg sec_flag;
reg [31:0]sec_cnt;
always @(posedge clk_10mhz, negedge rst_n) begin
if(!rst_n) begin
sec_cnt <= 32'd0;
sec_flag <= 'd0;
end else begin
if(sec_cnt <= 10_000_000 - 1) begin
sec_cnt <= sec_cnt + 1'b1;
sec_flag <= 'd0;
end else begin
sec_cnt <= 32'd0;
sec_flag <= 'd1;
end
end
end
reg [1:0]send_pos_cnt;
always @(posedge clk_10mhz, negedge rst_n) begin
if(!rst_n) begin
send_pos_cnt <= 2'b0;
end else begin
send_pos_cnt <= send_pos_cnt + 1'b1;
end
end
reg [7:0]time_8bit;
always @(posedge clk_10mhz, negedge rst_n) begin
if(!rst_n) begin
time_8bit <= 'd0;
end else begin
case(send_pos_cnt)
'd0:time_8bit <= time_rec[23:16];
'd1:time_8bit <= time_rec[15:8];
'd2:time_8bit <= time_rec[7:0];
'd3:time_8bit <= 'b1111_1111;
default: time_8bit <= 'b1111_1111;
endcase
end
end
wire tx_done;
uart_tx(.clk(clk_10mhz),
.rst_n(rst_n),
.tx_en(sec_flag),
.data(time_8bit),
.tx_data(tx_data),
.tx_done(tx_done)
);
endmodule
时间数据上传模块(time_send)–串口发送子模块 (uart_tx)
/
UART发送模块
8BIT数据位+1BIT停止位
波特率默认9600
/
module uart_tx(input clk,
input rst_n,
input tx_en,
input [7:0]data,
output reg tx_data,
output reg tx_done
);
/
波特率计算以及bps计数器
/
parameter CLK_BPS = 9600;
parameter BPS_cnt = 10_000_000/CLK_BPS;
reg clk_bps;
reg [31:0]CNT_BPS;
always @(posedge clk, negedge rst_n) begin
if(!rst_n) begin
clk_bps <= 0;
CNT_BPS <= 0;
end else begin
if(CNT_BPS <= BPS_cnt) begin
clk_bps <= 1'b0;
CNT_BPS <= CNT_BPS + 1;
end else begin
clk_bps <= 1'b1;
CNT_BPS <= 0;
end
end
end
reg [7:0]need_tx_data;
always @(posedge clk) begin
if(tx_en) begin
need_tx_data <= data;
end
end
/
发送状态机
/
parameter IDLE = 6'd0,
SEND_1 = 6'd1,
SEND_2 = 6'd2,
SEND_3 = 6'd3,
SEND_4 = 6'd4,
SEND_5 = 6'd5,
SEND_6 = 6'd6,
SEND_7 = 6'd7,
SEND_8 = 6'd8,
SEND_9 = 6'd9,
SEND_10 = 6'd10,
FINISH = 6'd11;
reg [5:0]current_state;
reg [3:0]tx_cnt;
always @(posedge clk, negedge rst_n) begin
if(!rst_n) begin
current_state <= 3'd0;
end else begin
case(current_state)
IDLE: begin
if(tx_en) current_state <= SEND_1;
else current_state <= current_state;
end
SEND_1: begin
if(clk_bps) begin
current_state <= SEND_2;
end else current_state <= current_state;
end
SEND_2: begin
if(clk_bps) begin
current_state <= SEND_3;
end else current_state <= current_state;
end
SEND_3: begin
if(clk_bps) begin
current_state <= SEND_4;
end else current_state <= current_state;
end
SEND_4: begin
if(clk_bps) begin
current_state <= SEND_5;
end else current_state <= current_state;
end
SEND_5: begin
if(clk_bps) begin
current_state <= SEND_6;
end else current_state <= current_state;
end
SEND_6: begin
if(clk_bps) begin
current_state <= SEND_7;
end else current_state <= current_state;
end
SEND_7: begin
if(clk_bps) begin
current_state <= SEND_8;
end else current_state <= current_state;
end
SEND_8: begin
if(clk_bps) begin
current_state <= SEND_9;
end else current_state <= current_state;
end
SEND_9: begin
if(clk_bps) begin
current_state <= SEND_10;
end else current_state <= current_state;
end
SEND_10: begin
if(clk_bps) begin
current_state <= FINISH;
end else current_state <= current_state;
end
FINISH: current_state <= IDLE;
default: current_state <= IDLE;
endcase
end
end
always @(posedge clk, negedge rst_n) begin
if(!rst_n) begin
tx_cnt <= 4'd0;
end else begin
case(current_state)
IDLE: begin
tx_data <= 1'b1;
tx_done <= 1'd0;
end
SEND_1: begin
if(clk_bps) begin
tx_data <= 0; //发送开始位
end
end
SEND_2: begin
if(clk_bps) begin
tx_data <= need_tx_data[0];
end
end
SEND_3: begin
if(clk_bps) begin
tx_data <= need_tx_data[1];
end
end
SEND_4: begin
if(clk_bps) begin
tx_data <= need_tx_data[2];
end
end
SEND_5: begin
if(clk_bps) begin
tx_data <= need_tx_data[3];
end
end
SEND_6: begin
if(clk_bps) begin
tx_data <= need_tx_data[4];
end
end
SEND_7: begin
if(clk_bps) begin
tx_data <= need_tx_data[5];
end
end
SEND_8: begin
if(clk_bps) begin
tx_data <= need_tx_data[6];
end
end
SEND_9: begin
if(clk_bps) begin
tx_data <= need_tx_data[7];
end
end
SEND_10: begin
if(clk_bps) begin
tx_data <= 1; //发送停止位
end
end
FINISH: begin
tx_cnt <= 4'd0;
tx_done <= 1'd1;
end
endcase
end
end
endmodule
代码验证
串口助手验证
时间上传
注意:我的上传时间是每一秒传一个时间单位(小时、分钟、秒钟),在串口中可以看出,目前时间是下午13点22分08秒;其中FF只是为了方便看当前时间,作分割用。
时间设置
可以看见当我像开发板发送21 30 12时,其对应时间也被设置为了晚上21点30分15秒(由于3秒延时上传)。
开发板数码管显示
上图显示时间为21点33分
问题总结
1、数码管切换问题:
数码管的选通实际是以非常快的速度逐个切换的(即同一时间实际只有一个数码管亮),由于视觉暂留效果会得到4个数码管同时显示的效果。但实际开发中,我发现若以10MHZ的频率去切换数码管,实际将导致数码管显示错乱,后续增加了一个延时(计时100个clk才切换)才解决该问题。
在未加延时时,通过signaltop对信号进行抓取,发现case语句居然跑飞了(会出现进入default语句的结果),这也实在是不可思议。
2、串口时间接收问题
在串口助手中,我们直接发送需要设置的时间,例如233021(21点30分21秒)。一开始我以为串口发送数据的顺序为21–>30–>23,但是实际上串口发送助手是发送23、30、21。
3、数组的比较问题
一开始我使用了 if(set_time[23:16] > 'd19) 和 if(set_time[23:16] > 'b10011) 形式,但实际上都是行不通的,这种数据切片比较必须把所有位数列出:if(set_time[23:16] > 'b0001_0011)文章来源:https://www.toymoban.com/news/detail-838285.html
已知BUG
目前代码并未对设置时间进行检查,也就是说如果你设置时间为99点99分99秒也是会继续计时下去的,并未对其进行防护,如果有有兴趣的可以自己在时间计算模块内部进行设置时间检查代码的添加。文章来源地址https://www.toymoban.com/news/detail-838285.html
到了这里,关于FPGA学习之数码管时间显示模块的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!