一、UART协议详解

1.1 基本概念

        通用异步接收器/收发器,通常成为UART,是一种串行、异步、全双工通信协议

1.2接口形式和电气标准

1.2.1 TTL 电平

参数 3.3V系统 5.0V系统
逻辑1(空闲) 3.3V 5.0V
逻辑0 0V 0V
传输距离 <1m <1m

1.2.2 RS232

参数 数值
逻辑1(空闲) -3v~-15v
逻辑0 +3v~+15v
典型电压 ±12v
最大速率 115200bps
传输距离 ≤15m
接口形式 DB9

RS232电平与TTL电平极性相反,需要采用芯片转换,最常用的芯片时MAX232

1.2.3 RS485

参数 数值
信号方式 差分信号(A、B 两线)
逻辑1 A − B > +200mV
逻辑0 A − B < −200mV
最大速率 10 Mbps(短距)
传输距离 最远 1200m(低速时)
挂载节点 最多 32 个(增强型可达 256)
通信方式 半双工(一对差分线)或全双工(两对)

RS-485 抗干扰能力强

1.3 UART数据帧结构

一个标准的UART数据帧通常由以下几个部分组成:

起始位(Start Bit):固定为逻辑 0(低电平),持续 1 bit 时间,告诉接收方"帧开始了"。

数据位(Data Bits):8 bit,从 LSB(D0)到 MSB(D7)依次发送,图中以 10110101 为例。

校验位(Parity Bit,可选):奇偶校验,可配置为 None / Even / Odd,

停止位(Stop Bit):固定为逻辑 1(高电平),1 bit 或 2 bit,帧结束后线路回归空闲。

1.4 数据传输

        异步通信中没有时钟信号,因此两个通信设备需要就波特率达成一致。常见的有9600、115200等。接收方根据约定的波特率来接收数据,在接收时,起始位会被检测到,并开始接收数据位,直到停止位为止。

1.5 信号线

UART通信协议通常需要两条线:

·TX(Transmit,传输线):发送端用来发送数据。

·RX  (Receive,接收线) :接收端用来接收数据。

这两个信号线连接在通信双方的设备之间,数据在这两条线中单向传输。

1.6 UART的工作过程

        (1)发送方:

  • 当数据准备好时,发送方将数据从并行形式转换为串行数据。
  • 在数据的前面,发送方会发送一个起始位(逻辑0),表示数据的开始。
  • 接着是数据位,通常是8位二进制数据。
  • 根据协议,发送方可能会发送一个校验位(如果启用的话)来进行错误检测。
  • 最后,发送停止位(通常是1位或2位逻辑1)来表示数据帧的结束。

        (2)接收方

  • 接收方在接收到起始位时开始接收数据。
  • 接收方按照规定的波特率逐位读取数据,直到接收到完整的数据帧。
  • 接收方检查校验位(如果存在)来确认数据是否正确。
  • 一旦接收到停止位,接收方认为数据传输完成,可以处理接收到的数据。

1.7 波特率和时钟同步

1.8 校验与错误检测

在UART通信中,为了减少数据传输中的错误,常常会使用校验位

  • 无校验:没有校验位,发送方和接收方无需进行任何错误检测。
  • 奇校验:校验位的选择使得数据位中“1”的数量为奇数。
  • 偶校验:校验位的选择使得数据位中“1”的数量为偶数。

1.9 UART与全双工和半双工

全双工:UART通常支持全双工通信,即发送和接收可以同时进行。发送方通过TX发送数据,接收方通过RX接收数据

半双工:某些情况下,UART可以配置为半双工模式,即只能在某一时刻进行发送或接收

二、Verilog实现UART发送

2.1 Verilog实现UART发送

串口发送模块整体框图

信号名称:

clk:时钟信号

data_byte:待发送数据

baud_set:波特率选择

send_en:发送使能信号

rst_n:复位信号

uart_tx:串口发送信号输出

tx_done:发送一字节数据结束信号,持续1clk的高电平

uart_state:串口发送状态,处于1表示串口正在处于发送状态

module Uart_Byte_tx
#(
	parameter CLK_FREQ=50_000_000		//时钟信号频率 单位Hz
)
(
	rst_n	,
	clk		,
	send_en	,
	baud_set,
	data_byte,
	uart_tx,
	tx_done,
	uart_state
);
	input	rst_n		;				//复位信号
	input	clk         ;               //时钟信号
	input	[2:0]baud_set    ;          //波特率选择
	input	[7:0]data_byte   ;          //待发送字节信号
	input	send_en			;           //发送使能信号
	output	reg uart_tx     ;           //输出信号
	output	reg tx_done     ;           //发送完成信号
	output	reg uart_state  ;           //发送状态,1:发送中

localparam START_BIT=0,
		   STOP_BIT =1;

