uart串口通信传输协议

一、Uart串口通信

        uart串口通信是一种异步串行全双工通信方式,tx端用于数据发送;rx端用于数据接收。信号线在空闲时为高电平。

        异步通信是按字符传输的。每传输一个字符就用起始位来收、发双方的同步。不会因收发双方的时钟频率的小的偏差导致错误。这种传输方式利用每一帧的起、止信号来建立发送与接收之间的同步。特点是:每帧内部各位均采用固定的时间间隔,而帧与帧之间的间隔时随即的。接收机完全靠每一帧的起始位和停止位来识别字符时正在进行传输还是传输结束。uart也是异步通信方式,数据发送会包装成数据帧的形式发送,数据帧的格式为:

        空闲时间为高电平,故rx端接收到低电平时,表示有数据开始发送,再根据波特率对数据进行接收。波特率:每秒传输二进制数据的位数,单位bps。

                若波特率为115200bps,即代表一秒钟需要传输115200个bit数据。

                1s=10e9ns,50MHz时钟周期为20ns,则传输一个bit所需的时间为

                                10e9/115200=8681ns

                则传输一个bit需要的时钟周期为8681/20=434个时钟周期。

        数据位的传输是串行从低位到高位传输,接收到的数据暂时存储在寄存器中,待接收完1字节的数据,通过串并转换保存接收到的数据。发送时通过tx信号线按照设置好的比特率将数据发送出去,数据发送仍要按照数据帧发送,先发送起始位,再从低位到高位发送数据。

        奇偶校验位:数据位加上校验位后,使得“1”的位数为偶数(偶校验)或者奇数(奇校验)。(一般都是无奇偶校验位的。)

        停止位:数据位传输完成后都会发送停止位,标志一个传输已经完成。(默认1位,可选择1.5位、2位。)

二、Verilog 代码

1.发送模块

module uart_tx(
	input clk,                     	        //系统时钟
	input rst_n,				//系统复位信号
	input [2:0]baud_set,			//波特率选择信号
	input [7:0]data_byte,			//并行数据
	input send_en,				//发送使能信号
	output reg rs_Tx,			//发送串行数据
	output reg tx_state, 			//uart正在发送信号		
	output reg tx_done 			//发送完成信号	
);


	reg [15:0]baud_temp;                     //波特率
	reg [15:0]baud_cnt;			//波特率分频器
	reg baud_clk;  				//波特率时钟
	reg [3:0]baud_clk_cnt; 			//波特率时钟计数器

//波特率选择
always @(posedge clk or negedge rst_n)begin
	if(!rst_n)
	  baud_temp <= 3'd0;
	else begin
	  case(baud_set)
	    3'd0:baud_temp <= 16'd5207;                 //波特率9600bps
	    3'd1:baud_temp <= 16'd2603;			//波特率19200bps
	    3'd2:baud_temp <= 16'd1301;			//波特率38400bps
	    3'd3:baud_temp <= 16'd867;			//波特率57600bps
	    3'd4:baud_temp <= 16'd433;			//波特率115200bps
	  default: baud_temp <= 16'd5207;
	  endcase
	end
end

//波特率分频计数器
always @(posedge clk or negedge rst_n)begin
	if(!rst_n)
	  baud_cnt <= 16'd0;
	else if(tx_state)begin                         	//开始发送后波特率分频计数器开始工作
	  if(baud_cnt == baud_temp)
	    baud_cnt <= 16'd0;
	  else
	    baud_cnt <= baud_cnt + 1'b1;
	end
	else
	  baud_cnt <= 16'd0;
end

