首页 > 其他分享 >串行通信协议--UART(Universal Asynchronous Receiver/Transmitter,通用异步收发传输器)

串行通信协议--UART(Universal Asynchronous Receiver/Transmitter,通用异步收发传输器)

时间:2024-08-06 18:23:41浏览次数:19  
标签:发送 Transmitter -- 通信协议 奇偶校验 UART 波特率 reg 时钟

一、UART 简介

  UART广泛应用于微控制器和计算机之间的数据通信,如GPS模块、蓝牙模块、GSM模块等。UART是一种通用串行数据总线,用于异步通信,该总线双向通信,可以实现全双工传输和接收。 在嵌入式设计中,UART用于主机与辅助设备通信UART通常被集成于其他通讯接口的连结上。UART 通道有两条数据线:每个设备上都有一个 RX 引脚和一个 TX 引脚(RX 用于接收,TX 用于发送)每个设备的 RX 引脚都连接到另一个设备的 TX 引脚。
  UART 是通用异步接收方发送方,没有时钟线。 异步通信以一个字符为传输单位:通信中两个字符之间的时间间隔不固定,但同一字符中两个相邻位之间的时间间隔是固定的。此时,需要在两个UART设备上指定相同的传输速率,以及空闲位、起始位、奇偶校验位和结束位,即遵循相同的协议。
在这里插入图片描述

二、UART 数据结构

  在UART中,传输模式为数据包形式。连接发送器和接收器的机制包括串行数据包的创建和物理硬件线路的控制。数据包由起始位、数据帧、奇偶校验位和停止位组成。

  • 起始位:由1个逻辑 0 的数据位表示
  • 有效数据:在起始位后紧接着的就是有效数据,有效数据的长度常被约定为 5、 6、 7 或 8 位长
  • 结束位:由 0.5、1、1.5 或 2 个逻辑 1 的数据位表示
  • 校验位:可选,采用奇偶校验方法
  • 波特率:常见的波特率为4800、 9600、 115200,具体实现要结合系统时钟进行计算
    在这里插入图片描述
    UART传输电气标准
      UART使用电子电路中常使用 TTL 的电平标准,理想状态下使用 5V 表示二进制逻辑 1,使用 0V 表示逻辑 0。
      为了扩展传输距离和应用范围,使用RS232标准,RS232接口通常采用DB9或DB25的形式,以DB9最常见。RS232采用负逻辑,为了增加串口通讯的远距离传输及抗干扰能力,它使用 -3 ~ -15V 表示逻辑 1+3 ~ +15V 表示逻辑 0,传输距离最高可达15m。

2.1 起始位

  当不传输数据时,UART数据传输线通常保持高电压电平。若要开始数据传输,发送UART会将传输线从高电平拉到低电平并保持1个时钟周期。当接收UART检测到高到低电压跃迁时,便开始以波特率对应的频率读取数据帧中的位。
在这里插入图片描述

2.2 数据帧

  数据帧包含所传输的实际数据。如果使用奇偶校验位,数据帧长度可以是5位到8位。如果不使用奇偶校验位,数据帧长度可以是9位。在大多数情况下,数据以最低有效位优先方式发送。
在这里插入图片描述

2.3 奇偶校验

  奇偶性描述数字是偶数还是奇数。通过奇偶校验位,接收UART判断传输期间是否有数据发生改变。电磁辐射、不一致的波特率或长距离数据传输都可能改变数据位。
  接收UART读取数据帧后,将计数值为1的位,检查总数是偶数还是奇数。如果奇偶校验位为0(偶数奇偶校验),则数据帧中的1或逻辑高位总计应为偶数。如果奇偶校验位为1(奇数奇偶校验),则数据帧中的1或逻辑高位总计应为奇数。当奇偶校验位与数据匹配时,UART认为传输未出错。但是,如果奇偶校验位为0,而总和为奇数,或者奇偶校验位为1,而总和为偶数,则UART认为数据帧中的位已改变。
在这里插入图片描述

2.4 停止位

  为了表示数据包结束,发送UART将数据传输线从低电压驱动到高电压并保持1到2位时间。
在这里插入图片描述

三、UART 串口通信FPGA实现

3.1 UART 接收代码

module	UART_SRX(Clk,Rst,Rxd,RxRdy,RxErr,Do);

input		Clk,
input		Rst;
input		Rxd;

output[7:0]Do;
output		RxRdy,RxErr;

