串口回环工程

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

串口回环工程:使用RS232进行上位机和FPGA之间的串口通信。


个人学习记录,仅供参考

一、实验目的

上位机(电脑)发送一帧数据(1位起始位,8位数据位,1位校验位,1位停止位)给FPGA,FPGA对数据进行校验,如果正确,将该帧数据保持不变发送给上位机;如果错误,发送带有校验位的00给上位机。

二、通信协议

物理层

信号线:1、TXD:发送端。 2、RXD:接收端
通信的方式和方向:异步通信全双工
RS232、RS422、RS485区别

图1、三种串口数据线对比

协议层

数据格式:
在这里插入图片描述图2、串口的数据格式
传输速率
波特率:9600、19200、38400、57600、115200。
单位:bit/s

三、项目框架

在这里插入图片描述
参数模块

`define CLK_FREQ 50_000_000 

`define TIME_zhen 10

`define BAUD_9600       9600
// `define BAUD_19200      19200
// `define BAUD_38400      38400
// `define BAUD_57600      57600
// `define BAUD_115200     115200 

`ifdef BAUD_9600
    `define BAUD_NUM    `CLK_FREQ / `BAUD_9600
`elsif BAUD_19200
    `define BAUD_NUM    `CLK_FREQ / `BAUD_19200
`elsif BAUD_38400
    `define BAUD_NUM    `CLK_FREQ / `BAUD_38400
`elsif BAUD_57600
    `define BAUD_NUM    `CLK_FREQ / `BAUD_57600
`elsif BAUD_115200
    `define BAUD_NUM    `CLK_FREQ / `BAUD_115200
`endif  

顶层模块

module top( 
    input               clk             ,
    input               rst_n           ,
    input               rxd             ,
    output              txd             
);                               

wire            [7:0]           rx_data             ;
wire                            rx_data_vld         ;
wire                            tx_buzy             ;
wire            [7:0]           ctrl_data           ;
wire                            ctrl_data_vld       ;

// 实例化
RX RX_inst(
    /* input                */.clk             (clk             ),
    /* input                */.rst_n           (rst_n           ),
    /* input                */.rxd             (rxd             ),
    /* output      [7:0]    */.rx_data         (rx_data         ),
    /* output               */.rx_data_vld     (rx_data_vld     )
); 

Ctrl Ctrl_inst(
    /* input                */.clk             (clk             ),
    /* input                */.rst_n           (rst_n           ),
    /* input       [7:0]    */.rx_data         (rx_data         ),
    /* input                */.rx_data_vld     (rx_data_vld     ),
    /* input                */.tx_buzy         (tx_buzy         ),
    /* output      [7:0]    */.ctrl_data       (ctrl_data       ),
    /* output               */.ctrl_data_vld   (ctrl_data_vld   )
);

TX TX_inst(
    /* input                */.clk             (clk             ),
    /* input                */.rst_n           (rst_n           ),
    /* input       [7:0]    */.ctrl_data       (ctrl_data       ),
    /* input                */.ctrl_data_vld   (ctrl_data_vld   ),
    /* output               */.tx_buzy         (tx_buzy         ),
    /* output               */.txd             (txd             )
);

endmodule

2.1、RX模块

实现目标
UART的接收模块————将串行数据转为并行数据后,传给Ctrl模块
逻辑步骤
1、寻找串行数据开始的地方
-----(寄存打拍)
2、在接收时间内寄存收到的数据
-----(两个计数器,一个计数波特率,一个计数数据长度)
3、接受完1帧数据后,进行奇校验,校验正确后将数据传给Ctrl模块,校验错误发送00给Ctrl模块
-----(当最后一位数据接收完毕,输出“数据合理”信号,表示一帧数据已经完整接收)
代码

`include "param.v"

module RX(
    input               clk             ,
    input               rst_n           ,
    input               rxd             ,
    output      [7:0]   rx_data         ,
    output              rx_data_vld     
);                               
//内部连线      
    // 1
    reg                 rxd_r0          ;
    reg                 rxd_r1          ;
    wire                nedge           ;
    reg                 rx_flag         ;       // 接收flag信号
    // 2
    reg         [12:0]  cnt_Baud        ;
    wire                add_cnt_Baud    ;
    wire                end_cnt_Baud    ;

    reg         [3:0]   cnt_byte        ;
    wire                add_cnt_byte    ;
    wire                end_cnt_byte    ;

    reg         [9:0]   rx_data_r       ;       // 输出寄存器
    // 3
    reg                 rx_data_vld_r   ;   
