UART介绍(附带原码)

基本上写的文章就是对于一个知识点的巩固

如果有什么出错的,还望大家指出,谢谢! 


物理层

UART 是通用异步串行收发器 , 他是异步全双工的通信协议

它的物理层有两条线组成,一条是rx,一条是tx ,用来接收和发送数据的两条线。

协议层 :

他的协议层由四部分组成:

1 、 起始位 (1bit )

2、  数据位 (6/7/8bit)

3、  奇偶校验位 (1 bit ) 

4、  停止位  (1/1.5/2 bit)

他的传输是遵循LSB 的,即地位先发 

1、  起始位 : 

起始位是低电平有效 

2、数据位: 

低位先发,后发高位

3、 奇偶校验位: 

奇偶校验位通过缩位异或的方式得到(xor) 

4、 停止位:

停止位是高电平有效 

Tips :  串口通信的速率用波特率来表示,他表示每秒传输二进制数据的位数,单位是bit/s (位/ 秒) 简称bps 

如何计算传输1bit所需要的时间? 

即 : 用50M  去除以对应的波特率 就可以得到相对应传输1bit 所需要的次数 。 

以上就是uart传输一个字节的协议层原理


以下是我的代码部分

我一共分为四个模块来写:

1、uart的rx模块 

2、uart的tx模块

3、uart的ctrl模块(将tx传输的数据用一个fifo存起来,再发往rx)

4、top模块,将以上三个模块整合成一个top

uart的rx模块

module uart_rx #(
    parameter       CLK_FREQ    =   50000000    ,
    parameter       BPS         =   115200      ,
    parameter       CHECK       =   "None"          //"None" 无校验 "Odd" 奇校验 "Even" 偶校验
)(
    input           clk         ,
    input           rst_n       ,
    output reg [7:0] rx_data     ,
    output          rx_data_vld ,
    input           rx_din      
);

    reg             rx_din_r1,rx_din_r2;
    wire            rx_din_nege;

    localparam      IDLE    =   0   ,
                    START   =   1   ,
                    DATA    =   2   ,
                    CAIL    =   3   ,   //校验位
                    STOP    =   4   ;
    
    reg     [2:0]   state           ;
    wire            idle2start      ;  
    wire            start2data      ;  
    wire            data2stop       ;  
    wire            data2cail       ;
    wire            cail2stop       ;
    wire            stop2idle       ;  

    reg	[8:0] cnt_bps;
    wire		  add_bps_cnt,end_bps_cnt;	
    localparam      MAX_BPS = CLK_FREQ/BPS;

    reg	[2:0] cnt_bit;
    wire		  add_bit_cnt,end_bit_cnt;	

(* keep *)    wire    rx_data_check;
    reg     rx_check;
    reg     error;
/**************************************************************
                            下降沿检测
**************************************************************/
    always@(posedge clk or negedge rst_n)
        if(!rst_n) begin
            rx_din_r1 <= 1;
            rx_din_r2 <= 1;
        end
        else begin
            rx_din_r1 <= rx_din;
            rx_din_r2 <= rx_din_r1;
        end
    assign rx_din_nege = !rx_din_r1 && rx_din_r2;

/**************************************************************
                            状态机
**************************************************************/
    always@(posedge clk or negedge rst_n)
        if(!rst_n)
            state <= IDLE;
        else case(state)
            IDLE    :   if(idle2start)
                            state <= START;
            START   :   if(start2data)
                            state <= DATA;
            DATA    :   if(data2stop)
                            state <= STOP;
                        else if(data2cail)
                            state <= CAIL;
            CAIL    :   if(cail2stop)
                            state <= STOP;
            STOP    :   if(stop2idle)
                            state <= IDLE;
            default :   state <= IDLE;
        endcase

    assign idle2start = state == IDLE   && rx_din_nege; 
    assign start2data = state == START  && end_bps_cnt; 
    assign data2stop  = state == DATA   && end_bit_cnt && CHECK == "None";
    assign data2cail  = state == DATA   && end_bit_cnt;
    assign cail2stop  = state == CAIL   && end_bps_cnt;
    assign stop2idle  = state == STOP   && (cnt_bps == MAX_BPS >> 1) ; 

/**************************************************************
                        波特率控制
**************************************************************/
    always@(posedge clk or negedge rst_n)	
        if(!rst_n)								
            cnt_bps <= 'd0;						
        else    if(add_bps_cnt) begin				
            if(end_bps_cnt)						
                cnt_bps <= 'd0;  				
            else									
                cnt_bps <= cnt_bps + 1'b1;		
        end	
        else 
            cnt_bps <= 'd0;										
    assign add_bps_cnt = state != IDLE;
    assign end_bps_cnt = add_bps_cnt && cnt_bps == MAX_BPS - 1;

/**************************************************************
                            数据位控制
**************************************************************/
    always@(posedge clk or negedge rst_n)	
        if(!rst_n)								
            cnt_bit <= 'd0;						
        else    if(add_bit_cnt) begin				
            if(end_bit_cnt)						
                cnt_bit <= 'd0;  				
            else									
                cnt_bit <= cnt_bit + 1'b1;		
        end											
    assign add_bit_cnt = end_bps_cnt && state == DATA;
    assign end_bit_cnt = add_bit_cnt && cnt_bit == 8 - 1;

