FPGA串口通信----uart(一)

 一、Fpga--PLL锁相环的使用

通过PLL锁相环IP的使用,可以输入时钟进行分频,倍频,也可以产生多路时钟输出信号。

外部提供晶振的原因:①Fpga电路大多数需要时序分析

                                 ②晶振在Fpga内部占空间大,所以放置于外部PCB板上

Uart异步传输速率不能很快的原因:考虑到工程实际问题,假如时钟周期大(传输速率慢的话),采取到数据电平变化的边沿时刻的概率会小很多,这样数据也很准确,通常uart适用于2M.

二、Uart 发送

2.1 uart_byte_tx(字节发送)

2.1.1模块设计框图 

2.1.2 Verilog设计及仿真 

`timescale 1ns / 1ps
//
// Description:  UART 字节发送 
//
module uart_tx_byte(
    Clk			,
	Rst_n		,
  
	data_byte	,
	send_en		,   
	Baud_Set	,  
	
	uart_tx		,  
	Tx_Done		,   
	uart_state 
);

	input 		Clk 		;    //模块全局时钟输入,50M
	input 		Rst_n		;    //复位信号输入,低有效
	input [7:0]	data_byte	;    //待传输8bit数据
	input 		send_en		;    //发送使能
	input [2:0]	Baud_Set	;    //波特率设置
	
	output reg 	uart_tx		;  //串口输出信号
	output reg 	Tx_Done		;  //1byte数据发送完成标志
	output reg 	uart_state	;  //发送数据状态

	localparam START_BIT = 1'b0;
	localparam STOP_BIT  = 1'b1; 
	
	reg 		bps_clk		 ; //波特率时钟	
	reg [15:0]	div_cnt		 ; //分频计数器	
	reg [15:0]	bps_DR		 ; //分频计数最大值	
	reg [3:0]	bps_cnt		 ; //波特率时钟计数器	
	reg [7:0]	data_byte_reg; //data_byte寄存后数据

//******************** UART发送状态标志 ****************** \\
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			uart_state <= 1'b0;
		else if(send_en)
			uart_state <= 1'b1;
		else if(bps_cnt >= 4'd11)
			uart_state <= 1'b0;
		else
			uart_state <= uart_state;
	end
//******************** 输入数据锁存 ****************** \\
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			data_byte_reg <= 8'd0;
		else if(send_en)
			data_byte_reg <= data_byte;
		else
			data_byte_reg <= data_byte_reg;
	end

//******************** 波特率时钟的生成模块 ****************** \\
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			bps_DR <= 16'd5207;
		else begin
			case(Baud_Set)
				0:bps_DR <= 16'd5207;  //波特率为9600      1_000_000_000/9600/20
				1:bps_DR <= 16'd2603;  //波特率为19200		
				2:bps_DR <= 16'd1301;  //波特率为38400
				3:bps_DR <= 16'd867;   //波特率为57600
				4:bps_DR <= 16'd433;   //波特率为115200
				default:bps_DR <= 16'd5207;			
			endcase
		end	
	end
	//counter
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			div_cnt <= 16'd0;
		else if(uart_state)begin
			if(div_cnt >= bps_DR)
				div_cnt <= 16'd0;
			else
				div_cnt <= div_cnt + 1'b1;
		end
		else
			div_cnt <= 16'd0;
	end
	// bps_clk gen
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			bps_clk <= 1'b0;
		else if(div_cnt == 16'd1)     //由于UART发送是从bps_cnt =1 发起始位,而不是从0,因此为了尽快进入发送模块,我们计数器不计满+1,等于一时+1
			bps_clk <= 1'b1;
		else
			bps_clk <= 1'b0;
	end
//******************** Uart的数据发送模块 ****************** \\
	//bps counter
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)	
			bps_cnt <= 4'd0;
		else if(bps_cnt >= 4'd11)
			bps_cnt <= 4'd0;
		else if(bps_clk)
			bps_cnt <= bps_cnt + 1'b1;
		else
			bps_cnt <= bps_cnt;
	end	
	//数据发送
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			uart_tx <= 1'b1;
		else begin
			case(bps_cnt)
				0:uart_tx <= 1'b1;
				1:uart_tx <= START_BIT;        // 从1开始,避免了起始位0与空闲态1冲突
				2:uart_tx <= data_byte_reg[0];
				3:uart_tx <= data_byte_reg[1];
				4:uart_tx <= data_byte_reg[2];
				5:uart_tx <= data_byte_reg[3];
				6:uart_tx <= data_byte_reg[4];
				7:uart_tx <= data_byte_reg[5];
				8:uart_tx <= data_byte_reg[6];
				9:uart_tx <= data_byte_reg[7];
				10:uart_tx <= STOP_BIT;
				default:uart_tx <= 1'b1;
			endcase
		end
	end	
	//数据发送完成标志 
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			Tx_Done <= 1'b0;
		else if(bps_cnt >= 4'd11)
			Tx_Done <= 1'b1;
		else
			Tx_Done <= 1'b0;
	end	
endmodule

 由上图可知,uart发送先发低位再发高位(aa 10101010)

三、Uart接收 

3.1 uart_byte_rx (字节接收)

 3.1.1模块设计框图 

3.1.2 Verilog设计及仿真  

我们接收数据(1起始位+8数据位+1停止位)总共接受10个数据,为避免电磁等干扰,对每一个数据进行16等分,对数据中间进行采样7次,然后比较得出真值,因此总共需160个小数据段,因此bps_clk需要161个这样才能进行保证数据的完整性。 

verilog 文件
`timescale 1ns / 1ps
//
// Dependencies: UART字节接收
//
module uart_byte_rx(
    Clk     ,
    Reset_n ,
    Baud_Set,
    uart_rx ,
    Data    ,
    Rx_Done
);
   input            Clk     ;
   input            Reset_n ;
   input     [2:0]  Baud_Set;
   input            uart_rx ;
   output reg[7:0]  Data    ; 
   output reg       Rx_Done ;

	reg [1:0]   uart_rx_r   ;
    reg         RX_EN       ;
    reg [8:0]   Bps_DR      ; 
	reg [8:0]   div_cnt     ;
    reg         bps_clk_16x ; 
    reg [7:0]   bps_cnt     ;