//内部逻辑         
    // 1、寻找串行数据开始的地方(寄存打拍)
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            rxd_r0 <= 1'd0 ;
            rxd_r1 <= 1'd0 ;
        end
        else begin 
            rxd_r0 <= rxd    ;
            rxd_r1 <= rxd_r0 ;
        end 
    end
    assign  nedge = (~rxd_r0) & rxd_r1 ;

    // 2、在接收时间内寄存收到的数据(两个计数器,一个计数波特率,一个计数数据长度)
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            rx_flag <= 1'd0 ;
        end 
        else if(nedge)begin 
            rx_flag <= 1'b1 ;
        end 
        else if(rx_data_vld_r)begin 
            rx_flag <= 1'b0 ;
        end 
    end
    // 计数波特率
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            cnt_Baud <= 13'd0;
        end 
        else if(add_cnt_Baud     )begin 
            if(end_cnt_Baud     )begin 
                cnt_Baud <= 13'd0;
            end
            else begin 
                cnt_Baud <= cnt_Baud + 1'b1;
            end 
        end
    end     
    assign add_cnt_Baud = rx_flag ;
    assign end_cnt_Baud = add_cnt_Baud && cnt_Baud == `BAUD_NUM-1'b1;

    // 计数数据长度
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            cnt_byte <= 4'd0 ;
        end 
        else if(add_cnt_byte)begin 
            if(end_cnt_byte)begin 
                cnt_byte <= 4'd0 ;
            end
            else begin 
                cnt_byte <= cnt_byte + 1'b1 ;
            end 
        end
    end 
    assign add_cnt_byte = end_cnt_Baud      ;
    assign end_cnt_byte = add_cnt_byte && cnt_byte == `TIME_zhen-1'b1 ;
    
    // 将每一位的数据传给rx_data_r寄存器    
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            rx_data_r <= 10'h3ff ;
        end 
        else if(cnt_Baud == (`BAUD_NUM >> 1))begin 
            rx_data_r[cnt_byte] <= rxd_r0 ;
        end
    end    

    // 还可以将数据进行移位寄存
    /* always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            rx_data_r <= 11'h7ff ;
        end 
        else if(cnt_Baud == (`BAUD_NUM >> 1))begin 
            rx_data_r <= {rxd_r0,rx_data_r[10:1]} ;
        end
    end */
    
    assign rx_data = rx_data_r[8:1] ; 
    /* // 进行奇校验
    assign check_bit = !(^(rx_data_r[8:1])) ;
    assign rx_data = (check_bit == rx_data_r[9]) ? (rx_data_r[8:1]) : (8'h00) ; 
    // ^rx_data_r[8:1]为1表示数据位中有奇数个1 */
    
    // 3、接受完1帧数据后,将数据传给Ctrl模块(当最后一位数据接收完毕,输出“数据合理”信号,表示一帧数据已经完整接收)
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            rx_data_vld_r <= 1'd0 ;
        end 
        else if(end_cnt_byte)begin 
            rx_data_vld_r <= 1'd1 ;
        end 
        else begin
            rx_data_vld_r <= 1'b0 ;
        end
    end
    assign rx_data_vld = rx_data_vld_r ;

    
endmodule

2.2、ctrl模块

将接收到的一字节数据(8bit)传递给TX模块,用了一个FIFO,FIFO设置为前显模式。

module Ctrl(
    input               clk             ,
    input               rst_n           ,
    input       [7:0]   rx_data         ,
    input               rx_data_vld     ,
    input               tx_buzy         ,
    output      [7:0]   ctrl_data       ,
    output              ctrl_data_vld     
);

wire            [7:0]       fifo_data   ;
wire                        fifo_rdreq  ;
wire                        fifo_wrreq  ;
wire                        fifo_empty  ;
wire                        fifo_full   ;
wire            [7:0]       fifo_q      ;
wire            [7:0]       fifo_usedw  ;

fifo_8_256	fifo_8_256_inst (
	.aclr       ( ~rst_n            ),
	.clock      ( clk               ),
	.data       ( fifo_data         ),
	.rdreq      ( fifo_rdreq        ),
	.wrreq      ( fifo_wrreq        ),
	.empty      ( fifo_empty        ),
	.full       ( fifo_full         ),
	.q          ( fifo_q            ),          
	.usedw      ( fifo_usedw        )
	);

assign      fifo_data   =   rx_data     ;
assign      fifo_rdreq  =   (!fifo_empty) && (!tx_buzy) ;
assign      fifo_wrreq  =   (!fifo_full) && rx_data_vld ;

