uart串口通信 fpga实现

原理

将接收的串行数据转换为并行数据,缓存起来,需要的时候将并行数据转化为串行数据输出。
大概就是三个部分:接收、缓存、输出
uart数据格式
起始位 0
数据位 通常6/7/8位
停止位 1

实现

三个模块
接收模块
控制模块(缓存模块)
发送模块

接收模块

rx_uart.v

module rx_uart(
    input           clk          ,
    input           rst_n        ,
    input           rxd          ,
    output [7:0]    dout         ,
    output          dout_vld     
);
//参数定义
    //波特率 9600B 即1s传输9600个字符 这里因为是两相调制 即波特率 = 比特率
    localparam BAUD = 9600;
    //时钟频率 即1s重复5000万次
    localparam CYCLE = 50_000_000;
    //传输1bit时间 1/9600*1000_000_000 ns
    //一个周期时间 1/50_000_000*1000_000_000 ns
    //所以 传输1bit所需周期数 50_000_000/9600 = 5208
    parameter TIME_1BIT = 5208;

//          默认数据格式
//          起始位+数据位(8位)+停止位

//信号定义
    reg [12:0]          cnt_baud    ;//传输1bit所需时间 波特率计数器
    wire                add_cnt_baud;
    wire                end_cnt_baud;

    reg [3:0]           cnt_bit     ;//bit数计数器 计数接收的bit数
    wire                add_cnt_bit ;
    wire                end_cnt_bit ;

    reg                 rx_flag     ;//接收开始标志

    reg                 rxd_r0      ;//输入同步
    reg                 rxd_r1      ;//输入打拍
    wire                rx_nedge    ;//检测下降沿

    reg [9:0]           rx_data     ;//接收数据寄存
    reg                 rx_data_vld ;//接收数据有效寄存

//cnt_baud 
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt_baud <= 0;
        end
        else if(!rx_flag)begin//当采集信号拉低 这里特指异常拉低 直接置零
            cnt_baud <= 0;
        end
        else if(add_cnt_baud)begin
            if(end_cnt_baud)begin
                cnt_baud <= 0;
            end
            else begin
                cnt_baud <= cnt_baud + 1;
            end
        end
    end
    assign add_cnt_baud = rx_flag;//采集信号拉高开始计时
    assign end_cnt_baud = add_cnt_baud && (cnt_baud == TIME_1BIT - 1);//1bit时间到 即重新计时

//cnt_bit
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt_bit <= 0;
        end
        else if(!rx_flag)begin//当采集信号拉低 这里特指异常拉低 直接置零
            cnt_bit <= 0;
        end
        else if(add_cnt_bit)begin
            if(end_cnt_bit)begin
                cnt_bit <= 0;
            end
            else begin
                cnt_bit <= cnt_bit + 1;
            end
        end
    end
    assign add_cnt_bit = end_cnt_baud;//1bit时间到 表示接收到1bit
    assign end_cnt_bit = add_cnt_bit && (cnt_bit == 10 - 1);//表示1组数据接收完成

//rx_data 接收数据寄存
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            rx_data <= 10'h3ff;//默认全1
        end
        else if(cnt_baud == (TIME_1BIT >> 2))begin//移位寄存
            //rx_data <= {rx_data[8:0],rxd};
            rx_data <= {rxd,rx_data[9:1]};//右移寄存 1********0  均正序
        end
    end

//rx_data_vld 接收数据有效标志寄存
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            rx_data_vld <= 1'b0;
        end
        else if(end_cnt_bit)begin//当接收完一组数据,有效信号拉高一个周期
            rx_data_vld <= 1'b1;
        end
        else begin
            rx_data_vld <= 1'b0;
        end
    end