//reg width name number/depth
    reg [2:0]   r_data[7:0];
    reg [2:0]   sta_bit;
    reg [2:0]   sto_bit;

    wire pedge_uart_rx;
//    assign pedge_uart_rx = ((uart_rx_r[1] == 0) && (uart_rx_r[0] == 1));
    wire nedge_uart_rx;
//    assign nedge_uart_rx = ((uart_rx_r[1] == 1) && (uart_rx_r[0] == 0)); 
//*******************  边沿检测(下降沿/上升沿)模块 ********************* \\
   always@(posedge Clk,negedge Reset_n)begin
        if(!Reset_n)
            uart_rx_r <= 'd0;
        else begin
            uart_rx_r[0] <= uart_rx;
            uart_rx_r[1] <= uart_rx_r[0] ;
        end
   end

    assign pedge_uart_rx = (uart_rx_r == 2'b01);
    assign nedge_uart_rx = (uart_rx_r == 2'b10);  
//*******************  Uart数据接收状态标志 ********************* \\    
    always@(posedge Clk or negedge Reset_n)begin
        if(!Reset_n) 
            RX_EN <= 0;
        else if(nedge_uart_rx)
            RX_EN <= 1;
        else if((bps_cnt == 'd160 && bps_clk_16x) || (sta_bit >= 4))// 起始位异常不产生RX_EN
            RX_EN <= 0;
        else
            RX_EN <= RX_EN;
    end   
//*******************  采样时钟(bps_clk_16x)产生模块 ********************* \\
    always@(posedge Clk or negedge Reset_n)begin
        if(!Reset_n)
            Bps_DR <= 16'd324;
        else begin
            case(Baud_Set)
                0:Bps_DR <= 16'd324; //波特率为9600      1_000_000_000/9600/20 /16
                1:Bps_DR <= 16'd162; //波特率为19200		
                2:Bps_DR <= 16'd80 ; //波特率为38400
                3:Bps_DR <= 16'd53 ; //波特率为57600
                4:Bps_DR <= 16'd26 ; //波特率为115200
                default:Bps_DR <= 16'd324; 
            endcase
        end
    end
    //counter
    always@(posedge Clk or negedge Reset_n)begin
        if(!Reset_n)    
            div_cnt <= 0;
        else if(RX_EN)begin
            if(div_cnt >= Bps_DR)
                div_cnt <= 0;
            else
                div_cnt <= div_cnt + 1'b1;
        end
        else
            div_cnt <= 0;
    end
    // bps_clk_16x gen
    always@(posedge Clk or negedge Reset_n)begin
        if(!Reset_n)
            bps_clk_16x <= 1'b0;
        else if(div_cnt == 16'd1)
            bps_clk_16x <= 1'b1;
        else
            bps_clk_16x <= 1'b0;
    end
//******************** Uart的数据接收模块 ****************** \\
    always@(posedge Clk or negedge Reset_n)begin
        if(!Reset_n) 
            bps_cnt <= 0;
        else if(RX_EN)begin
            if(bps_clk_16x)begin
                if(bps_cnt >= 160)
                    bps_cnt <= 0;
                else
                    bps_cnt <= bps_cnt + 1'b1;
            end
            else
                bps_cnt <= bps_cnt;
        end
        else
            bps_cnt <= 0;
    end
    //数据接收
    always@(posedge Clk or negedge Reset_n)begin
        if(!Reset_n) begin
            sta_bit <= 0;
            sto_bit <= 0;
            r_data[0] <= 0;
            r_data[1] <= 0;
            r_data[2] <= 0;
            r_data[3] <= 0;
            r_data[4] <= 0;
            r_data[5] <= 0;
            r_data[6] <= 0;
            r_data[7] <= 0;
        end
        else if(bps_clk_16x)begin
            case(bps_cnt)
                0:begin
                    sta_bit <= 0;
                    sto_bit <= 0;
                    r_data[0] <= 0;
                    r_data[1] <= 0;
                    r_data[2] <= 0;
                    r_data[3] <= 0;
                    r_data[4] <= 0;
                    r_data[5] <= 0;
                    r_data[6] <= 0;
                    r_data[7] <= 0;
                end
                5,6,7,8,9,10,11:sta_bit <= sta_bit + uart_rx;
                21,22,23,24,25,26,27: r_data[0] <= r_data[0] + uart_rx;
                37,38,39,40,41,42,43: r_data[1] <= r_data[1] + uart_rx;
                53,54,55,56,57,58,59: r_data[2] <= r_data[2] + uart_rx;
                69,70,71,72,73,74,75: r_data[3] <= r_data[3] + uart_rx;
                85,86,87,88,89,90,91: r_data[4] <= r_data[4] + uart_rx;
                101,102,103,104,105,106,107: r_data[5] <= r_data[5] + uart_rx;
                117,118,119,120,121,122,123: r_data[6] <= r_data[6] + uart_rx;
                133,134,135,136,137,138,139: r_data[7] <= r_data[7] + uart_rx;
                149,150,151,152,153,154,155: sto_bit <= sto_bit + uart_rx;
                default:;
            endcase
        end
    end
    //数据判断    
    always@(posedge Clk or negedge Reset_n)begin
        if(!Reset_n) 
            Data <= 0;        
        else if(bps_clk_16x && (bps_cnt == 160))begin
            Data[0] <= (r_data[0] >= 4)?1'b1:1'b0;
            Data[1] <= (r_data[1] >= 4)?1'b1:1'b0;
            Data[2] <= (r_data[2] >= 4)?1'b1:1'b0;
            Data[3] <= (r_data[3] >= 4)?1'b1:1'b0;
            Data[4] <= (r_data[4] >= 4)?1'b1:1'b0;
            Data[5] <= (r_data[5] >= 4)?1'b1:1'b0;
            Data[6] <= (r_data[6] >= 4)?1'b1:1'b0;
            Data[7] <= (r_data[7] >= 4)?1'b1:1'b0;
        end 
    end
    
//    always@(posedge Clk or negedge Reset_n)
//    if(!Reset_n) 
//        Data <= 0;        
//    else if(bps_clk_16x && (bps_cnt == 159))begin
//        Data[0] <= r_data[0][2];
//        Data[1] <= r_data[1][2];
//        Data[2] <= r_data[2][2];
//        Data[3] <= r_data[3][2];
//        Data[4] <= r_data[4][2];
//        Data[5] <= r_data[5][2];
//        Data[6] <= r_data[6][2];
//        Data[7] <= r_data[7][2];
//    end 
//******************** Uart接受完成标志 ****************** \\
    always@(posedge Clk or negedge Reset_n)begin
        if(!Reset_n) 
            Rx_Done <= 0;
        else if(bps_cnt == 'd160 && bps_clk_16x)
            Rx_Done <= 1;
        else
            Rx_Done <= 0; 
    end
endmodule

这边注意RX-done 以及RX_EN拉低的条件 。

TB文件
`timescale 1ns / 1ps
module uart_byte_rx_tb();

   reg           Clk     ;
   reg           Reset_n ;
   reg    [2:0]  Baud_Set;
   reg           uart_rx ;
   wire   [7:0]  Data    ; 
   wire          Rx_Done ;
uart_byte_rx uart_byte_rx_u0(
    .Clk        (Clk     ),
    .Reset_n    (Reset_n ),
    .Baud_Set   (  'd4   ),
    .uart_rx    (uart_rx ),
    .Data       (Data    ),
    .Rx_Done    (Rx_Done )
    );
initial  Clk = 0;
always #10 Clk = ~Clk;

initial begin
    Reset_n  = 0;
    #200;
    Reset_n = 1;
    #20;
    uart_tx_byte(8'haa);
    #90000//@(posedge Rx_Done);
    #5000;
    uart_tx_byte(8'h55);
    #90000;
    #5000;
    $stop;
end

task uart_tx_byte;
    input [7:0]tx_data;
    begin
        uart_rx = 1;
        #20;
        uart_rx = 0;
        #8680;
        uart_rx = tx_data[0];
        #8680;
        uart_rx = tx_data[1];
        #8680;
        uart_rx = tx_data[2];
        #8680;
        uart_rx = tx_data[3];
        #8680;
        uart_rx = tx_data[4];
        #8680;
        uart_rx = tx_data[5];
        #8680;
        uart_rx = tx_data[6];
        #8680;
        uart_rx = tx_data[7];
        #8680;
        uart_rx = 1;
        #8680;
    end
endtask
endmodule

 四、Uart串口控制LED灯闪烁

4.1 设计思路分析

4.2 verilog设计及仿真

4.2.1  .v文件

以下分别为cmd,LED以及top顶层代码:

`timescale 1ns / 1ps
//
// Description: 控制命令转换模块:将接受的8字节数据进行缓存,通过判断输出周期/控制的控制字给予LED模块
//
module uart_cmd(
    Clk     ,
    Reset_n ,
    rx_data ,
    rx_done ,
    ctrl    ,
    time_set,
);

input              Clk     ;
input              Reset_n ;
input      [7:0]   rx_data ;
input              rx_done ;
output reg [7:0]   ctrl    ;
output reg [31:0]  time_set;

reg [7:0] data_str [7:0];
reg       r_rx_done;
//***************** 字节缓存模块 *****************\\
always @(posedge Clk or negedge Reset_n) begin
    if(!Reset_n)begin
        data_str[7] <= 'd0;
        data_str[6] <= 'd0;
        data_str[5] <= 'd0;
        data_str[4] <= 'd0;
        data_str[3] <= 'd0;
        data_str[2] <= 'd0;
        data_str[1] <= 'd0;
        data_str[0] <= 'd0;
    end
    else if(rx_done) begin
        data_str[7] <= rx_data    ;
        data_str[6] <= data_str[7];
        data_str[5] <= data_str[6];
        data_str[4] <= data_str[5];
        data_str[3] <= data_str[4];
        data_str[2] <= data_str[3];
        data_str[1] <= data_str[2];
        data_str[0] <= data_str[1];
    end
    else begin
        data_str[7] <= data_str[7];
        data_str[6] <= data_str[6];
        data_str[5] <= data_str[5];
        data_str[4] <= data_str[4];
        data_str[3] <= data_str[3];
        data_str[2] <= data_str[2];
        data_str[1] <= data_str[1];
        data_str[0] <= data_str[0];
    end
end
//***************** 帧结构判断输出周期/控制 控制字 *****************\\
always @(posedge Clk or negedge Reset_n) begin
    if(!Reset_n)
       r_rx_done <= 'd0;
    else
       r_rx_done <= rx_done;
end

always @(posedge Clk or negedge Reset_n) begin
    if(!Reset_n)begin
        ctrl     <= 'd0;
        time_set <= 'd0;
    end
    else if(r_rx_done)begin
        if((data_str[0] == 8'h55)&&(data_str[1]==8'hA5)&&(data_str[7]==8'hF0))begin
             time_set[31:24] <= data_str[2];
             time_set[23:16] <= data_str[3];
             time_set[15: 8] <= data_str[4];
             time_set[ 7: 0] <= data_str[5]; 
             ctrl            <= data_str[6];     
        end
        else begin
            time_set <= time_set;
            ctrl     <= ctrl;
        end
    end
    else begin
        time_set <= time_set;
        ctrl     <= ctrl    ;
    end       
end
endmodule
`timescale 1ns / 1ps
//
// Description: led亮灭闪烁
//
module led_flash(
    Clk     ,
    Reset_n ,
    Time    ,
    ctrl    ,
    led     
);
input              Clk     ;
input              Reset_n ;
input      [7:0]   ctrl    ;
input      [31:0]  Time    ;
output reg         led     ;

reg[31:0]   time_cnt;
always @(posedge Clk or negedge Reset_n) begin
    if(!Reset_n)
        time_cnt <= 'd0;
    else if(time_cnt >= Time -1)
        time_cnt <= 'd0;
    else
        time_cnt <= time_cnt + 1;
end

reg[2:0]  led_cnt;
always @(posedge Clk or negedge Reset_n) begin
    if(!Reset_n)
        led_cnt <= 'd0;
    else if(time_cnt >= Time -1)begin
        if(led_cnt >= 7)
            led_cnt <= 'd0;
        else
            led_cnt <= led_cnt + 1;
    end
    else
        led_cnt <= led_cnt;
end

always @(posedge Clk or negedge Reset_n) begin
    if(!Reset_n)
        led <= 'd0;
    else begin
        case(led_cnt)
            0:led <= ctrl[0];
            1:led <= ctrl[1];
            2:led <= ctrl[2];
            3:led <= ctrl[3];
            4:led <= ctrl[4];
            5:led <= ctrl[5];
            6:led <= ctrl[6];
            7:led <= ctrl[7];
            default:led <= 'd0;
        endcase
    end
end
endmodule
`timescale 1ns / 1ps
module uart_led_top(
    Clk     ,
    Reset_n ,
    uart_rx ,
    led 
);
input        Clk     ;
input        Reset_n ;
input        uart_rx ;
output       led     ;

wire [7:0]   Data    ;
wire         Rx_Done ;
wire [7:0]   ctrl    ;
wire [31:0]  time_set;

uart_byte_rx uart_byte_rx_u0(
    .Clk      (Clk      ),
    .Reset_n  (Reset_n  ),
    .Baud_Set ('d4      ),
    .uart_rx  (uart_rx  ),
    .Data     (Data     ),
    .Rx_Done  (Rx_Done  )
);
uart_cmd uart_cmd_u0(
    .Clk      (Clk      ),
    .Reset_n  (Reset_n  ),
    .rx_data  (Data     ),
    .rx_done  (Rx_Done  ),
    .ctrl     (ctrl     ),
    .time_set (time_set )
);
led_flash led_flash_u0(
    .Clk      (Clk      ),
    .Reset_n  (Reset_n  ),
    .Time     (time_set ),
    .ctrl     (ctrl     ),
    .led      (led      )
);
endmodule

 

4.2.2 tb文件及仿真 

 

`timescale 1ns / 1ps
module uart_led_tb();
reg         Clk        ;
reg         Reset_n    ;
wire        Led        ;
reg         uart_rx    ;
wire [31:0] delay_time ;

uart_led_top uart_led_top_u0(
    .Clk      (Clk      ),
    .Reset_n  (Reset_n  ),
    .uart_rx  (uart_rx  ),
    .led      (Led      )
); 
parameter Baud_Set = 3'd4;
assign delay_time = (Baud_Set == 3'd0) ? 20'd104166:
                    (Baud_Set == 3'd1) ? 20'd52083:
                    (Baud_Set == 3'd2) ? 20'd26041:
                    (Baud_Set == 3'd3) ? 20'd17361:
                                         20'd8680;
initial Clk = 1;
always#10 Clk = ~Clk;

initial begin
    Reset_n = 0;
    uart_rx = 1;
    #201;
    Reset_n = 1;
    #200; 
    uart_tx_byte(8'h55);
    #(delay_time*10);
    uart_tx_byte(8'ha5);
    #(delay_time*10);
    uart_tx_byte(8'h55);
    #(delay_time*10);
    uart_tx_byte(8'ha5);
    #(delay_time*10);
    uart_tx_byte(8'h00);
    #(delay_time*10);
    uart_tx_byte(8'h00);
    #(delay_time*10);
    uart_tx_byte(8'hc3);
    #(delay_time*10);
    uart_tx_byte(8'h50);
    #(delay_time*10); 
    uart_tx_byte(8'hAA);
    #(delay_time*10); 
    uart_tx_byte(8'hf0);
    #(delay_time*10); 
    uart_tx_byte(8'h55);
    #(delay_time*10);
    uart_tx_byte(8'ha5);
    #(delay_time*10);
    uart_tx_byte(8'h9a);
    #(delay_time*10);
    uart_tx_byte(8'h78);
    #(delay_time*10);
    uart_tx_byte(8'h56);
    #(delay_time*10);
    uart_tx_byte(8'h34);
    #(delay_time*10); 
    uart_tx_byte(8'h12);
    #(delay_time*10); 
    uart_tx_byte(8'hf1);
    #(delay_time*10);
    #15000000; 
    $stop;
end

task uart_tx_byte;
    input [7:0]tx_data;
    begin
        uart_rx = 1;
        #20;
        uart_rx = 0;
        #delay_time;
        uart_rx = tx_data[0];
        #delay_time;
        uart_rx = tx_data[1];
        #delay_time;
        uart_rx = tx_data[2];
        #delay_time;
        uart_rx = tx_data[3];
        #delay_time;
        uart_rx = tx_data[4];
        #delay_time;
        uart_rx = tx_data[5];
        #delay_time;
        uart_rx = tx_data[6];
        #delay_time;
        uart_rx = tx_data[7];
        #delay_time;
        uart_rx = 1;
        #delay_time; 
    end
endtask

endmodule

 关于:rx_done

r_rx_done:这里由于最高位的缓存数据与输入数据间存在一个时钟周期的延迟,因此需要对
rx_done 信号打一拍后再作为条件去判断缓存中的数据。

 4.3 板级试验

 指令多发,有效指令为 55 A5 02 FA F0 80 F0 F0。因此 LED 按照亮亮亮亮灭灭灭(8’b111100000xF0)的规律,每 1s 变化一次,每 8 次变化为一组,不断循环变化。 与操作对应的板级现象吻合。亮四秒灭四秒,可以正确显示操作Led灯。

 

 

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值