UART串口通信协议——UART串口发送一个字节

UART串口发送一个字节

一、简介

1、 什么是UART?

UART(Universal Asynchronous Receiver/Transmitter) 代表通用异步收发器(异步串行通信协议中的一种)。它是微控制器内部的硬件外围设备。能够将传入和传出的数据转换为串行二进制流。使用串行到并行转换的方式,能将从外围设备接收的8位串行数据(8位)转换为并行形式(一个字节)。

UART通用串行数据总线可双向通信,也可全双工传输和接收(如:rs232,两条信号线组成)。


2、什么是波特率?

由于设备的发送端和接收端需要采用同样的速率,防止数据的丢失,于是在这里提出了波特率作为信号的传输速率,即计算机在串口通信时的速率。波特率bps:每秒钟传送的码元数(一个数字脉冲)

常见的波特率有:9600、115200……
如下表,系统时钟周期20ns,因此当波特率=9600时,波特率周期 = 1s / 9600 = 104167ns ,计数次数=(104167/20-1 = 5208-1
在这里插入图片描述


3、 UART串口通信协议

UART作为异步串口通信协议的一种,工作原理是将数据的每一个字符一位一位地传输。其中包括起始位(START低电平)、数据位(Bit0-7)、终止位(STOP高电平)。
在这里插入图片描述


二、串口接收和发送模块

如下架构来完成设备间的信息交互。串并转换是接收和发送模块的基本功能,其中接收模块是串行数据转换成并行,发送模块是并行数据转换成串行。
在这里插入图片描述

三、串口发送一个字节模块设计

在这里插入图片描述

发送模块内部结构:

包括主要部件:1.生成波特率查找表(DR_LUT) ; 2.生成波特率计数器(Div_cnt、bos_cnt); 3.Tx数据发送模块 (MUX10) ;

在这里插入图片描述

verilog编写UART发送模块

 

module uart_tx_1(
    input  clk,
    input  rst_n,
    input  [7:0] uart_data,
    input  [2:0] uart_sel,
    input  data_en,

    output reg tx_data
);

    reg   [7:0] uart_data_reg; //寄存一拍后的数据
    reg   [12:0] bps_cnt_max; //波特率计数最大值
    reg   [12:0] div_cnt;
    reg   cnt_flag;
    reg   TX_Done;
    reg   [3:0]bps_cnt;
	 reg   uart_state;


//避免数据丢失或错误,寄存
always @(posedge clk or negedge rst_n) begin
	if (!rst_n) begin
		uart_data_reg <= 8'b0;
	end
	else if (uart_state) begin
	    uart_data_reg <= uart_data;
	end
	else begin
		uart_data_reg <= uart_data_reg;
	end 
end

//设计波特率查找表
always @(posedge clk or negedge rst_n) begin
	if (!rst_n) begin
		bps_cnt_max <= 13'b0;
	end
	else begin
	    case(uart_sel)
	    	0: bps_cnt_max <= 13'd5207;  //9600
	    	1: bps_cnt_max <= 13'd2603;  //19200
	    	2: bps_cnt_max <= 13'd1301;  //38400
			3: bps_cnt_max <= 13'd867;   //57600
			4: bps_cnt_max <= 13'd433;   //115200
			default:bps_cnt_max <= 13'd5207;
	    endcase
	end
end
//发送数据有效信号(10位)
always @(posedge clk or negedge rst_n) begin
	if (!rst_n) begin
		uart_state <= 1'b0;
	end
	else if (data_en) begin
		uart_state <= 1'b1;
	end
	else if (TX_Done)begin  //当10位发送完成时 TX_Done为高电平
		uart_state <= 1'b0;
	end
	else begin
		uart_state <= uart_state;
	end
end

//设计分频计数器
always @(posedge clk or negedge rst_n) begin
	if (!rst_n) begin
		div_cnt <= 13'b0;
	end
	else if (uart_state) begin
	    if (div_cnt == bps_cnt_max)begin
	    	div_cnt <= 13'b0;
		 end
	    else begin
	    	div_cnt <= div_cnt + 1;
		 end
	end
	 else begin
	 	div_cnt <= 13'b0;
	 end	
end

//每div_cnt计数到1的时候发出一个高脉冲,即每一位数据发送的标志信号
always @(posedge clk or negedge rst_n) begin
	if (!rst_n) begin
	    cnt_flag <= 1'b0;
	end
	else if (div_cnt == 1) begin
		cnt_flag <= 1'b1;
	end
	else begin
		cnt_flag <= 1'b0;
	end
end
//产生bps_cnt
always @(posedge clk or negedge rst_n) begin
	if (!rst_n) begin
		bps_cnt <= 4'b0;
	end
	else if (uart_state)begin
	    if (cnt_flag)begin
		     bps_cnt <= bps_cnt + 1;
	    end
	    else begin
	        bps_cnt <= bps_cnt;
	    end
  end
	else begin
		bps_cnt <= 0;
	end
end
//产生TX_Done信号
always @(posedge clk or negedge rst_n) begin
	if (!rst_n) begin
		TX_Done <= 1'b0;	
	end
	else if (bps_cnt == 4'd10) begin //计数到11的原因是得到一个完整的计数值9
		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
		tx_data <= 1'b1;
	end
	else if (uart_state)begin
	    case(bps_cnt)
	        0: tx_data <= 1'b1; //闲置位
			  1: tx_data <= 1'b0; //开始位
	        2: tx_data <= uart_data_reg[0]; 
	        3: tx_data <= uart_data_reg[1];
	        4: tx_data <= uart_data_reg[2];
	        5: tx_data <= uart_data_reg[3];
	        6: tx_data <= uart_data_reg[4];
	        7: tx_data <= uart_data_reg[5];
	        8: tx_data <= uart_data_reg[6];
	        9: tx_data <= uart_data_reg[7];
	        10: tx_data <= 1'b1;//结束位
	        default:tx_data <= 1'b1;	  	  
	    endcase
	end

end

endmodule 

tesebench测试文件:

`timescale 1ns/1ns
`define clk_period 20
module uart_tx_1_tb;

    reg  clk=1  ;
    reg  rst_n;
    reg  [7:0] uart_data;
    reg  [2:0] uart_sel ;
    reg  data_en;

    wire tx_data;

	uart_tx_1 inst_uart_tx(
	
			.clk       (clk),
			.rst_n     (rst_n),
			.uart_data (uart_data),
			.uart_sel  (uart_sel),
			.data_en   (data_en),
			.tx_data   (tx_data)
			
		);
//产生50Mhz时钟
always#(`clk_period/2)  clk = ~clk;
//初始化输入信号
initial begin
    rst_n     = 1'b0;
	 uart_data = 8'b0;
	 uart_sel  = 3'd4; //115200
	 data_en   = 1'b0;
	 
	 #(`clk_period*200+1)
	 rst_n     = 1'b1;
	 #(`clk_period*100+1)
	 data_en   = 1'b1;
	 uart_data = 8'b0101_0111;
	 #(`clk_period*50)//延迟200ns
	 data_en=1'd0;
	  
//等待完成重新发送	  
	  #(`clk_period*5000)
	  uart_data = 8'b1101_0100;
	  data_en=1'd1;
	  #(`clk_period*50)
	  data_en=1'd0;	  
	  #(`clk_period*5000)
     $stop;
  end
endmodule 

波形分析

当我们输入8位的01010111时,发送的时候会一位一位的发送,开始位+低位开始的数据位+停止位,也就是start0 +11101010 + stop1 ,可看到tx_data输出为0 + 11101010 + 1 ;同理第二个字节发送时,tx_data输出为0 + 00101011 + 1
在这里插入图片描述

四、接收模块设计

由于UART是异步通信,也就是接收和发送采用不同的时钟,因此涉及到跨时钟域处理的问题。如下将输入打三拍来降低亚稳态现象出现。

接收模块的verilog编写:

//接收 串转并
module uart_rx_1(
	input clk,
	input rst_n,
	input uart_rx,

	output reg done,
	output reg [7:0] data
	);

reg uart_rx_1;
reg uart_rx_2;
reg uart_rx_3;
reg [7:0] rx_data;

reg        state;
wire       start_neg;
reg [12:0] bps_cnt;
reg [3:0]  bit_cnt;
reg        bit_flag;

//针对异步亚稳态,打三拍寄存
always @(posedge clk or negedge rst_n) begin
	if (!rst_n) begin
		uart_rx_1 <= 1'b0;
		uart_rx_2 <= 1'b0;
		uart_rx_3 <= 1'b0;
		
	end
	else  begin
		uart_rx_1 <= uart_rx;
		uart_rx_2 <= uart_rx_1;
		uart_rx_3 <= uart_rx_2;		
	end
end
//下降沿检测
assign start_neg = (uart_rx_3 & ~uart_rx_2);

//生成state信号,指示工作有效状态
always @(posedge clk or negedge rst_n) begin
	if (!rst_n) begin
		state <= 1'b0;
	end
	else if (start_neg) begin
		state <= 1'b1;
	end
	else if (done) begin
		state <= 1'b0;
	end
	else begin
		state <= state;
	end
end

//生成波特率计数器 bps_cnt
always @(posedge clk or negedge rst_n) begin
	if (!rst_n) begin
		bps_cnt <= 1'b0;
	end
	else begin
	    if (bps_cnt == 13'd433) begin
		    bps_cnt <= 1'b0;
	    end
	    else begin
	    	bps_cnt <= bps_cnt + 1'b1;
	    end
    end
end
//生成bit_flag
//中间时刻接收数据 更稳定
always @(posedge clk or negedge rst_n) begin
	if (!rst_n) begin
		bit_flag <= 1'b0;
	end
	else if(state)begin 
	    if (bps_cnt == 13'd216) begin
		     bit_flag <= 1'b1;
	    end
		 else
		     bit_flag <= 1'b0;
	end
	else begin
		bit_flag <= 1'b0;
	end
end
//生成bit_cnt,指示每一位发送什么数据
always @(posedge clk or negedge rst_n) begin
	if (!rst_n) begin
		bit_cnt <= 4'b0;
	end
	else if (bit_flag) begin
		if (bit_cnt == 4'd10) begin
			bit_cnt <= 1'b0;
		end
		else begin
			bit_cnt <= bit_cnt + 1'b1;
		end
	end
	else begin
		bit_cnt <= bit_cnt;
	end
end
//生成rx_data
always @(posedge clk or negedge rst_n) begin
	if (!rst_n) begin
		rx_data <= 8'b0;
	end
	else if (bit_flag) begin
		case(bit_cnt)
		    4'd0: rx_data <= 8'b0;
		    4'd1: rx_data[0] <= uart_rx_3;
		    4'd2: rx_data[1] <= uart_rx_3;
		    4'd3: rx_data[2] <= uart_rx_3;
		    4'd4: rx_data[3] <= uart_rx_3;
		    4'd5: rx_data[4] <= uart_rx_3;
		    4'd6: rx_data[5] <= uart_rx_3;
		    4'd7: rx_data[6] <= uart_rx_3;
		    4'd8: rx_data[7] <= uart_rx_3;
		    4'd9: rx_data <= rx_data;
		    default: rx_data <= 8'b0;		   		    
		endcase
	end
	else begin
		rx_data <= rx_data;
	end
end
//生成done信号
always @(posedge clk or negedge rst_n) begin
	if (!rst_n) begin
		done <= 1'b0;
	end
	else if (bit_flag && (bit_cnt == 4'd10))begin
	   done <= 1'b1;
	end
	else begin
	    done <= 1'b0;
   end
end	
//数据寄存
always @(posedge clk or negedge rst_n) begin
	if (!rst_n) begin
		data <= 1'b0;
	end
	else if(bit_flag && (bit_cnt == 4'd10))begin
	   data <= rx_data;
	end
	else
	    data <= data;
end
endmodule 

tb测试:

`timescale 1ns/1ns

module uart_rx_tb;

	reg clk;
	reg rst_n;
	reg uart_rx;

	wire       done;
	wire [7:0] data;


//模块例化
	uart_rx_1 inst_uart_rx
		(
			.clk     (clk),
			.rst_n   (rst_n),
			.uart_rx (uart_rx),
			.done    (done),
			.data (data)
		);

//生成时钟
initial clk = 1;
always #10 clk = ~clk;

//输入信号
initial begin
    rst_n = 1'b0;
	 uart_rx = 1'b0;
	 #200;
	 rst_n = 1'b1;
	 #1000;
	 uart_rx = 1'b1;
	 #20000;
	 uart_rx = 1'b0;
	 #20000;
	 uart_rx = 1'b1;
	 #20000;
	 uart_rx = 1'b0;	
	 #20000;
	 uart_rx = 1'b1;	
	 #10000;
	 uart_rx = 1'b0;	
	 #10000;
	 uart_rx = 1'b1; 
	 #20000;
	 uart_rx = 1'b0;	
	 #20000;
	 uart_rx = 1'b1;	
	 #30000;
	 uart_rx = 1'b0;	
	 #1000;
	 uart_rx = 1'b1;
	 #200000;
	 $stop;
end

endmodule 

波形:
在这里插入图片描述


发送模块(简化)

module uart_tx(
    input       clk,
	 input       rst_n,
	 input       start,
	 input [7:0] data,
	 
	 output  reg    uart_tx,
	 output  reg    done
);

    reg   [7:0] r_data;
	 reg         state;
	 reg   [9:0] baud_cnt;
	 reg   [3:0] bit_cnt;

//参数定义
	 parameter BAUD_CNT_MAX = 433; //波特率最大计数值
	 
//数据寄存一拍
always@(posedge clk or negedge rst_n)
    if(!rst_n)
	     r_data <= 0;
	  else if(start) 
	     r_data <= data;
	  else
	     r_data <= r_data; 

//生成state信号,表示传输有效阶段
always@(posedge clk or negedge rst_n)
    if(!rst_n)
	     state <= 1'b0;
	  else if(start)
	     state <= 1'b1;
	  else if(done)  
	     state <= 1'b0;
	  else
	     state <= state;
//生成baud_cnt计数器
always@(posedge clk or negedge rst_n)
    if(!rst_n)
	     baud_cnt <= 10'b0;
	  else if(state)
	      if(baud_cnt == BAUD_CNT_MAX)
			   baud_cnt <= 10'b0;
			else
		      baud_cnt <= baud_cnt + 1'b1; 	
	  else
	     baud_cnt <= 10'b0; 
		  
//生成bit_cnt计数器,用于计数每一位	      
always@(posedge clk or negedge rst_n)
     if(!rst_n)	    
	     bit_cnt <= 4'b0;
	  else if(state && (baud_cnt == 10'd1)) 
	     bit_cnt <= bit_cnt + 1'b1; 
	  else if(bit_cnt == 10)
	      bit_cnt <= 4'b0;
	  else
	      bit_cnt <= bit_cnt;
//uart_tx
always@(posedge clk or negedge rst_n)
     if(!rst_n)
	      uart_tx <= 1;
		else if(state)begin
		   case(bit_cnt)
			    4'd1: uart_tx <= 0; //开始位
				 4'd2: uart_tx <= r_data[0]; 
				 4'd3: uart_tx <= r_data[1]; 
				 4'd4: uart_tx <= r_data[2]; 
				 4'd5: uart_tx <= r_data[3];
				 4'd6: uart_tx <= r_data[4]; 
				 4'd7: uart_tx <= r_data[5];	
				 4'd8: uart_tx <= r_data[6]; 
				 4'd9: uart_tx <= r_data[7];	
				 default: uart_tx <= 1;
			endcase
		
		end
		else
		    uart_tx <= uart_tx;
//done信号
always@(posedge clk or negedge rst_n)
     if(!rst_n)
	      done <= 0;
	  else if(bit_cnt == 4'd10)
	      done <= 1;
	  else
	      done <= 0;
		    
endmodule 

tb文件

`timescale 1ns/1ns

module uart_tx_tb;


    reg       clk;
	 reg       rst_n;
	 reg       start;
	 reg [7:0] data;
	 
	 wire      uart_tx;
	 wire      done;

   uart_tx inst_uart_tx
		(
			.clk     (clk),
			.rst_n   (rst_n),
			.start   (start),
			.data    (data),
			.uart_tx (uart_tx),
			.done    (done)
		);
		
//时钟激励

initial clk = 1;
always #10 clk = ~clk;

//输入信号
initial begin
    rst_n = 0;
	 start = 0;
	 data  = 8'b0;
	 #101;
	 rst_n = 1;
	 #101;
	 data  = 8'b0111_1001;
	 #21;
	 start = 1;
	 #51;
	 start = 0;
	 #100000;
	 data  = 8'b0101_1001;
	 #21;
	 start = 1;
	 #51;
	 start = 0;
    #100000;	 
	 $stop;
end

endmodule 

发送波形图
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Fighting_FPGA

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值