三大低速总线之一:UART

三大低速总线之一:UART

文章目录

  • 三大低速总线之一:UART
  • 前言
  • 一、UART协议
  • 二、设计
    • 1.整体说明
    • 2.rx波形设计
  • 三 程序实现


前言

三大低速总线:UART、IIC、SPI,其中IIC和SPI是同步通讯,UART是异步的。

优点:
简单:tx和rx都是一根线,使用简单的帧结构传输。 UART通信协议相对简单,易于实现和调试。
适用性广泛:UART被广泛应用于各种设备之间的通信,具有较好的兼容性。
距离:UART通信距离较远,适用于需要长距离传输的场景。
缺点:

速度较低:UART通信速度相对较低,不适用于对速度要求较高的应用。
双工:UART通信是双工的,可以进行低速双工传输数据,进行数据的发送和接收。
不可靠:由于UART是异步通信,可能会受到噪声和干扰的影响,导致数据传输不可靠。

为我们的项目选择合适的协议:

通信速度:SPI 提供高速度,UART 提供高灵活性,I2C 适用于速度要求较低,接线简单的配置。
电路设计:I2C 可实现多个设备的高效空间管理,SPI 可实现大型设计中的性能,而 UART 可实现简单性和多功能性。
距离和通信环境:UART 在长距离上具有稳定性,而 I2C 更适合短距离。
双工要求:SPI 和 UART 提供全双工功能,而 I2C 仅限于半双工。

小结:

  • 使用帧传输,没有时钟的适合长距离,比如UART(如果是差分就更好了),但是帧传输会有冗余信息,传输相对较慢(但这个只针对一般接口,对于高速IO接口的工作频率会很高就不存在这个问题)
  • 带有随路时钟的,因为可能存在各种干扰,对时钟不友好,不适合长距离。
  • 使用哪种协议也要看使用的io资源,spi协议使用的端口就比较多,但是速度快
    应用案例:
    应用案例:
    uart:
    微控制器和外设之间的连接:用于简单直接的数据交换。
    GPS 模块和与计算机的串行接口:用于可靠、低复杂性的通信。
    工业机器:UART 通常用于工业设备中以实现稳定的通信。
    使用 RS 标准(例如 RS-232、RS-485):这些标准支持更长距离的 UART 通信,并提供使用适当的收发器创建多从属网络的可能性,从而增加 UART 应用的灵活性和广度。
    微控制器和外设之间的连接:用于简单直接的数据交换。
    GPS 模块和与计算机的串行接口:用于可靠、低复杂性的通信。
    工业机器:UART 通常用于工业设备中以实现稳定的通信。
    使用 RS 标准(例如 RS-232、RS-485):这些标准支持更长距离的 UART 通信,并提供使用适当的收发器创建多从属网络的可能性,从而增加 UART 应用的灵活性和广度。
    spi:
    SPI 非常适合需要快速可靠的数据传输的情况,例如 TFT 显示器SD 存储卡无线通信模块。然而,在具有许多从站的复杂系统中,其有效性会降低。
    IIC:
    就其应用而言,连接方面,I2C在需要简单经济的通信环境中表现出色。它尤其擅长在小型传感器、LCD 屏幕和 RTC(实时时钟)模块中使用。此外,I2C 由于其在紧凑电路中的效率,在温度控制设备电池管理系统LED 控制器中很有用。但是,在需要快速或长距离数据传输的项目中,最好选择其他协议

一、UART协议

特点:

  • 低速协议
  • rx 和 tx两根线
    帧结构
    在这里插入图片描述

二、设计

1.整体说明

整体框图
在这里插入图片描述
模块说明
在这里插入图片描述

2.rx波形设计

在这里插入图片描述
TX波形设计
在这里插入图片描述

三 程序实现

RX设计思想:根据上面时序图的端口顺序

  1. 同步
  2. 帧起始位判断(边沿检测,并用中间控制信号en代替flag,只检测一次)
  3. 帧比特率计数(其他的都是依靠这个衍生的)
  4. bit标志位
  5. bit_cnt
  6. 每bit的数据接受输出
  7. 稳定数据输出(不输出正在接受变化的数据,而是接受完毕后,在用数据接受完毕的flag来输出最后的数据)
  8. 稳定数据flag输出(需要延迟一拍rx_flag)

