微信公众号上线,搜索公众号小灰灰的FPGA,关注可获取相关源码,定期更新有关FPGA的项目以及开源项目源码,包括但不限于各类检测芯片驱动、低速接口驱动、高速接口驱动、数据信号处理、图像处理以及AXI总线等

UART数据发送和接收(Verilog)

UART 通信的原理,FPGA 来实现 UART 通信中的数据发送和接收

一、UART通信原理

UART即异步串行通信。一种异步收发传输器,数据发送将并行数据转换成串行数据发送,数据接收将串行数据转换成并行数据接收,可实现全双工传输和接收。

1、RS232的引脚定义

android uart数据接收 uart收发数据的过程及步骤_串口通信

DCD——调制解调器通知PC有载波被侦测到

RXD——接收数据

TXD——发送数据

DTR——PC通知调制解调器进行传输

GND——地线

DSR——调制解调器通知PC准备就绪

RTS——PC要求调制解调器提交数据

CTS——调制解调器通知PC传输数据

RI——调制解调器通知PC有数据输入

2、UART常用参数及时序

UART常用参数设置包括:数据位、波特率、奇偶校验、停止位

数据位:单个UART数据开始到停止期间发送的数据。

波特率:每秒通信的数据比特个数,通常为115200/19200/9600/2400/1200。

奇偶校验:验证数据的正确性。一般不使用,若使用,在偶校验中,数据改变会使得传送数据(所有位数)中“1”个数为偶数,在奇校验中,数据改变会使得传送数据中“1”个数为奇数。

停止位:每个字节的数据位发送完成,发送停止位,标志一次数据传输完成。

RS-232标准,常用配置8个数据位,无奇偶校验,1个停止位,时序如下:

android uart数据接收 uart收发数据的过程及步骤_fpga_02

二、UART串口发送(Verilog)
波特率计算,采用定时计数器产生波特率的时钟,计数值的计算为系统时钟频率除以波特率数值。

module uart_tx(
    input       wire       [00:00]          i_clk,      
    input       wire       [00:00]          i_rst_n, 
    input       wire       [02:00]          i_baud,        //波特率设置
    input       wire       [07:00]          i_data,        //发送数据
    input       wire       [00:00]          i_Tx_start,   //发送数据使能	
	output      reg        [00:00]          o_data,        //输出串行数据
    output      reg        [00:00]          o_Tx_stop,    //发送数据结束
    output      reg        [00:00]          o_state       //数据发送状态
);
/************************波特率时钟***************************/
    reg           [15:00]          baud_cnt;      //波特率时钟计数器
    reg           [00:00]          baud_clk;      //波特率时钟
    reg           [15:00]          baud_tmp;      //波特率数值设定值
    always@(posedge i_clk or negedge i_rst_n)
    begin
        if(!i_rst_n)    
            baud_tmp <= #1 16'd5208;
	    else begin
		    case(i_baud)
			     0:baud_tmp <= #1 16'd5208;     //波特率9600
				 1:baud_tmp <= #1 16'd2604;     //波特率19200
				 2:baud_tmp <= #1 16'd1302;     //波特率38400
				 3:baud_tmp <= #1 16'd868;      //波特率57600		
				 4:baud_tmp <= #1 16'd434;      //波特率115200
             default:baud_tmp <= #1 16'd5208;
          endcase			 
		end
    end
    always@(posedge i_clk or negedge i_rst_n)
    begin
        if(!i_rst_n)
            baud_cnt <= #1 1'b0;
	    else if(o_state)
		     begin
		        if(baud_cnt == baud_tmp)
                    baud_cnt <= #1 1'b0;
		        else
                  baud_cnt <= #1 baud_cnt + 1'b1;		
			 end
    end 	
    always@(posedge i_clk or negedge i_rst_n)
    begin
        if(!i_rst_n)
            baud_clk <= #1 1'b0;
	    else if(baud_cnt == baud_tmp)
		      baud_clk <= #1 1'b1;
		else
            baud_clk <= #1 1'b0;		
    end 