/**************************************************************
                            数据接收逻辑
**************************************************************/
    always@(posedge clk or negedge rst_n)
        if(!rst_n)
            rx_data <= 0;
        else if(state == DATA && (cnt_bps == MAX_BPS >> 1)) //波特率的中间时刻采样数据
            rx_data <= {rx_din_r2,rx_data[7:1]};

    assign rx_data_vld = stop2idle && !error;//(CHECK == "None")? data2stop : (stop2idle && !error);

/**************************************************************
                            校验
**************************************************************/
//计算接收到的数据
    assign rx_data_check = (CHECK == "Odd")? ~^rx_data : ^rx_data;    

//接收到的校验位
    always@(posedge clk or negedge rst_n)
        if(!rst_n)
            rx_check <= 0;
        else if(state == CAIL && (cnt_bps == MAX_BPS >> 1))
            rx_check <= rx_din_r2;

    always@(posedge clk or negedge rst_n)
        if(!rst_n)
            error <= 0;
        else if(cail2stop)
            error <= rx_check != rx_data_check;


endmodule


uart的tx模块

module uart_tx #(
    parameter       CLK_FREQ    =   50000000    ,
    parameter       BPS         =   115200      ,
    parameter       CHECK       =   "None"          //"None" 无校验 "Odd" 奇校验 "Even" 偶校验
)(
    input           clk         ,
    input           rst_n       ,
    input   [7:0]   tx_din      ,
    input           tx_din_vld  ,
    output          tx_ready     ,
    output  reg     tx_dout     
);

    localparam      IDLE    =   0   ,
                    START   =   1   ,
                    DATA    =   2   ,
                    CAIL    =   3   ,   //校验位
                    STOP    =   4   ;
    
    reg     [2:0]   state           ;
    wire            idle2start      ;  
    wire            start2data      ;  
    wire            data2stop       ;  
    wire            data2cail       ;
    wire            cail2stop       ;
    wire            stop2idle       ;  

    reg	[8:0] cnt_bps;
    wire		  add_bps_cnt,end_bps_cnt;	
    localparam      MAX_BPS = CLK_FREQ/BPS;

    reg	[2:0] cnt_bit;
    wire		  add_bit_cnt,end_bit_cnt;	

    reg     [7:0]   tx_din_r;
/**************************************************************
                            状态机
**************************************************************/
    always@(posedge clk or negedge rst_n)
        if(!rst_n)
            state <= IDLE;
        else case(state)
            IDLE    :   if(idle2start)
                            state <= START;
            START   :   if(start2data)
                            state <= DATA;
            DATA    :   if(data2stop)
                            state <= STOP;
                        else if(data2cail)
                            state <= CAIL;
            CAIL    :   if(cail2stop)
                            state <= STOP;
            STOP    :   if(stop2idle)
                            state <= IDLE;
            default :   state <= IDLE;
        endcase

    assign idle2start = state == IDLE   && tx_din_vld; 
    assign start2data = state == START  && end_bps_cnt; 
    assign data2stop  = state == DATA   && end_bit_cnt && CHECK == "None";
    assign data2cail  = state == DATA   && end_bit_cnt;
    assign cail2stop  = state == CAIL   && end_bps_cnt;
    assign stop2idle  = state == STOP   && end_bps_cnt; 

/**************************************************************
                        波特率控制
**************************************************************/
    always@(posedge clk or negedge rst_n)	
        if(!rst_n)								
            cnt_bps <= 'd0;						
        else    if(add_bps_cnt) begin				
            if(end_bps_cnt)						
                cnt_bps <= 'd0;  				
            else									
                cnt_bps <= cnt_bps + 1'b1;		
        end											
    assign add_bps_cnt = state != IDLE;
    assign end_bps_cnt = add_bps_cnt && cnt_bps == MAX_BPS - 1;

/**************************************************************
                            数据位控制
**************************************************************/
    always@(posedge clk or negedge rst_n)	
        if(!rst_n)								
            cnt_bit <= 'd0;						
        else    if(add_bit_cnt) begin				
            if(end_bit_cnt)						
                cnt_bit <= 'd0;  				
            else									
                cnt_bit <= cnt_bit + 1'b1;		
        end											
    assign add_bit_cnt = end_bps_cnt && state == DATA;
    assign end_bit_cnt = add_bit_cnt && cnt_bit == 8 - 1;

/**************************************************************
                            发送数据寄存
**************************************************************/
    always@(posedge clk or negedge rst_n)
        if(!rst_n)
            tx_din_r <= 0;
        else if(tx_din_vld)
            tx_din_r <= tx_din;

/**************************************************************
                            计算校验位
**************************************************************/
(* keep *)wire    uart_check;
    assign uart_check = ^tx_din_r;  //奇校验,缩位异或  1:奇数个1  0:偶数个1

/**************************************************************
                            UART发送时序
**************************************************************/
    always@(posedge clk or negedge rst_n)
        if(!rst_n)
            tx_dout <= 1;
        else case(state)
            START   :   tx_dout <= 0;
            DATA    :   tx_dout <= tx_din_r[cnt_bit];
            CAIL    :   tx_dout <= (CHECK == "Odd")? ~uart_check : uart_check;
            default :   tx_dout <= 1;
        endcase

    assign tx_ready = state == IDLE;

endmodule


ctrl模块和top模块就不拿出来了,简单实现了下uart的传输功能 。 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值