串口-时钟系列知识分享:

(1)串口通信实现-串口接收
(2)FPGA 驱动数码管动态显示
(3)使用串口发送实现ACX720开发板时钟显示

前言

本篇使用串口发送实现ACX720开发板时钟显示(Verilog&Vivado)。

1、 digital_clock顶层模块控制

用于用于实现整个模块控制,分为两个子模块1.hex_top(用于实现数码管数字显示) 2.uart_byte_rx(用于实现串口发送)3.自身用于实现时分秒的控制
digital_clock顶层模块控制输入、输出草图如下所示:顶层控制模块代码如下:

`timescale 1ns / 1ps
module digital_clock(
    clk,
    reset_n,
    sh_cp,
	st_cp,
	rx,
	ds
    );
    input clk;
    input reset_n;
    input rx;
    output sh_cp;
    output st_cp;
    output ds;
    
   
    reg [31:0] display_data;
    parameter second_cnt = 26'd50000000;
    hex_top hex_top(
    .clk(clk),
	.reset_n(reset_n),
	.sh_cp(sh_cp),
	.st_cp(st_cp),
	.ds(ds),
	.disp_data(display_data)
    );
    
    wire [7:0] data;
    wire rx_done;
    uart_byte_rx uart_byte_rx_demo(
	.clk(clk),
	.reset(reset_n),
	.baud_set(3'd4),
	.uart_rx(rx),
	.data_byte(data),
	.rx_done(rx_done)
);
    
    reg [1:0] set;
    always@(posedge clk or negedge reset_n)begin
        if(!reset_n)
            set <= 0;
        else if(set == 2'd3)
                set<= 1'd0;
        else if(rx_done)
                set <= set+ 1'd1;
    end
 //-- counter == second_cnt - 1'b1计时1s钟------------------------------------------------------------------  
     reg [25:0] counter;
    always@(posedge clk or negedge reset_n)begin
        if(!reset_n)
            counter <= 0;
        else if(counter == second_cnt - 1'b1)
            counter <= 0;
        else
            counter <= counter + 1'b1;  
    end
//-------------------------------------------------------------------------------------------------------  
    
//--产生秒脉冲sec_flag------------------------------------------------------------------------------------------------  
    reg sec_flag;
    always@(posedge clk or negedge reset_n)begin//产生秒脉冲
        if(!reset_n)
            sec_flag <= 1'b0;
        else if(counter == second_cnt - 1'b1)
             sec_flag <= 1'b1;
        else
            sec_flag <= 1'b0;
    end
 //----------------------------------------------------------------------------------------------------------   
    
 // --秒计数需要两个数码管故分为 second[7:4]  与  second[3:0]对两个数码管分别赋值
    reg [7:0]second;
    always@(posedge clk or negedge reset_n)begin
        if(!reset_n)
            second <= 8'd0;
        else if((rx_done )&&(set== 2'd2))
            second <= data;
        else if( sec_flag)begin
            if((second[7:4] == 4'd5)&&(second[3:0] == 4'd9))begin
                second <=8'd0;
            end
            else if(second[3:0] == 4'd9)begin
                second[7:4] <= second[7:4] + 1;
                second[3:0] <= 4'd0;
            end
            else
                second[3:0] <= second[3:0] + 1'b1;
        end
    end
 //-------------------------------------------------------------------------------------------------------
     reg [7:0]minute;
     always@(posedge clk or negedge reset_n)begin
        if(!reset_n)
           minute <= 8'd0;
         else if((rx_done )&&(set== 2'd1))
           minute <= data;
        else if(sec_flag)begin
            if((second[7:4] == 4'd5)&&(second[3:0] == 4'd9))begin
                if((minute[7:4]== 4'd5)&&(minute[3:0]== 4'd9))
                       minute <= 8'd0;
                else if(minute[3:0] == 4'd9)begin
                       minute[7:4] <= minute[7:4] + 1'b1;
                       minute[3:0] <= 4'd0;
                  end   
                else
                       minute[3:0] <= minute[3:0] + 1'b1;
        end
        end
    end
    
   reg [7:0]hour;
    always@(posedge clk or negedge reset_n)begin
        if(!reset_n)
           hour <= 8'd0;
        else if((rx_done )&&(set== 2'd0))
           hour <= data;
        else if(sec_flag)begin
             if((second[7:4] == 4'd5)&&(second[3:0] == 4'd9)&&(minute[7:4] == 4'd5)&&(minute[3:0] == 4'd9))begin
                if((hour[7:4]==4'd2)&&(hour[3:0] == 4'd3))
                    hour <= 8'd0;
                else if(hour[3:0] == 4'd9)begin
                    hour[3:0] <= 4'd0;
                    hour[7:4] <= hour[7:4] + 1'b1;
               end
               else
                    hour[3:0] <= hour[3:0] + 1'b1;
        end
       end
     end
     
 //--依次赋给display_data
     always@(posedge clk or negedge reset_n)
        if(!reset_n)
            display_data <= 32'd0;
        else if(sec_flag)
            //display_data <= {8'h0,hour[7:4],hour[3:0],minute[7:4],minute[3:0],second[7:4],second[3:0]};
            display_data <= {hour,4'hf,minute,4'hf,second};//4'f点亮为-,为了间隔开时分秒
endmodule

2、 串口发送模块

1.串口发送输入、输出草图如下

串口发送模块代码如下:

`timescale 1ns / 1ps

