FPGA学习之数码管时间显示模块

这篇具有很好参考价值的文章主要介绍了FPGA学习之数码管时间显示模块。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

数码管时间显示模块

背景

在学习完小梅哥的串口通信以及数码管显示教程后,他留下了一个课后作业,也就是本次的数码管时间显示模块。作为一个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

代码验证

串口助手验证

时间上传

FPGA学习之数码管时间显示模块,fpga开发,学习
注意:我的上传时间是每一秒传一个时间单位(小时、分钟、秒钟),在串口中可以看出,目前时间是下午13点22分08秒;其中FF只是为了方便看当前时间,作分割用。

时间设置

FPGA学习之数码管时间显示模块,fpga开发,学习
可以看见当我像开发板发送21 30 12时,其对应时间也被设置为了晚上21点30分15秒(由于3秒延时上传)。

开发板数码管显示

FPGA学习之数码管时间显示模块,fpga开发,学习
上图显示时间为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)

已知BUG

目前代码并未对设置时间进行检查,也就是说如果你设置时间为99点99分99秒也是会继续计时下去的,并未对其进行防护,如果有有兴趣的可以自己在时间计算模块内部进行设置时间检查代码的添加。文章来源地址https://www.toymoban.com/news/detail-838285.html

到了这里,关于FPGA学习之数码管时间显示模块的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [FPGA 学习记录] 数码管静态显示

    数码管静态显示 在我们的许多项目设计当中,我们通常需要一些显示设备来显示我们需要的信息。我们可以选择的显示设备有很多,而我们的数码管就是使用最多、最简单的显示设备之一。数码管它是一种半导体发光器件,它具有响应时间短、体积小、重量轻、寿命长的优点

    2024年02月03日
    浏览(40)
  • [FPGA 学习记录] 数码管动态显示

    数码管动态显示 在上一小节当中,我们对数码管的静态显示做了一个详细的讲解;但是如果单单只掌握数码管的静态显示这种显示方式是远远不够的,因为数码管的静态显示当中,被选中的数码位它们显示的内容都是相同的,这种显示方式在我们的实际应用当中显然是不合适

    2024年02月04日
    浏览(39)
  • FPGA实战------数码管(1)静态显示

    led的花样点灯差不多了吧,接下来学习另一个基础的东西,数码管。 数码管在FPGA开发板上占得位置不小,在工程开发中也必不可少,比如后边的温度传感器就会用数码管来显示温度。这里先不多介绍温度传感器,过一段时间就会发了。本篇文章先用数码管来做静态显示。也

    2024年02月04日
    浏览(35)
  • FPGA基础设计之数码管显示

    数码管是一种半导体发光器件,其基本单元是发光二极管。一般分为七段数码管和八段数码管,多的一段是小数点。也有其他如N型、米型数码管以及16段、24段管等。本次设计的是八段数码管 公阴极数码管高电平亮,公阳极数码管低电平亮。AC620上搭载的是公阳极数码管。 数

    2023年04月26日
    浏览(36)
  • FPGA(5)数码管静态显示与动态显示

    目录 一、数码管静态显示 二、数码管动态显示 1、变量定义 2、定时(60us) 3、动态显示 代码 FPGA的数码管有4位,8段 。( 位和段都是共阳,即低电平有效 )     位选的4位(二进制):分别为第1、2、3、4位数码管。 段选的8位(二进制):分别为第h、g、f、e、d、c、b、a段

    2023年04月12日
    浏览(33)
  • FPGA项目(5)--FPGA控制数码管动态显示的原理

            数码管是现在电子产品上常用的显示器件,它有驱动简单、显示清晰、价格低廉等优势。数码管的实物图:          数码管的内部结构图如下所示:          从图中可以看出,它由八个段组成,即A B C D E F G DP(小数点),只要将这八个段按规律组合点亮,就

    2024年02月11日
    浏览(35)
  • FPGA学习汇总(六)----数码管显示(1)

    目录 概念 单个数码管显示单个数字  操作  代码  现象 分析 四个数码管定时单个显示数字 分析 代码 四个数码管同时显示 分析 代码 现象

    2024年02月01日
    浏览(37)
  • 二、19【FPGA】数码管动态显示实验

    学习说明此文档为本人的学习笔记,注重实践,关于理论部分会给出相应的学习链接。 学习视频:是根据野火FPGA视频教程——第二十二讲 https://www.bilibili.com/video/BV1nQ4y1Z7zN?p=3 “天下武功唯快不破”    “看到的不一定为真” 眼睛的视觉暂留:光信号传入大脑需要短暂时间,

    2023年04月08日
    浏览(30)
  • 【FPGA】数码管电子时钟(可设置时间和闹钟)

    本次实验内容承接上一篇文章数码管电子时钟,在此基础上新增两个功能: 1.设置时间 2.设置闹钟,到点响铃 模块:beep counter seg_driver top 其中: 1.设置时间 2.设置闹钟,到点响铃 这两个功能都整合在counter模块,里面设置的重要信号如下所示 我们需要利用开发板上的按键来设

    2023年04月08日
    浏览(34)
  • FPGA基本实验之数码管的动态显示

            关于数码管的基本知识大家可以参考我上一篇文章数码管的静态显示,         使用 1ms 的刷新时间让六个数码管轮 流显示:第 1ms 点亮第一个数码管,第 2ms 点亮第二个数码管,以此类推依次点亮六个数 码管,6ms 一个轮回,也就是说每个数码管每 6ms 点亮

    2024年02月08日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包