FPGA 串口通信(补)——通用模块

目录

FPGA串口通信

1. 串口接收模块

2.  串口发送模块


FPGA串口通信

前面写的串口通信模块,没有通用性,这里写一个可以通用的串口模块,但凡以后需要串口通信的,就可以直接拿过来用。

1. 串口接收模块

表:串口接收模块接口列表
信号名称 I/O 位数 功能描述
clk I 1 系统时钟50MHz
rst_n I 1 系统复位
rs232_tx I 1 串口串行数据发送数据口
baud_set I 3 波特率选择信号
data_byte O 8 并行数据输出
rx_done O 1 接收1字节数据完成标志

代码如下: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

测试代码如下:
 

`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

 其仿真图形如下:


2.  串口发送模块

这里和串口接收模块对应,发送的是8位数据,外加1位起始位和1位停止位。该模块接口信号列表如下:
 

表:串口发送模块接口信号列表
信号名称 I/O 位数 功能描述
clk I 1 系统时钟50MHz
rst_n I 1 系统复位
send_en I 1 发送使能
data_byte I 8 发送的数据
baud_set I 3 波特率设置
rs232_tx O 1 FPGA将数据转换成串行数据发出
tx_done O 1 发送数据完毕标志
uart_state O 1 串口发送状态,1为忙,0为空闲

代码如下: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

测试代码如下:

`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

 仿真波形如下:

仿真验证后符合设计要求。

猜你喜欢

转载自blog.csdn.net/qq_33231534/article/details/106600942