//rx_flag
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            rx_flag <= 1'b0;
        end
        else if(rx_nedge)begin//检测到rxd下降沿拉高采集标志
            rx_flag <= 1'b1;
        end
        else if((cnt_baud == TIME_1BIT >> 2) && (cnt_bit == 0) && rxd_r0 == 1'b1)begin//如果接收的第一个bit为1,表示数据总线异常拉低 拉低采集标志
            rx_flag <= 1'b0;
        end
        else if(end_cnt_bit )begin//接收数据完成拉低采集标志
            rx_flag <= 1'b0;
        end
    end

//rxd_r
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            rxd_r0 <= 1'b1;
            rxd_r1 <= 1'b1;
        end
        else begin
            rxd_r0 <= rxd;
            rxd_r1 <= rxd_r0;
        end
    end

    assign rx_nedge = ~rxd_r0 & rxd_r1;//检测下降沿

//输出
    assign dout = rx_data[8:1];//传输有效数据到控制模块
    assign dout_vld = rx_data_vld;//数据有效 1个周期

endmodule

控制模块

ctrl_uart.v

module ctrl_uart(
    input               clk          ,
    input               rst_n        ,
    //rx 
    input [7:0]         din          ,
    input               din_vld      ,
    //tx
    input               txd_rdy      ,
    output [7:0]        dout         ,
    output              dout_vld                
);

//信号定义
wire         empty       ;
wire         full        ;
wire [7:0]   usedw       ;


assign dout_vld = txd_rdy & !empty;//发送模块空闲且fifo非空读
//模块例化
buffer	buffer_inst (
	.aclr   (~rst_n          ),
	.clock  (clk             ),
	.data   (din             ),//直接将有效数据存入缓存中
	.rdreq  (txd_rdy & !empty),//当发送模块空闲 读fifo给发送模块
	.wrreq  (din_vld & !full ),//接收数据有效且fifo非满写
	.empty  (empty           ),
	.full   (full            ),
	.q      (dout            ),
	.usedw  (usedw           )
	);

endmodule

发送模块

module tx_uart(
    input           clk          ,
    input           rst_n        ,

    input [7:0]     din          ,
    input           din_vld      ,
    
    output          txd_rdy      ,
    output          txd                   
);

//参数定义
    //1bit周期数
    parameter TIME_1BIT = 5208;

//信号定义
    reg [12:0]          cnt_baud    ;//传输1bit所需时间 波特率计数器
    wire                add_cnt_baud;
    wire                end_cnt_baud;

    reg [3:0]           cnt_bit     ;//bit数计数器 计数接收的bit数
    wire                add_cnt_bit ;
    wire                end_cnt_bit ;
    
    reg                 tx_flag     ;//发送标志

    reg [9:0]           tx_data     ;//发送数据寄存
    reg                 txd_r       ;//输出寄存
//cnt_baud 
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt_baud <= 0;
        end
        else if(!tx_flag)begin//采集标志拉低 置零 这里特指
            cnt_baud <= 0;
        end
        else if(add_cnt_baud)begin
            if(end_cnt_baud)begin
                cnt_baud <= 0;
            end
            else begin
                cnt_baud <= cnt_baud + 1;
            end
        end
    end
    assign add_cnt_baud = tx_flag;//发送数据有效时开始计数
    assign end_cnt_baud = add_cnt_baud && (cnt_baud == TIME_1BIT - 1);

//cnt_bit
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt_bit <= 0;
        end
        else if(!tx_flag)begin
            cnt_bit <= 0;
        end
        else if(add_cnt_bit)begin
            if(end_cnt_bit)begin
                cnt_bit <= 0;
            end
            else begin
                cnt_bit <= cnt_bit + 1;
            end
        end
    end
    assign add_cnt_bit = end_cnt_baud;//1bit时间到 表示接收到1bit
    assign end_cnt_bit = add_cnt_bit && (cnt_bit == 10 - 1);

//tx_flag
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            tx_flag <= 1'b0;
        end
        else if(din_vld)begin//当传输数据有效 拉高发送标志
            tx_flag <= 1'b1;
        end
        else if(end_cnt_bit)begin//传输完成 拉低发送标志
            tx_flag <= 1'b0;
        end
    end

//tx_data
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            tx_data <= 10'h3ff;//初始全1
        end
        else if(din_vld)begin
            tx_data <= {1'b1,din,1'b0};//发送数据补齐开始位和停止位
        end
        else if(end_cnt_bit)begin
            tx_data <= 10'h3ff;//发送完成置1
        end
    end

//txd_r 发送寄存
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            txd_r <= 1'b1;//初始全1
        end
        else if(cnt_baud == 0)begin
            txd_r <= tx_data[cnt_bit];//在波特率计数器计数期间 发送数据
        end
    end

    assign txd = txd_r;//发送

//txd_rdy
    assign txd_rdy = ~tx_flag;//当发送模块处于非发送状态 空闲标志拉高
endmodule

顶层

top.v

module top(
    input 			clk		,
    input 			rst_n	,
    input 			rxd		,

    output 			txd     	 
);
//参数定义

//信号定义
wire [7:0]      rx_data            ;
wire            rx_data_vld        ;
wire [7:0]      tx_data            ;
wire            tx_data_vld        ;
wire            txd_rdy             ;
//模块例化
//接收模块
rx_uart u_rx_uart(
    /* input            */.clk         (clk         ),
    /* input            */.rst_n       (rst_n       ),
    /* input            */.rxd         (rxd         ),

    /* output [7:0]     */.dout        (rx_data     ),
    /* output           */.dout_vld    (rx_data_vld )
);
//控制模块
ctrl_uart u_ctrl_uart(
    /* input                */.clk         (clk         ),
    /* input                */.rst_n       (rst_n       ),
    //rx 
    /* input                */.din         (rx_data     ),
    /* input                */.din_vld     (rx_data_vld ),
    //tx
    /* input                */.txd_rdy     (txd_rdy     ),
    /* output               */.dout        (tx_data     ),
    /* output               */.dout_vld    (tx_data_vld )
);
//发送模块
tx_uart u_tx_uart(
    /* input            */.clk         (clk         ),
    /* input            */.rst_n       (rst_n       ),
    /* input [7:0]      */.din         (tx_data     ),
    /* input            */.din_vld     (tx_data_vld ),
    /* output           */.txd_rdy     (txd_rdy     ),  
    /* output           */.txd         (txd         )         
);
endmodule

总结
第二次修改,这个程序是第几次写忘了,但是我现在感觉uart实现起来不算太难,只是需要注意各个信号的配合,对于uart这种频率不高的协议,时序对齐还是比较宽松的,并且真相是这应该算是fpga编程的入门级协议。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值