需要注意的点有下面几个:

同步的时候需要两拍,但是用这两拍判断帧的沿是不稳定的,需要用第二排和第三拍的输出
数据采样的时候依靠flag,但是flag为高的时候,采的数据是flag变化之前的(是起始位),即实际上是从1bit开始采样,采样到8的时候,有效数据采集完毕,起始和结束是没有采样的
变化的数据不能传递给下一个模块,要稳定的时候去传递,因此rx_data,只是中间数据,po_data才是最后的数据,同时po_plag也要同步域po_data(相对于rx_flag沿延迟一拍)

`timescale  1ns/1ns

module  uart_rx
#(
    parameter   UART_BPS    =   'd9600,         //串口波特率
    parameter   CLK_FREQ    =   'd50_000_000    //时钟频率
)
(
    input   wire            sys_clk     ,   //系统时钟50MHz
    input   wire            sys_rst_n   ,   //全局复位
    input   wire            rx          ,   //串口接收数据

    output  reg     [7:0]   po_data     ,   //串转并后的8bit数据
    output  reg             po_flag         //串转并后的数据有效标志信号
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//localparam    define
localparam  BAUD_CNT_MAX    =   CLK_FREQ/UART_BPS   ;

//reg   define
reg         rx_reg1     ;
reg         rx_reg2     ;
reg         rx_reg3     ;
reg         start_nedge ;
reg         work_en     ;
reg [12:0]  baud_cnt    ;
reg         bit_flag    ;
reg [3:0]   bit_cnt     ;
reg [7:0]   rx_data     ;
reg         rx_flag     ;

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//插入两级寄存器进行数据同步,用来消除亚稳态
//rx_reg1:第一级寄存器,寄存器空闲状态复位为1
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_reg1 <= 1'b1;
    else
        rx_reg1 <= rx;

//rx_reg2:第二级寄存器,寄存器空闲状态复位为1
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_reg2 <= 1'b1;
    else
        rx_reg2 <= rx_reg1;

//rx_reg3:第三级寄存器和第二级寄存器共同构成下降沿检测
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_reg3 <= 1'b1;
    else
        rx_reg3 <= rx_reg2;

//start_nedge:检测到下降沿时start_nedge产生一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        start_nedge <= 1'b0;
    else    if((~rx_reg2) && (rx_reg3))
        start_nedge <= 1'b1;
    else
        start_nedge <= 1'b0;

//work_en:接收数据工作使能信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        work_en <= 1'b0;
    else    if(start_nedge == 1'b1)
        work_en <= 1'b1;
    else    if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
        work_en <= 1'b0;

//baud_cnt:波特率计数器计数,从0计数到BAUD_CNT_MAX - 1
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        baud_cnt <= 13'b0;
    else    if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0))
        baud_cnt <= 13'b0;
    else    if(work_en == 1'b1)
        baud_cnt <= baud_cnt + 1'b1;

//bit_flag:当baud_cnt计数器计数到中间数时采样的数据最稳定,
//此时拉高一个标志信号表示数据可以被取走
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        bit_flag <= 1'b0;
    else    if(baud_cnt == BAUD_CNT_MAX/2 - 1)
        bit_flag <= 1'b1;
    else
        bit_flag <= 1'b0;

//bit_cnt:有效数据个数计数器,当8个有效数据(不含起始位和停止位)
//都接收完成后计数器清零
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        bit_cnt <= 4'b0;
    else    if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
        bit_cnt <= 4'b0;
     else    if(bit_flag ==1'b1)
         bit_cnt <= bit_cnt + 1'b1;

//rx_data:输入数据进行移位
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_data <= 8'b0;
    else    if((bit_cnt >= 4'd1)&&(bit_cnt <= 4'd8)&&(bit_flag == 1'b1))
        rx_data <= {rx_reg3, rx_data[7:1]};

//rx_flag:输入数据移位完成时rx_flag拉高一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_flag <= 1'b0;
    else    if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
        rx_flag <= 1'b1;
    else
        rx_flag <= 1'b0;

//po_data:输出完整的8位有效数据
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        po_data <= 8'b0;
    else    if(rx_flag == 1'b1)
        po_data <= rx_data;

//po_flag:输出数据有效标志(比rx_flag延后一个时钟周期,为了和po_data同步)
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        po_flag <= 1'b0;
    else
        po_flag <= rx_flag;

endmodule

TX设计思想: 根据上面时序图的端口顺序

  1. 判断有效数据输入标志flag
  2. 进行baud率计数器计数
  3. 一般不在边界进行采样或者发送,这个选择计数后的1作为1bit发送的标志信号(在发送的时候也使用这个信号)
  4. 生成bit_cnt
  5. 发送数据帧,以flag信号为标志进行发送(注意:bit_cnt加1是延迟一拍的,发送的时候使用flag发送的,比如采样到flag为1的时候,实际上cnt_bit是0,然后下一排cnt_bit才+1,即实际上当前发送的是第0,即起始位)
`timescale  1ns/1ns

