通讯协议学习之路:UART协议理论

通讯协议之路主要分为两部分,第一部分从理论上面讲解各类协议的通讯原理以及通讯格式,第二部分从具体运用上讲解各类通讯协议的具体应用方法。

后续文章会同时发表在个人博客(jason1016.club)、CSDN;视频会发布在bilibili(UID:399951374)

序、个人简述

  1. UART通讯为异步全双工通讯
  2. UART可以转化为RS232、RS485、RS422等协议
  3. UART数据包:
  • 空闲位:1
  • 起始位:0
  • 数据位:7位,高电平代表’1’,低电平代表’0’(如果没有奇偶位为8位)
  • 奇偶校验位:1位
  • 停止位:1

START(1)+数据位(7)+奇偶校验位(1)+结束位(1位)

一、uart串口通信简介

通用异步收发器 UART(Universal Asynchronous Receiver/Transmitter),是一种串行、异步、全双工的通信协议,将所需传输的数据一位接一位地传输,在UART通讯协议中信号线上的状态位高电平代表’1’,低电平代表’0’。其特点是通信线路简单,只要一对传输线就可以实现双向通信,大大降低了成本,但传送速度较慢。

二、串口传输

1、数据协议

在串口通信中,尤其需要关注的是数据流以及波特率。一个数据流由10个数据位组成,包含1位起始位,7位有效数据位,1位奇偶校验位,1位停止位。uart串口信号线上空闲时常驻高电平,当检测到低电平下降沿时认为数据传输开始,到停止位时数据传输结束,一共10位数据位组成一个数据包。

  • 起始位:通信线路上空闲时为“1”,当检测到“0”即下降沿时,认为数据传输开始
  • 有效数据位:传输开始后传递的需要接收和发送的数据值,可以表示指令或数据
  • 奇偶校验位:奇偶校验,通过来校验传输数据中“1”的个数为奇数个(奇校验)或偶数个(偶校验)来指示传输数据是否正确
  • 停止位:数据传输结束,传输线恢复常“1”状态

此外,还需关注数据传输波特率,波特率表示一秒内传输了多少个码元数量,一般波特率为300,1200,2400,9600,19200,38400,115200等。例如9600 Baud表示一秒内传输了9600个码元信息,当一个码元只含1 bit信息时,波特率=比特率

2、整体架构

串口协议用于与其他模块之间的信息交互,包含接收模块和发送模块,信号传输线上根据波特率完成码元的接收与发送,因而接收模块主要完成并串转换,串并转换是接收和发送模块必备的基本功能,发送模块完成并串转换,接收模块完成串并转换。

波特率与时钟频率关系如下(码元为单bit时):

三、串口传输实现

1、发送模块

代码如下

//===============
//author:LGYSSS
//proj:uart_transimitter
//===============
module uart_tx(
    clk    ,
    rst_n  ,
    data_vld , //有效信号
    data_in,   //输入信号
    data_out,  //并行转串行输出信号
    rdy        //模块有效信号,示意模块准备接收数据
    );


parameter WIDTH    =8;
parameter CLK_CNT= 5208;     //波特率baud=9600下单码元传输 码元宽度为104166  50MHz下一个时钟周期为20ns,传输一个数据位104166/20=5208个clk
parameter  NUM_CNT=10;     //数据位个数


input clk;
input rst_n;
input [WIDTH-1:0]data_in;
input data_vld;
output data_out;
output rdy;
 
reg data_out; 
reg [WIDTH-1:0]data_in_reg;
reg start_tx;
wire [WIDTH-1+2:0] data;

reg   [12:0]     cnt0;
wire             add_cnt0;
wire             end_cnt0;
reg   [3:0]      cnt1;
wire             add_cnt1;
wire             end_cnt1;
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)
        start_tx<=0;
    else if(start_tx==0&&data_vld==1)
        start_tx<=1;
    else if(end_cnt1)
        start_tx<=0;
end



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;
        end
end

