当前位置: 首页 > news >正文

UART协议

目录

  • 一、概述
  • 二、帧格式
    • 起始位
    • 数据位
    • 奇偶校验位
    • 停止位
  • 三、数据传输过程
  • 四、串行通信接口
    • RS232
    • RS422
    • RS485
  • 五、UART环回程序设计

参考:正点原子FPGA开发指南

一、概述

UART(通用异步收发器)是一种异步、全双工串行通信总线,在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。

UART以1个字符为传输单位,两字符之间间隔不固定,但同一字符两个相邻位之间间隔固定。

UART通信主要有TX、RX、GND3根线,收发端TX、RX交叉相连。
在这里插入图片描述

二、帧格式

UART传输的1帧数据由起始位、数据位、奇偶校验位、停止位组成。
在这里插入图片描述
UART通信的速率用波特率表示,波特率单位是bps,表示每秒传输的二进制位数。

起始位

UART传输线空闲状态时保持高电平,若要开始传输,需将传输线拉低并维持1个时钟周期

当接收端检测到传输线的下降沿(起始位)时,开始以波特率对应的速率读取数据帧中各位。

数据位

数据位可选择5~8位,一般常用8位数据位,优先发送数据的低位

奇偶校验位

检验传输数据中1的个数总和是奇数/偶数来判断数据传输是否出错,可选择奇校验、偶校验、无校验。

奇(偶)校验时,发送方应使数据位中 1 的个数与校验位中 1 的个数之和为奇(偶)数。接收放对接收数据中1的个数进行检查,若不为奇(偶)数,则说明在传输过程中出错。

停止位

UART将数据传输线拉高,并维持1~2个时钟周期。

三、数据传输过程

  1. UART发送模块从数据总线并行接收数据
    在这里插入图片描述
    2.UART发送模块将起始位、奇偶校验位、停止位添加到数据帧。
    在这里插入图片描述
    3.以串行方式将整个数据包传输到UART接收模块,UART接收模块以预配置的波特率对数据进行采样。

在这里插入图片描述
4.UART接收模块解包获取数据。
在这里插入图片描述
5.UART接收模块将串行数据转化为并行数据,并传输到接收端数据总线上。
在这里插入图片描述

四、串行通信接口

RS232

RS232采用负逻辑电平单端传输方式,可实现全双工通信,常用接口类型是DB9,一般只用到TXD、RXD、GND。

RS232抗干扰能力差、通信距离短、数据传输速率低,而且仅支持一对一通信,无法实现多个设备互联。
在这里插入图片描述

RS422

RS422采用差分传输方式,采用4根线实现全双工通信,2根用于发送、2根用于接收。

RS422最大传输速率可达10Mbps,允许在1条总线上连接10个接收器,实现了单个设备发送、多个设备接收的功能。

RS485

RS485同样采用差分传输,但RS485只有两根信号线,发送和接收共用,只能实现半双工通信,允许多个发送器连接到同一条总线上,各设备通过使能信号控制发送和接收过程。

五、UART环回程序设计