reg	[13:0]	count;
reg [2:0]	state;
reg	[7:0]	Do;
reg [23:0]	data_sam;
reg	[1:0]	RX_BIT_COUNT; 
reg	[2:0]	RX_START_COUNT;
reg [5:0]	RX_REC_COUNT;
reg			Clk_SAM;
reg			RxRdy;
reg			RxErr;
reg			Rxd_pr;

parameter	RX_IDLE		= 3'b000, //状态机实现
			RX_START	= 3'b001,
			RX_REC		= 3'b010,
			RX_STOP		= 3'b011,
			RX_DO		= 3'b100;

//parameter define
parameter CLK_FREQ = 24576000;					//系统时钟频率
parameter UART_BPS = 300;						//串口波特率
localparam BoundCnt = CLK_FREQ/UART_BPS/6-1;	//Fo = Bound*3 = 0.5*Fs/BoundCnt

always@(posedge Clk or posedge Rst)begin
	if(Rst)begin
		count <= 14'h0;
	end
	else begin
		if(count == BoundCnt)begin //波特率计数时钟
			count <= 14'h0;
			Clk_SAM <= ~Clk_SAM;
		end
		else begin
			count <= count + 14'h1;
		end
	end
end

always @(posedge Clk_SAM or posedge Rst) begin
	if(Rst)	begin
		Do <= 8'hff;
		RxRdy <= 1'b0;
		RxErr <= 1'b0;
		RX_START_COUNT <= 2'h0;
		RX_REC_COUNT <= 5'h0;
		data_sam[23:0]<= 24'b0;
		state <= RX_IDLE;
	end
	else begin
		case(state)
			RX_IDLE:begin //接收初始化
				RxRdy <= 1'b0;
				RxErr <= 1'b0;
				RX_START_COUNT <= 2'h0;
				RX_REC_COUNT <= 5'h0;
				if(Rxd == 0)begin
					state <= RX_START;
				end
			end
			RX_START:begin //接收起始位
				Rxd_pr <= Rxd;
				if(!(Rxd && Rxd_pr))begin
					RX_START_COUNT <= RX_START_COUNT + 2'h1;
					if(RX_START_COUNT == 1)begin //校验是否开启
						RX_START_COUNT <= 2'h0;
						state <= RX_REC;
					end
					else begin
						state <= RX_START;
					end
				end
				else begin
					state <= RX_IDLE;
				end
			end
			RX_REC:begin //校验起始
				data_sam[RX_REC_COUNT] <= Rxd;
				RX_REC_COUNT <= RX_REC_COUNT + 5'h1;
				if(RX_REC_COUNT == 23) begin
					state <= RX_STOP;
				end
			end
			RX_STOP:begin
				Do[0] <= ((data_sam[0]+data_sam[1]+data_sam[2])>1)?1'b1:1'b0;
				Do[1] <= ((data_sam[3]+data_sam[4]+data_sam[5])>1)?1'b1:1'b0;
				Do[2] <= ((data_sam[6]+data_sam[7]+data_sam[8])>1)?1'b1:1'b0;
				Do[3] <= ((data_sam[9]+data_sam[10]+data_sam[11])>1)?1'b1:1'b0;
				Do[4] <= ((data_sam[12]+data_sam[13]+data_sam[14])>1)?1'b1:1'b0;
				Do[5] <= ((data_sam[15]+data_sam[16]+data_sam[17])>1)?1'b1:1'b0;
				Do[6] <= ((data_sam[18]+data_sam[19]+data_sam[20])>1)?1'b1:1'b0;
				Do[7] <= ((data_sam[21]+data_sam[22]+data_sam[23])>1)?1'b1:1'b0;
				state <= RX_DO;
				RxErr <= !Rxd;
			end
			RX_DO:begin
				RxRdy <= 1'b1;
				state <= RX_IDLE;
			end
		endcase
	end
end
endmodule

3.2 UART 发送代码

module UART_TX(
    input               CLK    , //系统时钟
    input               RST_n  , //系统复位,低有效
    input               TX_EN  , //UART的发送使能
    input     [7:0]     TX_DATA, //UART要发送的数据
    output  reg         TXD    , //UART发送端口
    output  reg         TX_BUSY  //发送忙状态信号
    );

//parameter define
parameter CLK_FREQ = 50000000;               //系统时钟频率
parameter UART_BPS = 115200  ;               //串口波特率
localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS; //为得到指定波特率,对系统时钟计数BPS_CNT次

//reg define
reg  [7:0]  tx_data_t;  //发送数据寄存器
reg  [3:0]  tx_cnt   ;  //发送数据计数器
reg  [15:0] baud_cnt ;  //波特率计数器