/************************波特率时钟***************************/
/***************数据发送状态和结束标志**************************/
    reg           [03:00]          data_cnt;      //数据计数器     
    always@(posedge i_clk or negedge i_rst_n)
    begin
        if(!i_rst_n)
          data_cnt <= #1 1'b0;
	    else if(data_cnt == 4'd11)
		    data_cnt <= #1 1'b0;
		else if(baud_clk)
          data_cnt <= #1 data_cnt + 1'b1;
        else 
          data_cnt <= #1 data_cnt;		
    end 				   
    always@(posedge i_clk or negedge i_rst_n)
    begin
        if(!i_rst_n)
		    o_state <= #1 1'b0;
		else if(i_Tx_start)
		    o_state <= #1 1'b1;
		else if(data_cnt == 4'd11)
 		    o_state <= #1 1'b0; 
        else 
		    o_state <= #1 o_state;		
    end
    always@(posedge i_clk or negedge i_rst_n)
    begin
        if(!i_rst_n)
		    o_Tx_stop <= #1 1'b0;
		else if(data_cnt == 4'd11)
 		    o_Tx_stop <= #1 1'b1; 
        else 
		    o_Tx_stop <= #1 1'b0;		
    end	
/***************数据发送状态和结束标志**************************/
/***************数据发送*************************************/
    reg           [07:00]          i_data_n;      //数据存储器
    always@(posedge i_clk or negedge i_rst_n)
	begin
	    if(!i_rst_n)
		    i_data_n <= #1 1'b0;
		else if(i_Tx_start)
		    i_data_n <= #1 i_data;		 
	    else 
		    i_data_n <= #1 i_data_n;
    end
	always@(posedge i_clk or negedge i_rst_n)
	begin
        if(!i_rst_n)
		    o_data <= #1 1'b1;
		  else begin
		     case(data_cnt)
			     0:o_data <= #1 1'b1;
				 1:o_data <= #1 1'b0;            //start
				 2:o_data <= #1 i_data_n[0];
				 3:o_data <= #1 i_data_n[1];
				 4:o_data <= #1 i_data_n[2];
				 5:o_data <= #1 i_data_n[3];
				 6:o_data <= #1 i_data_n[4];
				 7:o_data <= #1 i_data_n[5];
				 8:o_data <= #1 i_data_n[6];
				 9:o_data <= #1 i_data_n[7];
			    10:o_data <= #1 1'b1;           //stop
		        default:o_data <= #1 1'b1;
		     endcase  
		end
    end
/***************数据发送*************************************/
endmodule

三、UART串口接收(Verilog)

module uart_rx(
    input      wire        [00:00]          i_clk,      
    input      wire        [00:00]          i_rst_n, 
    input      wire        [02:00]          i_baud,        //波特率设置
    input      wire        [00:00]          i_data,        //串行发送数据	
	output     reg         [07:00]          o_data,        //输出并行数据
    output     reg         [00:00]          o_Rx_stop      //接收数据结束
);
/**************************数据锁存***************************/
        reg        [00:00]          state;          //数据接收状态  
        reg        [00:00]          i_data_n;       //数据接收状态  
        reg        [00:00]          i_data_r;       //数据接收状态
        reg        [00:00]	        data_n;
		reg        [00:00]          data_r; 
		wire       [00:00]          i_data_neg;  
        reg        [15:00]          data_cnt;       //数据计数器       
 always@(posedge i_clk or negedge i_rst_n)
	 begin
	       if(!i_rst_n)
			     begin
			      i_data_n <= #1 1'b0;
				  i_data_r <= #1 1'b0;
				 end
		   else begin
			      i_data_n <= #1 i_data;
				  i_data_r <= #1 i_data_n;
	            end
	 end
    always@(posedge i_clk or negedge i_rst_n)
	 begin
	       if(!i_rst_n)
			     begin
			       data_n <= #1 1'b0;
				   data_r <= #1 1'b0;
				 end
		   else begin
			       data_n <= #1 i_data_r;
				   data_r <= #1 data_n;
	            end
	 end 
	 assign  i_data_neg  = (!i_data_r) & data_r;					 
/**************************数据锁存***************************/
/**************************数据发送状态************************/
    always@(posedge i_clk or negedge i_rst_n)
	 begin
	     if(!i_rst_n)
		    state <= #1 1'b0;
		 else if(i_data_neg)
	        state <= #1 1'b1;
		 else if(data_cnt == 16'd159)  
	        state <= #1 1'b0;
         else 
            state <= #1 state;		  
	 end
/**************************数据发送状态************************/
/******************波特率时钟(16倍的波特率)********************/
    reg           [15:00]          baud_cnt;      //波特率时钟计数器
    reg           [00:00]          baud_clk;      //波特率时钟
    reg           [15:00]          baud_tmp;      //波特率数值设定值
    always@(posedge i_clk or negedge i_rst_n)
    begin
        if(!i_rst_n)    
            baud_tmp <= #1 16'd325;
	    else begin
		    case(i_baud)
			     0:baud_tmp <= #1 16'd325;      //波特率9600
				 1:baud_tmp <= #1 16'd162;     //波特率19200
				 2:baud_tmp <= #1 16'd80;      //波特率38400
				 3:baud_tmp <= #1 16'd53;      //波特率57600		
				 4:baud_tmp <= #1 16'd26;      //波特率115200
             default:baud_tmp <= #1 16'd325;
          endcase			 
		end
    end
    always@(posedge i_clk or negedge i_rst_n)
    begin
        if(!i_rst_n)
            baud_cnt <= #1 16'd0;
	    else if(state)
		     begin
		        if(baud_cnt == baud_tmp)
		            baud_cnt <= #1 16'd0;
		        else
                    baud_cnt <= #1 baud_cnt + 1'b1;		
			 end
    end 	
    always@(posedge i_clk or negedge i_rst_n)
    begin
        if(!i_rst_n)
            baud_clk <= #1 1'b0;
	    else if(baud_cnt == baud_tmp)
		    baud_clk <= #1 1'b1;
		else
            baud_clk <= #1 1'b0;		
    end 
/******************波特率时钟(16倍的波特率)********************/
/********************数据接收结束标志***************************/
    always@(posedge i_clk or negedge i_rst_n)
    begin
         if(!i_rst_n)
            data_cnt <= #1 16'd0;
	     else if((data_cnt == 16'd159)||((data_cnt == 16'd12)&&(start_buffer > 3'd2)))
		    data_cnt <= #1 16'd0;
		 else if(baud_clk)
            data_cnt <= #1 data_cnt + 1'b1;
         else 
            data_cnt <= #1 data_cnt;		
    end 		   
    always@(posedge i_clk or negedge i_rst_n)
    begin
        if(!i_rst_n)
		    o_Rx_stop <= #1 1'b0;
		else if(data_cnt == 16'd159)
 		    o_Rx_stop <= #1 1'b1; 
        else 
		    o_Rx_stop <= #1 1'b0;		
    end	
/********************数据接收结束标志***************************/
/***********************数据发送**************************/
   reg      [02:00]      data_buffer     [07:00];    //中间数据缓存器  
   reg      [02:00]      start_buffer           ;    //开始缓存器
   reg      [02:00]      stop_buffer            ;    //结束缓存器
    always@(posedge i_clk or negedge i_rst_n)
	 begin
		  if(!i_rst_n)
           begin
	              start_buffer   <= #1 3'd0;
				  data_buffer[0] <= #1 3'd0;
				  data_buffer[1] <= #1 3'd0;				  
				  data_buffer[2] <= #1 3'd0;			  
				  data_buffer[3] <= #1 3'd0;			  
				  data_buffer[4] <= #1 3'd0;			  
				  data_buffer[5] <= #1 3'd0;
				  data_buffer[6] <= #1 3'd0;
				  data_buffer[7] <= #1 3'd0;
				  stop_buffer    <= #1 3'd0;				  
           end
		  else begin
		           case(data_cnt)
				   0:begin
	                           start_buffer   <= #1 3'd0;
				               data_buffer[0] <= #1 3'd0;
				               data_buffer[1] <= #1 3'd0;				  
				               data_buffer[2] <= #1 3'd0;			  
				               data_buffer[3] <= #1 3'd0;			  
				               data_buffer[4] <= #1 3'd0;			  
				               data_buffer[5] <= #1 3'd0;
				               data_buffer[6] <= #1 3'd0;
				               data_buffer[7] <= #1 3'd0;
				               stop_buffer    <= #1 3'd0;				
			                end			
		            6,7,8,9,10,11:
	                    start_buffer   <= #1 start_buffer + i_data_r;
                    22,23,24,25,26,27:
				       data_buffer[0] <= #1 data_buffer[0] + i_data_r;
	                38,39,40,41,42,43:
				       data_buffer[1] <= #1 data_buffer[1] + i_data_r;
	                54,55,56,57,58,59:
				       data_buffer[2] <= #1 data_buffer[2] + i_data_r;
	                70,71,72,73,74,75:
				       data_buffer[3] <= #1 data_buffer[3] + i_data_r;	
				    86,87,88,89,90,91:
				       data_buffer[4] <= #1 data_buffer[4] + i_data_r;
	                102,103,104,105,106,107:
				       data_buffer[5] <= #1 data_buffer[5] + i_data_r;
	                118,119,120,121,122,123:
				       data_buffer[6] <= #1 data_buffer[6] + i_data_r;
	                134,135,136,137,138,139:
				       data_buffer[7] <= #1 data_buffer[7] + i_data_r;
		            150,151,152,153,154,155:
	                   stop_buffer    <= #1 stop_buffer + i_data_r;
default : begin
	                                start_buffer   <= #1 start_buffer   ;
			                        data_buffer[0] <= #1 data_buffer[0] ;
			                        data_buffer[1] <= #1 data_buffer[1] ;
			                        data_buffer[2] <= #1 data_buffer[2] ;
			                        data_buffer[3] <= #1 data_buffer[3] ;
			                        data_buffer[4] <= #1 data_buffer[4] ;
			                        data_buffer[5] <= #1 data_buffer[5] ;
			                        data_buffer[6] <= #1 data_buffer[6] ;
			                        data_buffer[7] <= #1 data_buffer[7] ;
			                        stop_buffer    <= #1 stop_buffer    ;				  
                                 end 
    	           endcase
	          end					 
    end  
	 always@(posedge i_clk or negedge i_rst_n)
	 begin
	    if(!i_rst_n)
		    o_data <= #1 8'd0;
		 else if(o_Rx_stop)begin
			   o_data[0] <= #1 data_buffer[0][2] ;
			   o_data[1] <= #1 data_buffer[1][2] ;				  
			   o_data[2] <= #1 data_buffer[2][2] ;			  
			   o_data[3] <= #1 data_buffer[3][2] ;			  
			   o_data[4] <= #1 data_buffer[4][2] ;			  
			   o_data[5] <= #1 data_buffer[5][2] ;
			   o_data[6] <= #1 data_buffer[6][2] ;
			   o_data[7] <= #1 data_buffer[7][2];	    
		 end
	 end
/***********************数据发送**************************/
endmodule

三、UART串口接收和发送顶层Top(Verilog)

module uart_top(
    input      wire        [00:00]          i_clk,      
    input      wire        [00:00]          i_rst_n, 
    input      wire        [02:00]          i_baud,        //波特率设置
    input      wire        [07:00]          i_data,        //发送数据
    input      wire        [00:00]          i_Tx_start,    //发送数据使能
    output     wire        [00:00]          o_Tx_stop,     //发送数据结束
    output     wire        [00:00]          o_state,       //数据发送状态 
    output     wire        [07:00]          o_data_rx,     //输出并行数据
    output     wire        [00:00]          o_Rx_stop      //接收数据结束
);   
wire         [00:00]          o_data;        
uart_rx u_uart_rx(
    .i_clk        (i_clk      ),      
    .i_rst_n      (i_rst_n    ), 
    .i_baud       (i_baud     ),       
    .i_data       (o_data     ),            
	.o_data       (o_data_rx  ),      
    .o_Rx_stop    (o_Rx_stop  )            
);

uart_tx u_uart_tx(
    .i_clk       (i_clk      ),      
    .i_rst_n     (i_rst_n    ), 
    .i_baud      (i_baud     ),        
    .i_data      (i_data     ),        
    .i_Tx_start  (i_Tx_start ),        
	.o_data      (o_data     ),       
    .o_Tx_stop   (o_Tx_stop  ),        
    .o_state     (o_state    )        
);
endmodule

四、UART串口接收和发送测试Testbench(Verilog)

`timescale 1ns/1ns
`define clk_period 7
module tb_uart_top();
        reg          [00:00]          i_clk;      
        reg          [00:00]          i_rst_n;         
        wire         [07:00]          o_data_rx;    
        wire         [00:00]          o_Rx_stop;  
        reg          [02:00]          i_baud;       
        reg          [07:00]          i_data;        
        reg          [00:00]          i_Tx_start;    
        wire         [00:00]          o_Tx_stop;     
        wire         [00:00]          o_state;       		  		  
 uart_top u_uart_top(
    .i_clk     (i_clk),      
    .i_rst_n   (i_rst_n), 
    .i_baud    (i_baud),        
    .i_data    (i_data),        
    .i_Tx_start(i_Tx_start),   	
	.o_Tx_stop(o_Tx_stop),
	.o_state(o_state),  
	.o_data_rx (o_data_rx),     
    .o_Rx_stop (o_Rx_stop)     
);

initial i_clk = 0;
always#(`clk_period/2)  i_clk = ~i_clk;

initial begin
   #(`clk_period*20);
   
   i_rst_n = 0;
   #(`clk_period*20);
   
	i_rst_n = 1;
   #(`clk_period*20);
   
	i_baud  = 3'd0;
	i_data  = 8'h15;
	i_Tx_start = 1;
   #(`clk_period);	
	i_Tx_start = 0;
	
	@(posedge o_Tx_stop)
   #(`clk_period*20);
   i_data  = 8'h12;
	i_Tx_start = 1;
	
   #(`clk_period*20);	
   
   i_Tx_start = 0;
	@(posedge o_Tx_stop) 
   #(`clk_period*20);	
   
	i_data  = 8'h14;
	i_Tx_start = 1;
   #(`clk_period*20);	
   
	i_Tx_start = 0;	
	@(posedge o_Tx_stop) 
   #(`clk_period*20);	
   
	i_data  = 8'h31;
	i_Tx_start = 1;
   #(`clk_period*20);	
   
	i_Tx_start = 0;	
	@(posedge o_Tx_stop) 	
   #(`clk_period*200); 
	$stop; 
end
endmodule