module  uart_tx
#(
    parameter   UART_BPS    =   'd9600,         //串口波特率
    parameter   CLK_FREQ    =   'd50_000_000    //时钟频率
)
(
     input   wire            sys_clk     ,   //系统时钟50MHz
     input   wire            sys_rst_n   ,   //全局复位
     input   wire    [7:0]   pi_data     ,   //模块输入的8bit数据
     input   wire            pi_flag     ,   //并行数据有效标志信号
 
     output  reg             tx              //串转并后的1bit数据
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//localparam    define
localparam  BAUD_CNT_MAX    =   CLK_FREQ/UART_BPS   ;

//reg   define
reg [12:0]  baud_cnt;
reg         bit_flag;
reg [3:0]   bit_cnt ;
reg         work_en ;

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//work_en:接收数据工作使能信号
always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            work_en <= 1'b0;
        else    if(pi_flag == 1'b1)
            work_en <= 1'b1;
        else    if((bit_flag == 1'b1) && (bit_cnt == 4'd9))
            work_en <= 1'b0;

//baud_cnt:波特率计数器计数,从0计数到BAUD_CNT_MAX - 1
always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            baud_cnt <= 13'b0;
        else    if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0))
            baud_cnt <= 13'b0;
        else    if(work_en == 1'b1)
            baud_cnt <= baud_cnt + 1'b1;

//bit_flag:当baud_cnt计数器计数到1时让bit_flag拉高一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            bit_flag <= 1'b0;
        else    if(baud_cnt == 13'd1)
            bit_flag <= 1'b1;
        else
            bit_flag <= 1'b0;

//bit_cnt:数据位数个数计数,10个有效数据(含起始位和停止位)到来后计数器清零
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        bit_cnt <= 4'b0;
    else    if((bit_flag == 1'b1) && (bit_cnt == 4'd9))
        bit_cnt <= 4'b0;
    else    if((bit_flag == 1'b1) && (work_en == 1'b1))
        bit_cnt <= bit_cnt + 1'b1;

//tx:输出数据在满足rs232协议(起始位为0,停止位为1)的情况下一位一位输出
always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            tx <= 1'b1; //空闲状态时为高电平
        else    if(bit_flag == 1'b1)
            case(bit_cnt)
                0       : tx <= 1'b0;
                1       : tx <= pi_data[0];
                2       : tx <= pi_data[1];
                3       : tx <= pi_data[2];
                4       : tx <= pi_data[3];
                5       : tx <= pi_data[4];
                6       : tx <= pi_data[5];
                7       : tx <= pi_data[6];
                8       : tx <= pi_data[7];
                9       : tx <= 1'b1;
                default : tx <= 1'b1;
            endcase

endmodule

(1)奇偶校验的检错率只有50%,因为只有奇数个数据位发生变化能检测到,如果偶数个数据位发生变化则难以发现;
(2)奇偶校验每传输一个字节都需要加一位校验位,对传输效率影响很大。
(3)奇偶校验只能发现错误,但不能纠正错误。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值