module uart_byte_rx(
	clk,
	reset,
	baud_set,
	uart_rx,

	data_byte,
	rx_done
);
    

	input clk;    //模块全局时钟输入,50M
	input reset;   //复位信号输入,低有效

	input [2:0]baud_set;  //波特率设置
	input uart_rx;   //串口输入信号

	output [7:0]data_byte; //串口接收的1byte数据
	output rx_done;   //1byte数据接收完成标志

	reg [7:0]data_byte;
	reg rx_done;
	reg uart_rx_sync1;   //同步寄存器
	reg uart_rx_sync2;   //同步寄存器

	reg uart_rx_reg1;    //数据寄存器
	reg uart_rx_reg2;    //数据寄存器

	reg [15:0]bps_DR;    //分频计数最大值
	reg [15:0]div_cnt;   //分频计数器
	reg bps_clk;   //波特率时钟
	reg [7:0] bps_cnt;   //波特率时钟计数器
	reg uart_EN;//接收数据状态

	wire uart_rx_nedge;  //下降沿检测

	reg [2:0]START_BIT;
	reg [2:0]STOP_BIT;
	reg [2:0]data_byte_pre [7:0];

	//同步串行输入信号,消除亚稳态
	always@(posedge clk or posedge reset)
	if(!reset)begin
		uart_rx_sync1 <= 1'b0;
		uart_rx_sync2 <= 1'b0;
	end
	else begin
		uart_rx_sync1 <= uart_rx;
		uart_rx_sync2 <= uart_rx_sync1;
	end

	//数据寄存器
	always@(posedge clk or posedge reset)
	if(!reset)begin
		uart_rx_reg1 <= 1'b0;
		uart_rx_reg2 <= 1'b0;
	end
	else begin
		uart_rx_reg1 <= uart_rx_sync2;
		uart_rx_reg2 <= uart_rx_reg1;
	end

  //下降沿检测
	assign uart_rx_nedge = !uart_rx_reg1 & uart_rx_reg2;

	always@(posedge clk or posedge reset)
	if(!reset)
		bps_DR <= 16'd324;
	else begin
		case(baud_set)
			0:bps_DR <= 16'd324;
			1:bps_DR <= 16'd162;
			2:bps_DR <= 16'd80;
			3:bps_DR <= 16'd53;
			4:bps_DR <= 16'd26;
			default:bps_DR <= 16'd324;		
		endcase
	end


    always@(posedge clk or posedge reset)
        if(!reset)
            uart_EN <= 1'b0;
        else if(uart_rx_nedge)
            uart_EN <= 1'b1;
        else if(rx_done || (bps_cnt == 8'd12 && (START_BIT > 2)) || (bps_cnt == 8'd155 && (STOP_BIT < 3)))
            uart_EN <= 1'b0;
        else
            uart_EN <= uart_EN;

	
	always@(posedge clk or posedge reset)
	if(!reset)
		div_cnt <= 16'd0;
	else if(uart_EN)begin
		if(div_cnt == bps_DR)
			div_cnt <= 16'd0;
		else
			div_cnt <= div_cnt + 1'b1;
	end
	else
		div_cnt <= 16'd0;
	
	
	always@(posedge clk or posedge reset)
	if(!reset)
		bps_clk <= 1'b0;
	else if(div_cnt == 16'd1)
		bps_clk <= 1'b1;
	else
		bps_clk <= 1'b0;

	
	always@(posedge clk or posedge reset)
	if(!reset)
		bps_cnt <= 8'd0;
	else if(bps_cnt == 8'd159 || (bps_cnt == 8'd12 && (START_BIT > 2)))
		bps_cnt <= 8'd0;
	else if(bps_clk)
		bps_cnt <= bps_cnt + 1'b1;
	else
		bps_cnt <= bps_cnt;

	always@(posedge clk or posedge reset)
	if(!reset)
		rx_done <= 1'b0;
	else if(bps_cnt == 8'd159)
		rx_done <= 1'b1;
	else
		rx_done <= 1'b0;	
	
	always@(posedge clk or posedge reset)
	if(!reset)begin
		START_BIT <= 3'd0;
		data_byte_pre[0] <= 3'd0;
		data_byte_pre[1] <= 3'd0;
		data_byte_pre[2] <= 3'd0;
		data_byte_pre[3] <= 3'd0;
		data_byte_pre[4] <= 3'd0;
		data_byte_pre[5] <= 3'd0;
		data_byte_pre[6] <= 3'd0;
		data_byte_pre[7] <= 3'd0;
		STOP_BIT <= 3'd0;
	end
	else if(bps_clk)begin
		case(bps_cnt)
			0:begin
        START_BIT <= 3'd0;
        data_byte_pre[0] <= 3'd0;
        data_byte_pre[1] <= 3'd0;
        data_byte_pre[2] <= 3'd0;
        data_byte_pre[3] <= 3'd0;
        data_byte_pre[4] <= 3'd0;
        data_byte_pre[5] <= 3'd0;
        data_byte_pre[6] <= 3'd0;
        data_byte_pre[7] <= 3'd0;
        STOP_BIT <= 3'd0;		
      end
			6 ,7 ,8 ,9 ,10,11:START_BIT <= START_BIT + uart_rx_sync2;
			22,23,24,25,26,27:data_byte_pre[0] <= data_byte_pre[0] + uart_rx_sync2;
			38,39,40,41,42,43:data_byte_pre[1] <= data_byte_pre[1] + uart_rx_sync2;
			54,55,56,57,58,59:data_byte_pre[2] <= data_byte_pre[2] + uart_rx_sync2;
			70,71,72,73,74,75:data_byte_pre[3] <= data_byte_pre[3] + uart_rx_sync2;
			86,87,88,89,90,91:data_byte_pre[4] <= data_byte_pre[4] + uart_rx_sync2;
			102,103,104,105,106,107:data_byte_pre[5] <= data_byte_pre[5] + uart_rx_sync2;
			118,119,120,121,122,123:data_byte_pre[6] <= data_byte_pre[6] + uart_rx_sync2;
			134,135,136,137,138,139:data_byte_pre[7] <= data_byte_pre[7] + uart_rx_sync2;
			150,151,152,153,154,155:STOP_BIT <= STOP_BIT + uart_rx_sync2;
			default:
      begin
        START_BIT <= START_BIT;
        data_byte_pre[0] <= data_byte_pre[0];
        data_byte_pre[1] <= data_byte_pre[1];
        data_byte_pre[2] <= data_byte_pre[2];
        data_byte_pre[3] <= data_byte_pre[3];
        data_byte_pre[4] <= data_byte_pre[4];
        data_byte_pre[5] <= data_byte_pre[5];
        data_byte_pre[6] <= data_byte_pre[6];
        data_byte_pre[7] <= data_byte_pre[7];
        STOP_BIT <= STOP_BIT;
      end
		endcase
	end

	always@(posedge clk or posedge reset)
	if(!reset)
		data_byte <= 8'd0;
	else if(bps_cnt == 8'd159)begin
		data_byte[0] <= data_byte_pre[0][2];
		data_byte[1] <= data_byte_pre[1][2];
		data_byte[2] <= data_byte_pre[2][2];
		data_byte[3] <= data_byte_pre[3][2];
		data_byte[4] <= data_byte_pre[4][2];
		data_byte[5] <= data_byte_pre[5][2];
		data_byte[6] <= data_byte_pre[6][2];
		data_byte[7] <= data_byte_pre[7][2];
	end

		

endmodule

3、 hex_top

用于实现数码管显示其包含两个子模块1.hex_display数码管显示2.hex_hc595数码管引脚转换
其输入、输出草图如下:

其代码展示如下:

`timescale 1ns / 1ps
module hex_top(
    clk,
	reset_n,
	sh_cp,
	st_cp,
	ds,
	disp_data
    );
    input clk;
    input reset_n;
    input [31:0]disp_data;
    output sh_cp;
    output st_cp;
    output ds;
    wire[7:0] sel;
    wire[7:0] seg;
    wire [15:0] data;
    assign data = {seg,sel};//先传段选再传位选
    hex_display hex_display(
	.clk(clk),
	.reset_n(reset_n),
	.disp_data(disp_data),
	.sel(sel),
	.seg(seg)
    );
  
    hex_hc595 hc595(
	.clk(clk),
	.reset_n(reset_n),
	.data(data),
	.sh_cp(sh_cp),
	.st_cp(st_cp),
	.en(1'b1),
	.ds(ds)
    );
endmodule

4、 hex_display模块

用于实现数码管显示其输入、输出草图如下:

代码展示如下:

`timescale 1ns / 1ps
module hex_display(
   clk,
	reset_n,
	disp_data,
	sel,
	seg
    );
	input clk;
	input reset_n;
	input [31:0] disp_data;//要现实的数据一共对应八位数码管的八位
	output reg [7:0] sel;//位选输出
	output reg [7:0] seg;//译码输出 a-h : seg[0]-seg[7]

	reg clk_div;   //计时1ms时间产生EN信号
	reg [14:0]div_cnt;   //计时1ms时间
	reg [2:0]num_cnt;     //产生8个数据进行赋值操作
	always@(posedge clk or negedge reset_n)begin
		if(!reset_n)
			div_cnt <= 15'd0;
		else if(div_cnt == 24999)
			div_cnt <= 15'd0;
		else
			div_cnt <= div_cnt + 1'd1;

	end

	always@(posedge clk or negedge reset_n)begin
		if(!reset_n)
			clk_div <= 1;
		else if(div_cnt == 24999)
			clk_div <= 1'b1;
		else 
		    clk_div <= 0;
	end

	always@(posedge clk or negedge reset_n)begin
		if(!reset_n)
			num_cnt <= 0;
		else if(clk_div)
			num_cnt <= num_cnt + 1;
	end
//--每隔1ms进行赋值操作  sel控制8个数码管的开关-----------------------------------------------------------
	always@(*)begin
		case(num_cnt)
			0: sel <= 8'b00000001;
			1: sel <= 8'b00000010;
			2: sel <= 8'b00000100;
			3: sel <= 8'b00001000;
			4: sel <= 8'b00010000;
			5: sel <= 8'b00100000;
			6: sel <= 8'b01000000;
			7: sel <= 8'b10000000;
		endcase
	end
//--------------------------------------------------------------------------------------------------------
	
//--每个数码管想要输出的数值------------------------------------------------------------------------------	
	reg [3:0]disp_temp;
	always@(*)begin
		case(num_cnt)
			0: disp_temp <= disp_data[3:0];
			1: disp_temp <= disp_data[7:4];
			2: disp_temp <= disp_data[11:8];
			3: disp_temp <= disp_data[15:12];
			4: disp_temp <= disp_data[19:16];
			5: disp_temp <= disp_data[23:20];
			6: disp_temp <= disp_data[27:24];
			7: disp_temp <= disp_data[31:28];
		endcase
	end
//--------------------------------------------------------------------------------------------------------

//--查找表控制数码管输出-----------------------------------------------------------------------------------
//--ACX720FPGA板为共阳极  
//--当disp_data[31:28]==0时数码管a、b、c、d、e、f为低电平 g、dp为高电平
//--seg为8’b11000000
	always@(*)begin
		case(disp_temp)
			4'h0:	seg = 8'hc0;
			4'h1:	seg = 8'hf9;
			4'h2:	seg = 8'ha4;
			4'h3:	seg = 8'hb0;
			4'h4:	seg = 8'h99;
			4'h5:	seg = 8'h92;
			4'h6:	seg = 8'h82;
			4'h7:	seg = 8'hf8;
			4'h8:	seg = 8'h80;
			4'h9:	seg = 8'h90;
			4'hf :  seg =  8'hbf; //此处显示不是f而是-,为了上板区分时分秒
			default:seg =  8'hc0;
		endcase
	end
//----------------------------------------------------------------------------------------------------------
endmodule

5、 hex_hc595模块

ACX720开发板使用HC595芯片作为驱动,故无法分配给sel、seg引脚于是使用hex_hc595模块转换
其中[15:0]data = {[seg,sel]}其输入输出草图如下:

hex_hc595模块输出波形图如下:

hex_hc595模块代码展示如下:

`timescale 1ns / 1ps

module hex_hc595(
	clk,
	reset_n,
	data,
	sh_cp,
	st_cp,
	en,
	ds
    );
	input clk;
	input reset_n;
	input [15:0] data;
	input en;
	output reg sh_cp;   //HC595输出波形图中的时钟信号12.5MHZ(3.3V)
	output reg st_cp;   
	output reg ds;
	reg [5:0] shcp_edge_cnt;
	reg [15:0] r_data;
	always@(posedge clk)begin
		if(en)
		  r_data <= data;
	end
	reg[2:0] div_cnt;
	
//---产生时钟分频25MHz--------------------------------------------------------------------------------------------------------------------------------------
	always@(posedge clk or negedge reset_n)begin
		if(!reset_n)
			div_cnt <= 0;
		else  if(div_cnt == 1'b1)
			div_cnt <= 0;
		else
			div_cnt <= div_cnt + 1'b1;
		end
	wire sck;
	assign sck = (div_cnt == 1'b1);
//------------------------------------------------------------------------------------------------------------------------------------------------------------


//--以div_cnt == 1'b1为节点产生32个shcp_edge_cnt 用于赋值------------------------------------------------------------------------------------------------------
	always@(posedge clk or negedge reset_n)begin
		if(!reset_n)
		shcp_edge_cnt <= 0;
		else if(sck)begin
			if(shcp_edge_cnt == 6'd32)
				shcp_edge_cnt <= 0;
			else 
				shcp_edge_cnt <= shcp_edge_cnt + 1'b1;
		end
		else
		shcp_edge_cnt <= shcp_edge_cnt;
	end
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
	
	
//按HC595芯片输出波形图----------------------------------------------------------------------------------------------------------------------------------------------------------
		
	always@(posedge clk or negedge reset_n)begin
		if(!reset_n)begin
			sh_cp <= 1'b0;
			st_cp <= 1'b0;
			ds <= 1'b0;
		end
		else begin
			case(shcp_edge_cnt)
				0:begin sh_cp <= 1'd0; ds <= r_data[15];end
				1:begin sh_cp <= 1'd1;st_cp <= 1'd0;end
				2:begin sh_cp <= 1'd0; st_cp <= 1'd0;ds <= r_data[14];end
				3:sh_cp <= 1'd1;
				4:begin sh_cp <= 1'd0; st_cp <= 1'd0;ds <= r_data[13];end
				5:sh_cp <= 1'd1;
				6:begin sh_cp <= 1'd0; st_cp <= 1'd0;ds <= r_data[12];end
				7:sh_cp <= 1'd1;
				8:begin sh_cp <= 1'd0; st_cp <= 1'd0;ds <= r_data[11];end
				9:sh_cp <= 1'd1;
				10:begin sh_cp <= 1'd0; st_cp <= 1'd0;ds <= r_data[10];end
				11:sh_cp <= 1'd1;
				12:begin sh_cp <= 1'd0; st_cp <= 1'd0;ds <= r_data[9];end
				13:sh_cp <= 1'd1;
				14:begin sh_cp <= 1'd0; st_cp <= 1'd0;ds <= r_data[8];end
				15:sh_cp <= 1'd1;
				16:begin sh_cp <= 1'd0; st_cp <= 1'd0;ds <= r_data[7];end
				17:sh_cp <= 1'd1;
				18:begin sh_cp <= 1'd0; st_cp <= 1'd0;ds <= r_data[6];end
				19:sh_cp <= 1'd1;
				20:begin sh_cp <= 1'd0; st_cp <= 1'd0;ds <= r_data[5];end
				21:sh_cp <= 1'd1;
				22:begin sh_cp <= 1'd0; st_cp <= 1'd0;ds <= r_data[4];end
				23:sh_cp <= 1'd1;
				24:begin sh_cp <= 1'd0; st_cp <= 1'd0;ds <= r_data[3];end
				25:sh_cp <= 1'd1;
				26:begin sh_cp <= 1'd0; st_cp <= 1'd0;ds <= r_data[2];end
				27:sh_cp <= 1'd1;
				28:begin sh_cp <= 1'd0; st_cp <= 1'd0;ds <= r_data[1];end
				29:sh_cp <= 1'd1;
				30:begin sh_cp <= 1'd0; st_cp <= 1'd0;ds <= r_data[0];end
				31:sh_cp <= 1'd1;
				32:st_cp <= 1'd1;
				default : begin sh_cp <= 1'b0;st_cp <= 1'b0;ds <= 1'b0;end
			endcase	
		  end
		end
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
endmodule

6、 TB文件展示

`timescale 1ns / 1ps
module time_clock_tb( );
    reg clk;
	reg reset_n;
	wire sh_cp;
	wire st_cp;
	reg rx;
	wire  ds;
	digital_clock   digital_clock(
       .clk(clk ),
       .reset_n(reset_n),
       .sh_cp(sh_cp),
       .st_cp(st_cp),
       .rx(rx),
       .ds(ds)    
    );
	
	
	
	
    initial clk = 1;
    always#10 clk = ~clk;
    initial begin
        reset_n = 0;
        rx = 1;
        #201;
        reset_n= 1;
        #200;
        uart_tx_byte(8'h01); //调用task
        #90000;
        uart_tx_byte(8'h30); 
        #90000;
        uart_tx_byte(8'h59); 
        #90000;
        $stop;
    end
    
    task uart_tx_byte; //创建任务
        input [7:0]tx_data;
        begin
            rx= 1;
            #20;
            rx = 0;
            #8680; //一位的发送时间
            rx = tx_data[0];
            #8680; 
           rx = tx_data[1];
            #8680; 
           rx = tx_data[2];
            #8680; 
            rx = tx_data[3];
            #8680; 
            rx = tx_data[4];
            #8680; 
            rx = tx_data[5];
            #8680; 
            rx = tx_data[6];
            #8680; 
            rx = tx_data[7];
            #8680; 
            rx = 1;
            #8680; 
            
        end  
    endtask

endmodule

【附件:】链接:https://pan.baidu.com/s/1gK-0TX7tbdopJGFE4XbNAA?pwd=vgax
提取码:vgax