assign add_cnt0 = start_tx;
assign end_cnt0 = add_cnt0 && cnt0== CLK_CNT-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;
    end
end

assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && cnt1== NUM_CNT-1;

always  @(posedge clk or negedge rst_n)begin  //在数据有vld效时进行数据锁存,进行下一步串并转换
    if(!rst_n)begin
        data_in_reg<=0;
    end
    else if(start_tx==0&&data_vld==1)begin
        data_in_reg<=data_in;
    end
end

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        data_out <= 1'b1;
    end
    else if(add_cnt0 && cnt0==1-1)begin
        data_out <= data[cnt1];
    end
end

assign data={1'b1,data_in_reg,1'b0}; //起始位,停止位拼接

assign rdy=((data_vld==1)||(start_tx==1))?1'b0:1'b1;

endmodule

2、接收模块

代码如下:

//===============
//author:LGYSSS
//proj:uart_receiver
//===============
module uart_rx(
    clk    ,
    rst_n  ,
    data_vld ,
    rx_data_in,
    rx_data_out
    );

parameter WIDTH    =8;
parameter CLK_CNT= 5208;     //波特率baud=9600下单码元传输 码元宽度为104166  50MHz下一个时钟周期为20ns,传输一个数据位104166/20=5208个clk
parameter CLK_CNT_MID=2604;
parameter  NUM_CNT=10;

input clk;
input rst_n;
input rx_data_in;

output reg [WIDTH-1:0] rx_data_out;
output reg data_vld;
wire               start_rx;

reg [19:0]         cnt0;
wire               add_cnt0;
wire               end_cnt0;

reg [3:0]          cnt1;
wire               add_cnt1;
wire               end_cnt1;
reg               flag;
reg rx_data_in_reg0;
reg rx_data_in_reg1;
reg rx_data_in_reg2;


always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        rx_data_in_reg0<=0;
        rx_data_in_reg1<=0;
        rx_data_in_reg2<=0;
    end
    else begin
        rx_data_in_reg0<=rx_data_in;
        rx_data_in_reg1<=rx_data_in_reg0;
        rx_data_in_reg2<=rx_data_in_reg1;
    end
end

assign start_rx=(rx_data_in_reg2&&~rx_data_in_reg1)==1; //下降沿检测


always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            flag <= 1'b0;
        end
        else if(start_rx)begin
            flag <= 1'b1;
        end
        else if(end_cnt1)begin
            flag <= 1'b0;
        end
end
 
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;
        end
end

assign add_cnt0 = flag;
assign end_cnt0 = add_cnt0 && cnt0== CLK_CNT-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;
        end
end

assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && cnt1== NUM_CNT-1-1;
 
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        rx_data_out<=0;
    end
    else if(cnt0==CLK_CNT_MID-1&&cnt1!=0&&flag==1)begin
        rx_data_out[cnt1-1]<=rx_data_in_reg2;
    end
end
 
always  @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            data_vld <= 1'b0;
        end
        else if(end_cnt1)begin
            data_vld <= 1'b1;
        end
        else begin
            data_vld <= 1'b0;
        end    
end
 
endmodule
 

四、串口收发仿真

串口发送模块仿真波形:

串口接收模块仿真波形:

总结

看到这里模块功能其实很清晰了,两个模块完成的功能其实就是收发数据的串并转换,传输的内容具体是什么我们是不用关心的,只需要关心数据收发格式,检测到下降沿时开始传输,传输一定数据位时结束当前传输,就完成了一个数据帧的传输,即完成了uart串口通信的一次收/发。.

同时,在本模块设计中笔者是采用锁存器来锁存当vld有效时的信号,当数据流传输速度较大时,我们也可以采取fifo来缓存我们接收的数据,以免造成数据丢失

参考文章:【精选】通信协议详解(一):UART串口(协议+数据格式+设计实现)_串口数据格式-CSDN博客

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

JASON丶LI

作者码力不足了,卑微的求个打赏

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

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

打赏作者

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

抵扣说明:

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

余额充值