assign      ctrl_data = fifo_q ;
assign      ctrl_data_vld = fifo_rdreq ;

endmodule

2.3、TX模块

实现目标: UART的发送模块(并转串)————什么时候传、传什么、怎么传、什么时候结束传输

逻辑步骤: 1、当vld有效时,添加起始位和停止位,拼接成完整的一帧数据。
2、传输数据(一个计数波特率,一个计数数据位),按照协议发送数据
3、传输结束时,将buzy拉低

`include "param.v"
module TX(
    input               clk             ,
    input               rst_n           ,
    input       [7:0]   tx_data         ,
    input               tx_data_vld     ,
    output              tx_buzy         ,
    output              txd             
);                               
// 内部连线         
    reg         [9:0]   tx_data_r           ;

    reg         [12:0]  cnt_Baud       ;
    wire                add_cnt_Baud   ;
    wire                end_cnt_Baud   ;

    reg         [3:0]   cnt_byte            ;
    wire                add_cnt_byte        ;
    wire                end_cnt_byte        ;

    reg                 tx_flag             ;       // 发送flag信号
    reg                 txd_r               ;

    wire                check               ;

// 内部逻辑         
    /* // 0、添加校验位
    assign check = ^tx_data   ;        // ^tx_data   :8位数据1的个数为奇数个时为1,为偶数个为0  
    // --------------------------奇校验--数据位和校验位中1的个数为奇数个---------------------      
    // 当8位数据位的1的个数为奇数个时(check为1),校验位为0(~check)可满足奇数个1
    // 当8位数据位的1的个数为偶数个时(check为0),校验位为1(~check)可满足奇数个1

    // --------------------------偶校验--数据位和校验位中1的个数为偶数个---------------------
    // 当8位数据位的1的个数为奇数个时(check为1),校验位为1(check)可满足偶数个1
    // 当8位数据位的1的个数为偶数个时(check为0),校验位为0(check)可满足偶数个1 */

    // 1、当vld有效时,开始传输数据 (输入寄存与拼接)
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            tx_data_r   <= 11'd0 ;
        end 
        else if(tx_data_vld  )begin // 奇校验
            tx_data_r   <= {1'b1,tx_data,1'b0};
        end 
    end
    
    // 2、传输数据(一个计数波特率,一个计数数据位),按照协议发送数据
    // 计数有效信号——tx_flag
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            tx_flag <= 1'd0 ;
        end 
        else if(tx_data_vld  )begin 
            tx_flag <= 1'b1 ;
        end 
        else if (end_cnt_byte)begin 
            tx_flag <= 1'b0 ;
        end 
    end
    // 波特计数器——cnt_Baud
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            cnt_Baud <= 13'd0 ;
        end 
        else if(add_cnt_Baud)begin 
            if(end_cnt_Baud)begin 
                cnt_Baud <= 13'd0 ;
            end
            else begin 
                cnt_Baud <= cnt_Baud + 1'b1 ;
            end 
        end
    end 
    assign add_cnt_Baud = tx_flag;
    assign end_cnt_Baud = add_cnt_Baud && (cnt_Baud == (`BAUD_NUM-1'b1)) ;
    // 数据位计数器——cnt_byte
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            cnt_byte <= 3'd0 ;
        end 
        else if(add_cnt_byte)begin 
            if(end_cnt_byte)begin 
                cnt_byte <= 3'd0 ;
            end
            else begin 
                cnt_byte <= cnt_byte + 1'b1 ;
            end 
        end
    end 
    assign add_cnt_byte = end_cnt_Baud;
    assign end_cnt_byte = add_cnt_byte && (cnt_byte == (`TIME_zhen-1'b1)) ;
    // 按位传输数据——txd_r(在tx_flag有效期间内按位传)
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            txd_r <= 1'b1 ;
        end 
        else if(tx_flag)begin 
            txd_r <= tx_data_r[cnt_byte] ;
        end 
    end
    assign txd = txd_r ;
    // txd_buzy_r信号——信号波形和tx_flag一样,不用在单独定义寄存器输出
    assign tx_buzy = tx_flag ;
    
    
endmodule

面试遇到的一些问题

1、讲一下UART协议

物理层:

UART为异步全双工通信,物理层有两根数据线(RX和TX),这两根数据线空闲为高电平。

协议层:

数据是LSB传输的,首先是1位的低电平表示数据传输开始,然后是5~8位的数据位,之后有一个奇偶校验位,最后是1位的高电平表示数据传输结束。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寻常人家_二一

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值