FPGA serial communication (supplement)-general module

table of Contents

FPGA serial communication

1. Serial port receiver module

2. Serial port sending module


FPGA serial communication

The serial communication module written above has no universality. Here is a universal serial module, but if you need serial communication in the future, you can directly use it.

1. Serial port receiver module

Table: Serial port receiving module interface list
Signal name I / O Digits Function description
clk I 1 System clock 50MHz
rst_n I 1 System reset
rs232_tx I 1 Serial data port
baud_set I 3 Baud rate selection signal
data_byte THE 8 Parallel data output
rx_done THE 1 Receive 1 byte data complete flag

The code is as follows: UART_Byte_Rx.v

//-------------------------------------------------------------------
//https://blog.csdn.net/qq_33231534 PHF的CSDN   
//File name:           UART_Byte_Rx.v
//Last modified Date:  2020/5/22
//Last Version:        
//Descriptions:        工业级别串口数据接收模块,防干扰。对每位数据内部采样16个点,
//                     对中间6位数据进行判定数据是1还是0
//-------------------------------------------------------------------

module UART_Byte_Rx(
    input                   clk         ,//系统时钟50MHz
    input                   rst_n       ,//系统复位
    input                   rs232_tx    ,//串口串行数据发送数据口
    input       [  2: 0]    baud_set    ,//波特率选择信号
    output  reg [  7: 0]    data_byte   ,//并行数据输出
    output  reg             rx_done      //接收1字节数据完成标志,rx_done可以作为输出有效信号使用
);

reg   [ 13: 0]         baud_c   ;//波特率对应计数次数(4800bps-10416),(9600bps-5208),(19200bps-2604),
                                 //(38400bps-1302),(57600bps-868),(115200bps-434)

reg                    rs232_tx_ff0     ;
reg                    rs232_tx_ff1     ;
reg                    rs232_tx_ff2     ;
wire                   tx_neg_flag      ;
reg                    add_flag         ;

reg   [ 13: 0]         cnt0             ;
reg   [  3: 0]         cnt1             ;
reg   [  9: 0]         cnt2             ;
reg   [  3: 0]         cnt3             ;
reg   [  2: 0]         cnt_0            ;
reg   [  2: 0]         cnt_1            ;

wire                   add_cnt0         ;
wire                   end_cnt0         ;
wire                   add_cnt1         ;
wire                   end_cnt1         ;
wire                   add_cnt2         ;
wire                   end_cnt2         ;
wire                   add_cnt3         ;
wire                   end_cnt3         ;