//生成波特率时钟
always @(posedge clk or negedge rst_n)begin
	if(!rst_n)
	  baud_clk <= 1'b0;
	else if(baud_cnt == 1'b1)
	  baud_clk <= 1'b1;
	else
	  baud_clk <= 1'b0;
end

//对波特率时钟进行计数
always @(posedge clk or negedge rst_n)begin
	if(!rst_n)
	  baud_clk_cnt <= 4'd0;
	else if(baud_clk_cnt == 4'd11)
	  baud_clk_cnt <= 4'd0;
	else if(baud_clk)
	  baud_clk_cnt <= baud_clk_cnt + 1'b1;
	else
	  baud_clk_cnt <= baud_clk_cnt;
end


//发送完成标志
always @(posedge clk or negedge rst_n)begin
	if(!rst_n)
	  tx_done <= 1'b0;
	else if(baud_clk_cnt == 4'd11)               //波特率计数器记到4'd11时,发送完成
	  tx_done <= 1'd1;
	else
	  tx_done <= 1'd0;
end

//正在发送标志
always @(posedge clk or negedge rst_n)begin
	if(!rst_n)
	  tx_state <= 1'b0;
	else if(send_en)                            	//发送使能拉高时,发送开始
	  tx_state <= 1'b1;
	else if(baud_clk_cnt == 4'd11)
	  tx_state <= 1'd0;
	else
 	  tx_state <= tx_state;
end

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

//并行数据转变成串行发送
always @(posedge clk or negedge rst_n)begin
	if(!rst_n)
	  rs_Tx <= 1'b0;
	else begin
	  case(baud_clk_cnt)
	    0:rs_Tx <= 1'b1;
	    1:rs_Tx <= 1'b0;
	    2:rs_Tx <= data_byte_r[0];
	    3:rs_Tx <= data_byte_r[1];
	    4:rs_Tx <= data_byte_r[2];
	    5:rs_Tx <= data_byte_r[3];
	    6:rs_Tx <= data_byte_r[4];
	    7:rs_Tx <= data_byte_r[5];
	    8:rs_Tx <= data_byte_r[6];
	    9:rs_Tx <= data_byte_r[7];
	    10:rs_Tx <= 1'b1;
	  endcase
	end
end

endmodule
`timescale 1ns/1ps
`define clk_period 20
module uart_tx_tb();

	reg clk;
	reg rst_n;
	reg [2:0]baud_set;
	reg [7:0]data_byte;
	reg send_en;
	wire rs_Tx;
	wire tx_state;
	wire tx_done;

uart_tx u1(
	.clk(clk),
	.rst_n(rst_n),
	.baud_set(baud_set),
	.data_byte(data_byte),
	.send_en(send_en),
	.rs_Tx(rs_Tx),
	.tx_state(tx_state),
	.tx_done(tx_done)
	);

initial clk = 0;

always #(`clk_period/2)clk = ~clk;

initial begin
	rst_n = 1'b0;
	data_byte = 8'd0;
	send_en = 1'b0;
	baud_set = 3'd4;
	#(`clk_period*20+1)
	rst_n = 1'b1;
	#(`clk_period*50);
	data_byte = 8'hae;
	send_en = 1'b1;
	#`clk_period;
	send_en = 0;
	
	@(posedge tx_done)
	#(`clk_period*5000);
	data_byte = 8'hbc;
	send_en = 1'b1;
	#`clk_period;
	send_en = 1'b0;
	@(posedge tx_done)
	#(`clk_period*5000);
	$stop;
end
endmodule

仿真图

 2.接收模块

module uart_rx(
	input clk,                       	//系统时钟
	input rst_n,				//系统复位信号
	input rs_rx,				//输入接收到的串行数据
	input reg[2:0]baud_set,			//波特率选择信号
	output reg[7:0]r_data_byte,			//输出并行数据
	output reg rx_done				//接收完成信号
	);

	reg rs_rx_r1,rs_rx_r2;      		//同步寄存器
	reg rs_rx_temp1,rs_rx_temp2;		//数据寄存器
	wire nedege;				//判断起始信号
	
//此时输入信号相对与系统时钟是异步信号,需要对其进行同步处理

//同步寄存器,消除亚稳态
always @(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
	  rs_rx_r1 <= 1'b0;
	  rs_rx_r2 <= 1'b0;
	end
	else begin
	  rs_rx_r1 <= rs_rx;
	  rs_rx_r2 <= rs_rx_r1;
	end
end
//数据寄存
always @(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
	  rs_rx_temp1 <= 1'b0;
	  rs_rx_temp2 <= 1'b0;
	end
	else begin
	  rs_rx_temp1 <= rs_rx_r2;
	  rs_rx_temp2 <= rs_rx_temp1;
	end
end

assign nedege = (!rs_rx_temp1) && rs_rx_temp2;               //若为1,则输入了起始位



/*
	实际传输中,会有许多干扰,只采样一次的数据是很不可靠的。这里将每个数据平均分为16段,采样中间6段较为平稳的数据,进行累加,
		1-3'b001,2-3'b010,3-3'b011,4-3'b100,5-3'b101,6-3'b110.
	可见当采样数据有一半的状态为1时,最高位都为1,故以最高位来判断此时传输的数据
*/
	reg [15:0]baud_temp;                                 //波特率
	reg [15:0]baud_cnt;                                  //波特率分频计数器
	reg baud_clk;                                        //波特率时钟
	reg [7:0]baud_clk_cnt;                               //波特率时钟计数器

//波特率选择
//相比较发送模式的采样频率,接收模式的采样频率是其的16倍
always @(posedge clk or negedge rst_n)begin
	if(!rst_n)
	  baud_temp <= 16'd324;
	else begin
	  case(baud_set)
	    0:baud_temp <= 16'd324;
	    1:baud_temp <= 16'd162;
	    2:baud_temp <= 16'd80;
	    3:baud_temp <= 16'd53;
	    4:baud_temp <= 16'd26;
	  default : baud_temp <= 16'd324;
	  endcase
	end
end

	reg rx_state;                              	//正在传输信号

//正在传输数据时baud_cnt开始计数
always @(posedge clk or negedge rst_n)begin
	if(!rst_n)
	  baud_cnt <= 16'd0;
	 else if(rx_state)begin
	   if(baud_cnt == baud_temp)
		baud_cnt <= 16'd0;
	   else
		baud_cnt <= baud_cnt + 1'b1;
	  end
	  else
		baud_cnt <= 16'd0;
end

//
always @(posedge clk or negedge rst_n)begin
	if(!rst_n)
	  baud_clk <= 1'b0;
	else if(baud_cnt == 16'd1)
	  baud_clk <= 1'b1;
	else
	  baud_clk <= 1'b0;
end


	reg [2:0]data_byte_r [7:0];
	reg [2:0]START_BIT,STOP_BIT;

always @(posedge clk or negedge rst_n)begin
	if(!rst_n)
	  baud_clk_cnt <= 8'd0;
	else if(baud_clk_cnt == 8'd159 || ((baud_clk_cnt == 8'd12) && (START_BIT > 2)))//baud_clk_cnt计满时、或者是起始信号不为1时清零;
	  baud_clk_cnt <= 8'd0;
	else if(baud_clk)
	  baud_clk_cnt <= baud_clk_cnt + 1'b1;
	else
	  baud_clk_cnt <= baud_clk_cnt;
end


//传输完成信号
always @(posedge clk or negedge rst_n)begin
	if(!rst_n)
	  rx_done <= 1'b0;
	else if(baud_clk_cnt == 8'd159)
	  rx_done <= 1'b1;
	else
	  rx_done <= 1'b0;
end

//正在传输信号
always @(posedge clk or negedge rst_n)begin
	if(!rst_n)
	  rx_state <= 1'b0;
	else if(nedege)
	  rx_state <= 1'b1;
	else if(rx_done || (baud_clk_cnt == 8'd12 &&(START_BIT>2)))
	  rx_state <= 1'b0;
	else
	  rx_state <= rx_state;	
end

//计数完成时,串行数据转成并行数据

always @(posedge clk or negedge rst_n)begin
	if(!rst_n)
		r_data_byte <= 8'b0;
	else if(baud_clk_cnt == 8'd159)begin
		r_data_byte[0] <= data_byte_r[0][2];
		r_data_byte[1] <= data_byte_r[1][2];
		r_data_byte[2] <= data_byte_r[2][2];
		r_data_byte[3] <= data_byte_r[3][2];
		r_data_byte[4] <= data_byte_r[4][2];
		r_data_byte[5] <= data_byte_r[5][2];
		r_data_byte[6] <= data_byte_r[6][2];
		r_data_byte[7] <= data_byte_r[7][2];
	end
	else begin
		r_data_byte[0] <= data_byte_r[0];
		r_data_byte[1] <= data_byte_r[1];
		r_data_byte[2] <= data_byte_r[2];
		r_data_byte[3] <= data_byte_r[3];
		r_data_byte[4] <= data_byte_r[4];
		r_data_byte[5] <= data_byte_r[5];
		r_data_byte[6] <= data_byte_r[6];
		r_data_byte[7] <= data_byte_r[7];
	end
end

always @(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
	  START_BIT <= 3'd0;
	  data_byte_r[0] <= 3'd0;
	  data_byte_r[1] <= 3'd0;
	  data_byte_r[2] <= 3'd0;
	  data_byte_r[3] <= 3'd0;
	  data_byte_r[4] <= 3'd0;
	  data_byte_r[5] <= 3'd0;
	  data_byte_r[6] <= 3'd0;
	  data_byte_r[7] <= 3'd0;
	end
	else if(baud_clk)begin
	  case(baud_clk_cnt)
		0:begin
		START_BIT <= 3'd0;
	  	data_byte_r[0] <= 3'd0;
	  	data_byte_r[1] <= 3'd0;
	  	data_byte_r[2] <= 3'd0;
	  	data_byte_r[3] <= 3'd0;
	  	data_byte_r[4] <= 3'd0;
	 	data_byte_r[5] <= 3'd0;
	  	data_byte_r[6] <= 3'd0;
	  	data_byte_r[7] <= 3'd0;
		STOP_BIT <= 3'd0;
		end
		
		6,7,8,9,10,11:START_BIT <= START_BIT + rs_rx_r2;
		22,23,24,25,26,27:data_byte_r[0] <= data_byte_r[0] + rs_rx_r2;
		38,39,40,41,42,43:data_byte_r[1] <= data_byte_r[1] + rs_rx_r2;
		54,55,56,57,58,59:data_byte_r[2] <= data_byte_r[2] + rs_rx_r2;
		70,71,72,73,74,75:data_byte_r[3] <= data_byte_r[3] + rs_rx_r2;
		86,87,88,89,90,91:data_byte_r[4] <= data_byte_r[4] + rs_rx_r2;
		102,103,104,105,106,107:data_byte_r[5] <= data_byte_r[5] + rs_rx_r2;
		118,119,120,121,122,123:data_byte_r[6] <= data_byte_r[6] + rs_rx_r2;
		134,135,136,137,138,139:data_byte_r[7] <= data_byte_r[7] + rs_rx_r2;
		150,151,152,153,154,155:STOP_BIT <= STOP_BIT + rs_rx_r2;
		
		default : begin
		START_BIT <= START_BIT;
	  	data_byte_r[0] <= data_byte_r[0];
	  	data_byte_r[1] <= data_byte_r[1];
	  	data_byte_r[2] <= data_byte_r[2];
	  	data_byte_r[3] <= data_byte_r[3];
	  	data_byte_r[4] <= data_byte_r[4];
	 	data_byte_r[5] <= data_byte_r[5];
	  	data_byte_r[6] <= data_byte_r[6];
	  	data_byte_r[7] <= data_byte_r[7];
		STOP_BIT <= STOP_BIT;
		end
	  endcase
	end
end

endmodule
`timescale 1ns/1ps
`define clk_period 20

module uart_rx_tb();

	reg clk;
	reg rst_n;
	reg send_en;
	reg [2:0]baud_set;
	reg rs_rx;
	wire rs_Tx;
	wire tx_state;
	wire tx_done;
	wire rx_done;
	reg [7:0]data_byte;	
	wire [7:0]r_data_byte;
	uart_tx u1(
	.clk(clk),
	.rst_n(rst_n),
	.baud_set(baud_set),
	.data_byte(data_byte),
	.send_en(send_en),
	.rs_Tx(rs_Tx),
	.tx_state(tx_state),
	.tx_done(tx_done)
	);

	uart_rx u2(
	.clk(clk),
	.rst_n(rst_n),
	.rs_rx(rs_Tx),
	.baud_set(baud_set),
	.r_data_byte(r_data_byte),
	.rx_done(rx_done)
	);

	initial clk = 1;
	always #(`clk_period/2)clk = ~clk;
	
	initial begin
	
	rst_n = 1'b0;
	data_byte <= 8'd0;
	send_en = 1'b0;
	baud_set = 4'd4;
	
	#(`clk_period*20+1);
	rst_n = 1'b1;
	#(`clk_period*500);
	data_byte = 8'haa;
	send_en = 1;
	#(`clk_period);
	send_en = 0;
	
	@(posedge tx_done)
	#(`clk_period*500);
	data_byte = 8'h55;
	send_en = 1;
	#(`clk_period);
	send_en = 0;
	@(posedge tx_done)
	#(`clk_period*5000);
	$stop;
	end
endmodule

仿真图

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值