//UART发送模块
module uart_send(input	      sys_clk,                  //系统时钟input         sys_rst_n,                //系统复位,低电平有效input         uart_en,                  //发送使能信号input  [7:0]  uart_din,                 //待发送数据output        uart_tx_busy,             //发送忙状态标志      output  reg   uart_txd                  //UART发送端口);//parameter define
parameter  CLK_FREQ = 50000000;            //系统时钟频率
parameter  UART_BPS = 9600;                //串口波特率
localparam  BPS_CNT  = CLK_FREQ/UART_BPS;   //为得到指定波特率,对系统时钟计数BPS_CNT次//reg define
reg        uart_en_d0; 
reg        uart_en_d1;  
reg [15:0] clk_cnt;                         //系统时钟计数器
reg [ 3:0] tx_cnt;                          //发送数据计数器
reg        tx_flag;                         //发送过程标志信号
reg [ 7:0] tx_data;                         //寄存发送数据//wire define
wire       en_flag;//*****************************************************
//**                    main code
//*****************************************************
//在串口发送过程中给出忙状态标志
assign uart_tx_busy = tx_flag;//捕获uart_en上升沿,得到一个时钟周期的脉冲信号
assign en_flag = (~uart_en_d1) & uart_en_d0;//对发送使能信号uart_en延迟两个时钟周期
always @(posedge sys_clk or negedge sys_rst_n) begin         if (!sys_rst_n) beginuart_en_d0 <= 1'b0;                                  uart_en_d1 <= 1'b0;end                                                      else begin                                               uart_en_d0 <= uart_en;                               uart_en_d1 <= uart_en_d0;                            end
end//当脉冲信号en_flag到达时,寄存待发送的数据,并进入发送过程          
always @(posedge sys_clk or negedge sys_rst_n) begin         if (!sys_rst_n) begin                                  tx_flag <= 1'b0;tx_data <= 8'd0;end else if (en_flag) begin                 //检测到发送使能上升沿                      tx_flag <= 1'b1;                //进入发送过程,标志位tx_flag拉高tx_data <= uart_din;            //寄存待发送的数据end//计数到停止位结束时,停止发送过程else if ((tx_cnt == 4'd9) && (clk_cnt == BPS_CNT -(BPS_CNT/16))) begin                                       tx_flag <= 1'b0;                //发送过程结束,标志位tx_flag拉低tx_data <= 8'd0;endelse begintx_flag <= tx_flag;tx_data <= tx_data;end 
end//进入发送过程后,启动系统时钟计数器
always @(posedge sys_clk or negedge sys_rst_n) begin         if (!sys_rst_n)                             clk_cnt <= 16'd0;                                  else if (tx_flag) begin                 //处于发送过程if (clk_cnt < BPS_CNT - 1)clk_cnt <= clk_cnt + 1'b1;elseclk_cnt <= 16'd0;               //对系统时钟计数达一个波特率周期后清零endelse                             clk_cnt <= 16'd0; 				    //发送过程结束
end//进入发送过程后,启动发送数据计数器
always @(posedge sys_clk or negedge sys_rst_n) begin         if (!sys_rst_n)                             tx_cnt <= 4'd0;else if (tx_flag) begin                 //处于发送过程if (clk_cnt == BPS_CNT - 1)			//对系统时钟计数达一个波特率周期tx_cnt <= tx_cnt + 1'b1;		//此时发送数据计数器加1elsetx_cnt <= tx_cnt;       endelse                              tx_cnt  <= 4'd0;				    //发送过程结束
end//根据发送数据计数器来给uart发送端口赋值
always @(posedge sys_clk or negedge sys_rst_n) begin        if (!sys_rst_n)  uart_txd <= 1'b1;        else if (tx_flag)case(tx_cnt)4'd0: uart_txd <= 1'b0;         //起始位 4'd1: uart_txd <= tx_data[0];   //数据位最低位4'd2: uart_txd <= tx_data[1];4'd3: uart_txd <= tx_data[2];4'd4: uart_txd <= tx_data[3];4'd5: uart_txd <= tx_data[4];4'd6: uart_txd <= tx_data[5];4'd7: uart_txd <= tx_data[6];4'd8: uart_txd <= tx_data[7];   //数据位最高位4'd9: uart_txd <= 1'b1;         //停止位default: ;endcaseelse uart_txd <= 1'b1;                   //空闲时发送端口为高电平
endendmodule	          
//UART接收模块
module uart_recv(input			  sys_clk,                  //系统时钟input             sys_rst_n,                //系统复位,低电平有效input             uart_rxd,                 //UART接收端口output  reg       uart_done,                //接收一帧数据完成标志output  reg [7:0] uart_data                 //接收的数据);//parameter define
parameter  CLK_FREQ = 50000000;                //系统时钟频率
parameter  UART_BPS = 9600;                    //串口波特率
localparam  BPS_CNT  = CLK_FREQ/UART_BPS;       //为得到指定波特率,//需要对系统时钟计数BPS_CNT次
//reg define
reg        uart_rxd_d0;
reg        uart_rxd_d1;
reg [15:0] clk_cnt;                             //系统时钟计数器
reg [ 3:0] rx_cnt;                              //接收数据计数器
reg        rx_flag;                             //接收过程标志信号
reg [ 7:0] rxdata;                              //接收数据寄存器//wire define
wire       start_flag;//*****************************************************
//**                    main code
//*****************************************************
//捕获接收端口下降沿(起始位),得到一个时钟周期的脉冲信号
assign  start_flag = uart_rxd_d1 & (~uart_rxd_d0);    //对UART接收端口的数据延迟两个时钟周期
always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) begin uart_rxd_d0 <= 1'b0;uart_rxd_d1 <= 1'b0;          endelse beginuart_rxd_d0  <= uart_rxd;                   uart_rxd_d1  <= uart_rxd_d0;end   
end//当脉冲信号start_flag到达时,进入接收过程           
always @(posedge sys_clk or negedge sys_rst_n) begin         if (!sys_rst_n)                                  rx_flag <= 1'b0;else beginif(start_flag)                          //检测到起始位rx_flag <= 1'b1;                    //进入接收过程,标志位rx_flag拉高//计数到停止位中间时,停止接收过程else if((rx_cnt == 4'd9) && (clk_cnt == BPS_CNT/2))rx_flag <= 1'b0;                    //接收过程结束,标志位rx_flag拉低elserx_flag <= rx_flag;end
end//进入接收过程后,启动系统时钟计数器
always @(posedge sys_clk or negedge sys_rst_n) begin         if (!sys_rst_n)                             clk_cnt <= 16'd0;                                  else if ( rx_flag ) begin                   //处于接收过程if (clk_cnt < BPS_CNT - 1)clk_cnt <= clk_cnt + 1'b1;elseclk_cnt <= 16'd0;               	//对系统时钟计数达一个波特率周期后清零endelse                              				clk_cnt <= 16'd0;						//接收过程结束,计数器清零
end//进入接收过程后,启动接收数据计数器
always @(posedge sys_clk or negedge sys_rst_n) begin         if (!sys_rst_n)                             rx_cnt  <= 4'd0;else if ( rx_flag ) begin                   //处于接收过程if (clk_cnt == BPS_CNT - 1)				//对系统时钟计数达一个波特率周期rx_cnt <= rx_cnt + 1'b1;			//此时接收数据计数器加1elserx_cnt <= rx_cnt;       endelserx_cnt  <= 4'd0;						//接收过程结束,计数器清零
end//根据接收数据计数器来寄存uart接收端口数据
always @(posedge sys_clk or negedge sys_rst_n) begin if ( !sys_rst_n)  rxdata <= 8'd0;                                     else if(rx_flag)                            //系统处于接收过程if (clk_cnt == BPS_CNT/2) begin         //判断系统时钟计数器计数到数据位中间case ( rx_cnt )4'd1 : rxdata[0] <= uart_rxd_d1;   //寄存数据位最低位4'd2 : rxdata[1] <= uart_rxd_d1;4'd3 : rxdata[2] <= uart_rxd_d1;4'd4 : rxdata[3] <= uart_rxd_d1;4'd5 : rxdata[4] <= uart_rxd_d1;4'd6 : rxdata[5] <= uart_rxd_d1;4'd7 : rxdata[6] <= uart_rxd_d1;4'd8 : rxdata[7] <= uart_rxd_d1;   //寄存数据位最高位default:;                                    endcaseendelse rxdata <= rxdata;elserxdata <= 8'd0;
end//数据接收完毕后给出标志信号并寄存输出接收到的数据
always @(posedge sys_clk or negedge sys_rst_n) begin        if (!sys_rst_n) beginuart_data <= 8'd0;                               uart_done <= 1'b0;endelse if(rx_cnt == 4'd9) begin               //接收数据计数器计数到停止位时           uart_data <= rxdata;                    //寄存输出接收到的数据uart_done <= 1'b1;                      //并将接收完成标志位拉高endelse beginuart_data <= 8'd0;                                   uart_done <= 1'b0; end    
endendmodule	
//UART环回模块
module uart_loop(input	         sys_clk,                   //系统时钟input            sys_rst_n,                 //系统复位,低电平有效input            recv_done,                 //接收一帧数据完成标志input      [7:0] recv_data,                 //接收的数据input            tx_busy,                   //发送忙状态标志      output reg       send_en,                   //发送使能信号output reg [7:0] send_data                  //待发送数据);//reg define
reg recv_done_d0;
reg recv_done_d1;
reg tx_ready;//wire define
wire recv_done_flag;//*****************************************************
//**                    main code
//*****************************************************//捕获recv_done上升沿,得到一个时钟周期的脉冲信号
assign recv_done_flag = (~recv_done_d1) & recv_done_d0;//对发送使能信号recv_done延迟两个时钟周期
always @(posedge sys_clk or negedge sys_rst_n) begin         if (!sys_rst_n) beginrecv_done_d0 <= 1'b0;                                  recv_done_d1 <= 1'b0;end                                                      else begin                                               recv_done_d0 <= recv_done;                               recv_done_d1 <= recv_done_d0;                            end
end//判断接收完成信号,并在串口发送模块空闲时给出发送使能信号
always @(posedge sys_clk or negedge sys_rst_n) begin         if (!sys_rst_n) begintx_ready  <= 1'b0; send_en   <= 1'b0;send_data <= 8'd0;end                                                      else begin                                               if(recv_done_flag)begin                 //检测串口接收到数据tx_ready  <= 1'b1;                  //准备启动发送过程send_en   <= 1'b0;send_data <= recv_data;             //寄存串口接收的数据endelse if(tx_ready && (~tx_busy)) begin   //检测串口发送模块空闲tx_ready <= 1'b0;                   //准备过程结束send_en  <= 1'b1;                   //拉高发送使能信号endend
endendmodule 

http://www.mrgr.cn/news/28078.html

相关文章:

  • Spring Boot框架:电商系统的技术革新
  • 基于RK3568+FPGA的1U/2U电力通讯管理机电力网关解决方案
  • layui的table组件中,对某一列的文字设置颜色为浅蓝怎么设置
  • 力扣 LeetCode 225. 用队列实现栈(Day5:栈与队列)
  • JDBC-Mysql 时区问题详解
  • 生成式GPT商品推荐:精准满足用户需求
  • 7-16 一元多项式求导(vector)
  • 笔记整理—内核!启动!—kernel部分(7)rcs文件和登录部分与密码解析
  • pip安装包、卸载包、更新包命令
  • 8.JMeter+Ant(基于工具的实现接口自动化,命令行方式)
  • CF 231 E Cactus 题解(仙人掌图上找环)
  • C Primer Plus 第5章习题
  • 数据清洗-缺失值填充-K-NN算法(K-Nearest Neighbors, K-NN算法)
  • 尚品汇-秒杀商品存入缓存、Redis发布订阅实现状态位(五十一)
  • 最少逆序对数量+dp
  • 鸿蒙 ArkUI组件二
  • Linux:进程状态和优先级
  • 基于51单片机的220V交流数字电流表proteus仿真
  • 差分进化算法(DE算法)求解实例---旅行商问题 (TSP)
  • 电脑硬盘被BitLocker,忘记秘钥
  • 仿先卜php阴盘奇门排盘的算法简述以及php的代码实现开源支持二开
  • Python进阶————迭代器与生成器
  • 《浔川社团官方联合会入驻 CSDN 公告》
  • java定时任务
  • 共享内存的理解
  • GDB调试