//查找表
always  @(posedge clk or negedge rst_n)begin
     if(rst_n==1'b0)begin
        baud_c <= 5208;
    end
    else begin
        case(baud_set)
            0:      baud_c = 14'd10416;
            1:      baud_c = 14'd5208 ;
            2:      baud_c = 14'd2604 ;
            3:      baud_c = 14'd1302 ;
            4:      baud_c = 14'd868  ;
            5:      baud_c = 14'd434  ;
            default:baud_c = 14'd5208 ;//默认9600bps
        endcase
    end   
end

//打两拍 防止亚稳态,同时scan negedge
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        rs232_tx_ff0 <= 1;
        rs232_tx_ff1 <= 1;
        rs232_tx_ff2 <= 1;
    end
    else begin
        rs232_tx_ff0 <= rs232_tx;
        rs232_tx_ff1 <= rs232_tx_ff0;
        rs232_tx_ff2 <= rs232_tx_ff1;
    end
end
//扫描下降沿
assign tx_neg_flag = rs232_tx_ff2 && !rs232_tx_ff1;

//计数标志信号
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        add_flag <= 0;
    end
    else if(tx_neg_flag) begin
        add_flag <= 1;
    end
    else if(rx_done)begin
    add_flag <= 0;
    end
end

//计数器,计数1bit数据长度
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt0 <= 0;
    end
    else if(add_cnt0)begin
        if(end_cnt0)
            cnt0 <= 0;
        else
            cnt0 <= cnt0 + 1'b1;
    end
end

assign add_cnt0 = add_flag;
assign end_cnt0 = add_cnt0 && cnt0==baud_c-1;

//计数器,计数8位接收数据长度
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        cnt1 <= 0;
    end
    else if(add_cnt1)begin
        if(end_cnt1)
            cnt1 <= 0;
        else
            cnt1 <= cnt1 + 1'b1;
    end
end

assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && cnt1== 8;

//比特内部采样点时钟计数
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt2 <= 0;
    end
    else if(add_cnt2)begin
        if(end_cnt2)
            cnt2 <= 0;
        else
            cnt2 <= cnt2 + 1'b1;
    end
end

assign add_cnt2 = add_flag;       
assign end_cnt2 = add_cnt2 && (cnt2== (baud_c/16)-1 || end_cnt0);   

//一个bit数据中16个采样点计数
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt3 <= 0;
    end
    else if(add_cnt3)begin
        if(end_cnt3)
            cnt3 <= 0;
        else
            cnt3 <= cnt3 + 1'b1;
    end
end

assign add_cnt3 = add_cnt2 && cnt2== (baud_c/16)-1;       
assign end_cnt3 = end_cnt0 || (end_cnt2 && cnt3==16-1);   

//比特内选取6个采样点是0或1计数
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        cnt_0 <= 0;
        cnt_1 <= 0;
    end
    else if(add_flag) begin
        if(cnt3>=6 && cnt3<=11)begin
            if(cnt2==baud_c/16/2 && rs232_tx_ff1==0)
                cnt_0 <= cnt_0 + 1'b1;
            else if(cnt2==baud_c/16/2 && rs232_tx_ff1==1)
                cnt_1 <= cnt_1 + 1'b1;
        end
        else if(end_cnt0)begin
            cnt_0 <= 0;
            cnt_1 <= 0;
        end

    end
    else begin
        cnt_0 <= 0;
        cnt_1 <= 0;
    end
end

//输出并行数据data_byte
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        data_byte <= 0;
    end
    else if(end_cnt0 && cnt1>0 && cnt1 <9) begin
        if(cnt_0 >= cnt_1)
            data_byte[cnt1-1] = 0;
        else if(cnt_0 < cnt_1)
            data_byte[cnt1-1] = 1;
    end
end

//输出接收完成标志信号
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        rx_done <= 0;
    end
    else if(end_cnt1) begin
        rx_done <= 1;
    end
    else begin
        rx_done <= 0;
    end
end

endmodule

The test code is as follows:
 

`timescale 1 ns/ 1 ns
module UART_Byte_Rx_tb();
// constants                                           
// test vector input registers
reg [2:0] baud_set;
reg clk;
reg rs232_rx;
reg rst_n;
// wires                                               
wire [7:0]  data_byte;
wire rx_done;

parameter clk_period = 20;

// assign statements (if any)                          
UART_Byte_Rx i1 (
// port map - connection between master ports and signals/registers   
	.baud_set(baud_set),
	.clk(clk),
	.data_byte(data_byte),
	.rs232_rx(rs232_rx),
	.rst_n(rst_n),
	.rx_done(rx_done)
);

initial clk = 0;
always #(clk_period/2) clk = ~clk;

initial begin
	#1;
	rst_n = 0;
    baud_set = 0;
	rs232_rx = 1;
	#(clk_period*5);
	rst_n = 1;
    baud_set = 3'd1;
	#(clk_period*3);
	
	repeat(1)begin
	//发送0000_1010
	rs232_rx = 0;
	#(clk_period*5208);
	rs232_rx = 0;
	#(clk_period*5208*4);
	rs232_rx = 1;
	#(clk_period*5208);
	rs232_rx = 0;
	#(clk_period*5208);
	rs232_rx = 1;
	#(clk_period*5208);
	rs232_rx = 0;
	#(clk_period*5208);
	rs232_rx = 1;
	#(clk_period*5208);
	
	//发送1000_0101
	rs232_rx = 0;
	#(clk_period*5208);
	rs232_rx = 1;
	#(clk_period*5208);
	rs232_rx = 0;
	#(clk_period*5208*4);
	rs232_rx = 1;
	#(clk_period*5208);
	rs232_rx = 0;
	#(clk_period*5208);
	rs232_rx = 1;
	#(clk_period*5208);
	rs232_rx = 1;
	#(clk_period*5208);
	
	end
	#(clk_period*50);

	$stop;
end
endmodule

 The simulation graphics are as follows:


2. Serial port sending module

Corresponding to the serial port receiving module here, 8-bit data is sent, plus a start bit and a stop bit. The module interface signal list is as follows:
 

Table: List of serial port sending module interface signals
Signal name I / O Digits Function description
clk I 1 System clock 50MHz
rst_n I 1 System reset
send_en I 1 Send enable
data_byte I 8 Data sent
baud_set I 3 Baud rate setting
rs232_tx THE 1 FPGA converts data into serial data and sends it out
tx_done THE 1 Send data complete flag
uart_state THE 1 Serial port sending status, 1 means busy, 0 means idle

The code is as follows: Uart_Byte_Tx.v

//-------------------------------------------------------------------
//https://blog.csdn.net/qq_33231534 phf的CSDN   
//File name:           Uart_Byte_Tx.v
//Last modified Date:  2020/5/22
//Last Version:        
//Descriptions:        串口发送模块,8位数据位、1位起始位和1位停止位、无校验位
//-------------------------------------------------------------------
module Uart_Byte_Tx(
    input                   clk         , //系统时钟
    input                   rst_n       , //系统复位
    input                   send_en     , //发送使能
    input   [ 7 : 0 ]       data_byte   , //发送的数据
    input   [ 2 : 0 ]       baud_set    , //波特率设置
    output  reg             rs232_tx    , //FPGA将数据转换成串行数据发出
    output  reg             tx_done     , //发送数据完毕标志
    output  reg             uart_state    //串口发送状态,1为忙,0为空闲
);


reg   [ 13: 0]         baud_c   ;//(4800bps-10416),(9600bps-5208),(19200bps-2604),
                                 //(38400bps-1302),(57600bps-868),(115200bps-434)
wire  [  9: 0]         data_out      ;
reg   [ 15: 0]         cnt0          ; //1bit数据长度计数
reg   [  3: 0]         cnt1          ; //发送一字节数据对每个字节计数
wire                   add_cnt0      ; //计数器cnt0加一条件
wire                   add_cnt1      ; //计数器cnt1加一条件
wire                   end_cnt0      ; //计数器cnt0结束条件
wire                   end_cnt1      ; //计数器cnt1结束条件
reg   [  7: 0]         data_byte_ff  ; //发送使能时将发送的数据寄存下来

//波特率查找表
always  @(posedge clk or negedge rst_n)begin
     if(rst_n==1'b0)begin
        baud_c <= 5208;
    end
    else begin
        case(baud_set)
            0:      baud_c = 14'd10416;
            1:      baud_c = 14'd5208 ;
            2:      baud_c = 14'd2604 ;
            3:      baud_c = 14'd1302 ;
            4:      baud_c = 14'd868  ;
            5:      baud_c = 14'd434  ;
            default:baud_c = 14'd5208 ;//默认9600bps
        endcase
    end
    
end

//串口状态标志,0为空闲,1为忙
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        uart_state <= 0;
    end
    else if(send_en) begin
        uart_state <= 1;
    end
    else if(end_cnt1)begin
        uart_state <= 0;
    end
    else begin
        uart_state <= uart_state;
    end
end

//1bit数据长度计数
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt0 <= 0;
    end
    else if(add_cnt0)begin
        if(end_cnt0)
            cnt0 <= 0;
        else
            cnt0 <= cnt0 + 1'b1;
    end
end

assign add_cnt0 = uart_state==1;
assign end_cnt0 = add_cnt0 && cnt0== baud_c-1;

//发送一字节数据对每个字节计数
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        cnt1 <= 0;
    end
    else if(add_cnt1)begin
        if(end_cnt1)
            cnt1 <= 0;
        else
            cnt1 <= cnt1 + 1'b1;
    end
end

assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && cnt1== 10-1;

//串口发送结束标志
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        tx_done <= 0;
    end
    else if(end_cnt1) begin
        tx_done <= 1;
    end
    else begin
        tx_done <= 0;
    end
end

//发送使能时将发送的数据寄存下来
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        data_byte_ff <= 0;
    end
    else if(send_en) begin
        data_byte_ff <= data_byte;
    end
end

//发送串行数据到串口
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        rs232_tx <= 1;
    end
    else if(uart_state && cnt0==0) begin
        rs232_tx <= data_out[cnt1];
    end
end
assign data_out = {1'b1,data_byte_ff,1'b0};

endmodule

The test code is as follows:

`timescale 1 ns/ 1 ns
module Uart_Byte_Tx_tb();
// constants                                           
// test vector input registers
reg [2:0] baud_set;
reg clk;
reg [7:0] data_byte;
reg rst_n;
reg send_en;
// wires                                               
wire rs232_tx;
wire tx_done;
wire uart_state;

parameter clk_period = 20;

// assign statements (if any)                          
Uart_Byte_Tx i1 (
// port map - connection between master ports and signals/registers   
	.baud_set(baud_set),
	.clk(clk),
	.data_byte(data_byte),
	.rs232_tx(rs232_tx),
	.rst_n(rst_n),
	.send_en(send_en),
	.tx_done(tx_done),
	.uart_state(uart_state)
);

initial clk = 1;
always #(clk_period/2) clk = ~clk;

initial begin
	rst_n = 0;
	baud_set = 3'd1;
	send_en = 0;
	data_byte = 0;
	#(clk_period*5);
	rst_n = 1;
	#(clk_period*5);
	
	send_en = 1;
	data_byte = 8'b0001_0100;
	#(clk_period);	
	send_en = 0;
	data_byte = 0;
	#(clk_period*5208*15);
	$stop;
end
                                              
endmodule

 The simulation waveform is as follows:

It meets the design requirements after simulation verification.

 

Guess you like

Origin blog.csdn.net/qq_33231534/article/details/106600942