//根据时钟频率 CLK_FREQ 计算波特率分频系数
wire	[15:0]baud_div_9600=CLK_FREQ/9600-1;
wire	[15:0]baud_div_19200=CLK_FREQ/19200-1;
wire	[15:0]baud_div_38400=CLK_FREQ/38400-1;
wire	[15:0]baud_div_57600=CLK_FREQ/57600-1;
wire	[15:0]baud_div_115200=CLK_FREQ/115200-1;

//根据baud_set;选择波特率
reg [15:0]bps_DR;
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
		bps_DR<=baud_div_9600;
	end
	else begin
		case(baud_set)
			3'b000:bps_DR<=baud_div_9600   ;
			3'b001:bps_DR<=baud_div_19200  ;
			3'b010:bps_DR<=baud_div_38400  ;
			3'b011:bps_DR<=baud_div_57600  ;
			3'b100:bps_DR<=baud_div_115200 ;
			default:bps_DR<=baud_div_9600  ;
		endcase
	end
end

reg	[15:0]div_cnt;				//波特率分频计数器
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
		div_cnt<=16'b0;
	end
	else if(uart_state)begin
		if(div_cnt==bps_DR)begin
			div_cnt<=16'b0;
		end
		else begin
			div_cnt<=div_cnt+1'b1;
		end
	end
	else begin
		div_cnt<=16'b0;
	end
end
reg bps_clk;				//波特率时钟
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
		bps_clk<=1'b0;
	end
	else if(div_cnt==16'd1)begin
		bps_clk<=1'b1;
	end
	else begin
		bps_clk<=1'b0;
	end
end

reg [3:0]bps_cnt;            //发送计数器
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
		bps_cnt<=4'b0;
	end
	else if(bps_cnt==4'd11)begin
		bps_cnt<=4'b0;
	end
	else if(bps_clk)begin
		bps_cnt<=bps_cnt+1'b1;
	end
	else begin
		bps_cnt<=bps_cnt;
	end
end

always@(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
		tx_done<=1'b0;
	end
	else if(bps_cnt==4'd11)begin        //发送完成,tx_done拉高一个时钟周期
		tx_done<=1'b1;
	end
	else begin
		tx_done<=1'b0;
	end
end

always@(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
		uart_state<=1'b0;
	end
	else if(send_en)begin               //发送过程中,为1 
		uart_state<=1'b1;
	end
	else if(bps_cnt==4'd11)begin         //发送完成,置 0 
		uart_state<=1'b0;
	end
	else begin
		uart_state<=uart_state;
	end
end

reg [7:0]data_byte_reg;                 //数据寄存器
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
		data_byte_reg<=8'b0;
	end
	else if(send_en)begin
		data_byte_reg<=data_byte;
	end
	else begin
		data_byte_reg<=data_byte_reg;
	end
end


always@(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
		uart_tx<=1'b0;
	end
	else begin
		case(bps_cnt)
			0 :uart_tx<=1'b1;
			1 :uart_tx<=START_BIT;
			2 :uart_tx<=data_byte_reg[0];
			3 :uart_tx<=data_byte_reg[1];
			4 :uart_tx<=data_byte_reg[2];
			5 :uart_tx<=data_byte_reg[3];
			6 :uart_tx<=data_byte_reg[4];
			7 :uart_tx<=data_byte_reg[5];
			8 :uart_tx<=data_byte_reg[6];
			9 :uart_tx<=data_byte_reg[7];
			10:uart_tx<=STOP_BIT;
			default:uart_tx<=1'b1;
		endcase
	end
end

endmodule

频率为50Mhz时,其test_bench和仿真结果如下:

`timescale 1ns/1ps
module test_bench();
reg	clk;
reg	rst_n;
reg	send_en;
reg	[2:0]baud_set;
reg	[7:0]data_byte;
wire	tx_done;
wire	uart_tx;
wire	uart_state;

Uart_Byte_tx	
#(
	.CLK_FREQ(50_000_000)		//时钟信号频率 单位Hz
)
Uart_Byte_tx_inst(
	.rst_n		(rst_n),
	.clk		(clk),
	.send_en(send_en),
	.baud_set(baud_set),
	.data_byte(data_byte),
	.uart_tx(uart_tx),
	.tx_done(tx_done),
	.uart_state(uart_state)
);

initial clk=1'b1;
always #10 clk=~clk;
initial begin
	rst_n=0;
	send_en=0;
	data_byte=0;
	#203;
	rst_n=1;
	#200;
	send_en=1;
	baud_set=3'b100;
	data_byte=8'h55;
	#20;
	send_en=0;
	
	@(posedge tx_done);
	#200;
	send_en=1;
	baud_set=3'b100;
	data_byte=8'haa;
	#20;
	send_en=0;
	
	@(posedge tx_done);
	#200;
	send_en=1;
	baud_set=3'b100;
	data_byte=8'h5c;
	#20;
	send_en=0;
	
	@(posedge tx_done);
	#20000;
	$stop;
	
end



endmodule

频率为100Mhz时,其test_bench和仿真结果如下:

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