//当TX_EN为高时,寄存输入的并行数据,并拉高BUSY信号
always @(posedge CLK or negedge RST_n) begin
    if(!RST_n) begin
        tx_data_t <= 8'b0;
        TX_BUSY <= 1'b0;
    end
    //发送使能时,寄存要发送的数据,并拉高BUSY信号
    else if(TX_EN) begin
        tx_data_t <= TX_DATA;
        TX_BUSY <= 1'b1;
    end
    //当计数到停止位结束时,停止发送过程
    else if(tx_cnt == 4'd9 && baud_cnt == BAUD_CNT_MAX - BAUD_CNT_MAX/16) begin
        tx_data_t <= 8'b0;     //清空发送数据寄存器
        TX_BUSY <= 1'b0;  	   //并拉低BUSY信号
    end
    else begin
        tx_data_t <= tx_data_t;
        TX_BUSY <= TX_BUSY;
    end
end

//波特率的计数器赋值
always @(posedge CLK or negedge RST_n) begin
    if(!RST_n) 
        baud_cnt <= 16'd0;
    //当处于发送过程时,波特率计数器(baud_cnt)进行循环计数
    else if(TX_BUSY) begin
        if(baud_cnt < BAUD_CNT_MAX - 1'b1)
            baud_cnt <= baud_cnt + 16'b1;
        else 
            baud_cnt <= 16'd0; //计数达到一个波特率周期后清零
    end    
    else
        baud_cnt <= 16'd0;     //发送过程结束时计数器清零
end

//tx_cnt进行赋值
always @(posedge CLK or negedge RST_n) begin
    if(!RST_n) 
        tx_cnt <= 4'd0;
    else if(TX_BUSY) begin             //处于发送过程时tx_cnt才进行计数
        if(baud_cnt == BAUD_CNT_MAX - 1'b1) //当波特率计数器计数到一个波特率周期时
            tx_cnt <= tx_cnt + 1'b1;        //发送数据计数器加1
        else
            tx_cnt <= tx_cnt;
    end
    else
        tx_cnt <= 4'd0;                     //发送过程结束时计数器清零
end

//根据tx_cnt来给uart发送端口赋值
always @(posedge CLK or negedge RST_n) begin
    if(!RST_n) 
        TXD <= 1'b1;
    else if(TX_BUSY) begin
        case(tx_cnt) 
            4'd0 : TXD <= 1'b0        ; //起始位
            4'd1 : TXD <= tx_data_t[0]; //数据位最低位
            4'd2 : TXD <= tx_data_t[1];
            4'd3 : TXD <= tx_data_t[2];
            4'd4 : TXD <= tx_data_t[3];
            4'd5 : TXD <= tx_data_t[4];
            4'd6 : TXD <= tx_data_t[5];
            4'd7 : TXD <= tx_data_t[6];
            4'd8 : TXD <= tx_data_t[7]; //数据位最高位
            4'd9 : TXD <= 1'b1        ; //停止位
            default : TXD <= 1'b1;
        endcase
    end
    else
        TXD <= 1'b1;                    //空闲时发送端口为高电平
end
endmodule

四、USART – 扩展UART

USART 通用同步异步收发器(Universal Synchronous Asynchronous Receiver and Transmitter)
STM32内部的USART控制器学习

  USART是一种扩展了UART功能的通信接口,它不仅支持异步通信(如UART),还支持同步通信模式。USART可以在同一硬件上实现两种不同的通信方式,提供了更大的灵活性,广泛应用于需要稳定和高速数据传输的场合。

  1. USART的主要功能
    异步模式: 与UART功能相同,用于无需时钟信号的点对点通信。
    同步模式: 需要外部时钟信号的同步通信方式,适用于高数据速率和需要精确时序控制的场合。
  2. USART与UART的区别
    同步模式: USART可以工作在同步模式,而UART只能工作在异步模式。
    时钟信号: 在同步模式下,USART需要一个时钟信号进行数据同步,而UART则不需要。
    灵活性: USART提供了更多的配置选项和更高的灵活性,适用于更多种类的通信需求。

- 异步模式通信过程
起始位:发送端发送一个低电平信号,通知接收端即将传输数据。
数据位:发送端按预定的波特率逐位发送数据,接收端根据相同波特率接收数据。
校验位:可选,用于检测数据传输中的错误。
停止位:发送端发送一个高电平信号,表示数据传输结束。
- 同步模式通信过程
时钟信号:发送端发送时钟信号,接收端根据时钟信号的边缘(上升沿或下降沿)同步接收数据。
数据位:每个数据位在时钟信号的一个边缘发送或接收。
帧格式:可以根据应用需求配置起始位、数据位、校验位和停止位。

标签:发送,Transmitter,--,通信协议,奇偶校验,UART,波特率,reg,时钟
From: https://blog.csdn.net/qq_45389511/article/details/140956945

相关文章

  • C语言:qsort详解
    在上一篇文章我们大致的了解了回调函数的用法和作用,在这一篇让我们来了解一下在回调函数qsort的使用吧。一.qsortqsort是一种用来排各种类型数据的函数,利用的是快速排序的方式。说到排序,我们就想到了之前学习的冒泡排序,但冒泡排序也有很明显的缺点:时间复杂度太高,效率慢,但qsor......
  • Jmeter SHA512接口加密测试
    前言:最近,我遇到一些测试接口必须传入经过SHA512加密后的sign签名,并且签名有1小时时间限制,即签名不是一成不变超1小时就会过期,这导致在测试过程中就得频繁手工去更新签名。其实Jmeter是有提供函数去进行自动转换的,以下详解SHA512加密,可以去网上搜索SHA512在线转换 1、已知,接口......
  • springblade技术架构
    1.前后端的下载运行与对接SpringBlade源码下载地址https://gitee.com/smallc/SpringBlade打开终端,事先准备好一个空文件夹创建project文件夹在project文件夹下创建cloud、boot、vue文件夹进入cloud执行gitclone命令gitclonehttps://gitee.com/smallc/SpringBlade.git下......
  • 以“小”见“大” 打开“折叠”的世界
    「玩出个性,玩出潮流。」 他来了!它来了!昨晚,在年轻人的聚集地——B站,易烊千玺携手novaFlip亮相新生之夜。带着“新一代”和“潮流”两个标签,这一款小折叠,为华为的折叠屏手机带来更多的可能性,也将激活整个折叠屏手机市场。待激活的小折叠经过几年的低迷期之后,今年的智能手......
  • Mac开发基础24-NSToolbar
    NSToolbar是macOS应用中的一个重要控件,用于创建窗口顶部的工具栏。工具栏通常包含按钮和其他控件,用户可以通过这些控件快速访问常用功能。NSToolbar和NSToolbarItem协同工作,NSToolbar是工具栏容器,而NSToolbarItem是工具栏项。下面我们详细介绍NSToolbar的常见API和基......
  • OpenAI 重大人事变动,联创加入死敌;阿里视频框架 Tora 操控物体运动轨迹丨 RTE 开发者日
      开发者朋友们大家好: 这里是「RTE开发者日报」,每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享RTE(Real-TimeEngagement)领域内「有话题的新闻」、「有态度的观点」、「有意思的数据」、「有思考的文章」、「有看点的会议」,但内容仅代表编辑......
  • Mac开发基础25-NSAlert
    NSAlert是macOS应用中的一个重要控件,用于显示警告与通知对话框。NSAlert允许开发者创建和配置弹出窗口,用于通知用户、确认操作或显示错误信息。基本使用创建和显示简单的警告框Objective-C#import<Cocoa/Cocoa.h>//实例化NSAlertNSAlert*alert=[[NSAlertalloc]......
  • linux下的临时目录
    是的,Linux系统中有临时目录,主要用于存储临时文件。常见的临时目录包括:/tmp:这是最常用的临时目录,通常所有用户都可以在这里创建临时文件。系统在启动时可能会清空这个目录中的文件,或者在定期清理时移除未使用的文件。/var/tmp:与 /tmp 相似,但 /var/tmp 通常用于存放需要......
  • 一文带你玩转全新采集配置 CRD:AliyunPipelineConfig
    作者:玄飏既然是一文玩转,自然要讲些背景1.1. 什么是 iLogtail 采集配置长话短说:SLS:阿里云日志服务,一站式提供数据采集、加工、查询与分析、可视化、告警、消费与投递等功能,全面提升您在研发、运维、运营、安全等场景的数字化能力。iLogtail:SLS推出的一款可观测数据采集器......
  • 【BUUCTF】Blacklist
    【BUUCTF】Blacklist(SQL注入)题目来源收录于:BUUCTFGYCTF2020题目描述纯粹的SQL注入题当触发黑名单时返回如下过滤了以下关键字setpreparealterrenameselectupdatedeletedropinsertwhere.题解发现可以进行堆叠注入?inject=1';showdatabases;爆表......