基于FPGA的RS232串口通信协议

RS232原理

注意:以下仅为学习笔记,借鉴网络各个知识点汇聚而成,如有错误请留言。

原理:RS232协议是UART的一种,只有两根数据线,分别是rx和tx,分别用来接收数据和发送数据,数据收发基于帧结构,每次按照8bit的大小来接收和发送数据。数据线空闲状态下为高电平,开始发送数据后将电平拉低一帧作为起始位,随后8帧的数据为数据内容,发送完毕后将数据线电平拉高一帧作为停止位,然后一直拉高回到空闲状态。          
                 

RS232缺点:1.传输数据的距离进; 2.传输数据慢。

           优点:1.很多传感器CPU都带有串口功能,方便调试;

                      2.只有两条数据线,减少IO口。

串口RS232协议:位宽都是1bit。通过串口调试助手发送一个8bit的数据(一位一位的发送和接收);RS232串口在发送数据时将并行数据转换成串行数据来传输;在接收数据时将接收到的串行数据转换成并行数据。

串口发送接收1bit数据时间称一个波特,如:波特率9600,波特是1/9600s,系统时钟50Mhz,时钟周期是20ns,在9600波特率下,一个波特等于5208个系统时钟周期((1s * 10^9ns)ns/9600)/20ns,也就是说每个bit数据在50Mhz时钟下保持5208个时钟周期,起始位和停止位都是一个波特时间,本次实例采用9600波特率。

串口数据RX发送模块

    该模块的功能是将并行数据转化为串行数据。

RX接收模块框图:

                         

理想波形设计: 

 

RX接收模块编程代码:


module    uart_rx
#(
    parameter     UART_BPS = 'd9600,       //波特率9600
	              CLK_FREQ = 'd50_000_000    //时钟频率50MHZ
)
(  
    input    wire        sys_clk      ,
	  input    wire        sys_rst_n    ,
	  input    wire        rx           ,
	                           
    output   reg  [7:0]  po_data      ,  
    output   reg         po_flag      
);
 
localparam  BAUD_CNT_MAX    =   CLK_FREQ/UART_BPS   ;  //接受1bit数据所需多少个脉冲周期
reg             rx_reg1       ; //打一拍,同步时钟
reg             rx_reg2       ; //打两拍,减小亚状态
reg             rx_reg3       ; //打三拍,在次减小亚状态
reg             start_flag    ; //开始标志信号
reg             work_en       ; //计数使能信号
reg    [15:0]   baud_cnt      ; //波特率计数器
reg             bit_flag      ; //bit计数信号
reg    [3:0]    bit_cnt       ; //bit计数
reg    [7:0]    rx_data       ; //传输移位bit
reg             rx_flag       ; //传输1帧完成信号
 
//rx_reg: 进行打拍操作,消除亚稳态					   
always @(posedge sys_clk or negedge sys_rst_n)
     if( !sys_rst_n ) 
       begin
	       rx_reg1 <= 1'b1;
		     rx_reg2 <= 1'b1;
		     rx_reg3 <= 1'b1;
		   end
     else   
       begin
         rx_reg1 <= rx      ;	
         rx_reg2 <= rx_reg1 ;
         rx_reg3 <= rx_reg2 ;	
		  end
		
//start_nedge:开始传输数据的标志								   
always @(posedge sys_clk or negedge sys_rst_n)
     if( !sys_rst_n )
         start_flag	<= 1'b0;
     else if((rx_reg2 == 1'b0 && rx_reg3 == 1'b1 ) && (work_en == 1'b0))
	       start_flag	<= 1'b1;
	   else 
	       start_flag	<= 1'b0;
		 
//work_en:高电平传输数据使能	
always @(posedge sys_clk or negedge sys_rst_n)
     if( !sys_rst_n )
         work_en	<= 1'b0;
     else if (start_flag == 1'b1)
	       work_en	<= 1'b1;
	   else if ((bit_flag == 1'b1) && (bit_cnt == 4'd8))
	       work_en	<= 1'b0;
	   else
	       work_en	<= work_en;
	     	 
//传输一位数据需要花 5208 脉冲周期
always @(posedge sys_clk or negedge sys_rst_n)
    if( !sys_rst_n )
	     baud_cnt	<= 16'd0;
	  else  if (baud_cnt == BAUD_CNT_MAX - 1'b1) 
	     baud_cnt	<= 16'd0;
    else  if (work_en	== 1'b1)
	     baud_cnt  <= baud_cnt + 1'b1;
	  else  
	     baud_cnt	<= 16'd0;
		
//bit_flag:接收数据标志	
always @(posedge sys_clk or negedge sys_rst_n)
    if( !sys_rst_n )
	       bit_flag	<= 1'd0;    		
	  else  if (( baud_cnt == (BAUD_CNT_MAX/2)  - 1 ))
         bit_flag	<= 1'b1; 
	  else	
	       bit_flag	<= 1'b0;

//bit_cnt:bit计数器,计数传输bit数据	       
always @(posedge sys_clk or negedge sys_rst_n)
    if( !sys_rst_n )
	       bit_cnt <= 4'd0; 
	  else if ((bit_flag	== 1'b1) && (bit_cnt == 4'd8))
	       bit_cnt <= 4'd0;
	  else if (bit_flag == 1'b1)
         bit_cnt <= bit_cnt + 1'b1;

	       

//rx_fata:将数据传输移位	
always @(posedge sys_clk or negedge sys_rst_n)
    if( !sys_rst_n )  
        rx_data	<= 1'b0; 
	  else if ((bit_cnt >= 4'd1)&&(bit_cnt <= 4'd8)&&(bit_flag == 1'b1))
	      rx_data <= {rx_reg3, rx_data[7:1]};

//	rx_flag:数据传输后的标志位 	
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_flag <= 1'b0;
    else    if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
        rx_flag <= 1'b1;
    else
        rx_flag <= 1'b0;

//把传输移位的数据输出到po_data,并设置一个输出标志位		
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)  
      begin
        po_data <= 1'b0;
		    po_flag  <= 1'b0;
		  end
    else if (rx_flag == 1'b1)  
      begin	
   	    po_data <= rx_data;
		    po_flag  <= 1'b1; 		
		  end
	else 	
	    po_flag  <= 1'b0;
endmodule

RX接收模块仿真代码:

`timescale  1ns/1ns
 
module  tb_uart_rx();
 
reg             sys_clk;
reg             sys_rst_n;
reg             rx;
 
wire    [7:0]   po_data;
wire            po_flag;
 
always #10 sys_clk = ~sys_clk;
 
initial begin
        sys_clk    = 1'b1;
        sys_rst_n <= 1'b0;
        rx        <= 1'b1;
        #20;
        sys_rst_n <= 1'b1;
end
 
//模拟发送8次数据,分别为0~7
initial begin
        #200
        rx_bit(8'd0);  //任务的调用,任务名+括号中要传递进任务的参数
        rx_bit(8'd1);
        rx_bit(8'd2);
        rx_bit(8'd3);
        rx_bit(8'd4);
        rx_bit(8'd5);
        rx_bit(8'd6);
        rx_bit(8'd7);
end
task rx_bit(input   [7:0]   data);
        integer i;      //定义一个变量
		                //integer类型也是一种寄存器数据类型,integer类型的变量为有符号数
        for(i=0; i<10; i=i+1) begin  //不可用i=i++的方式
            case(i)
                0: rx <= 1'b0;   //起始位
                1: rx <= data[0];
                2: rx <= data[1];
                3: rx <= data[2];
                4: rx <= data[3];
                5: rx <= data[4];
                6: rx <= data[5];
                7: rx <= data[6];
                8: rx <= data[7];
                9: rx <= 1'b1;
            endcase
            #(5208*20); //每发送1位数据延时5208个时钟周期
        end
endtask         //任务以endtask结束
 
uart_rx uart_rx_inst(
        .sys_clk    (sys_clk    ),  
        .sys_rst_n  (sys_rst_n  ),  
        .rx         (rx         ),  
                
        .po_data    (po_data    ),  
        .po_flag    (po_flag    )   
);
endmodule
 

RX仿真图像显示:

 串口数据TX发送模块

 该模块的功能是将串行数据转化为并行数据。

TX接收模块框图:

                        

理想波形图像:

 

TX发送模块编程代码:


module     uart_tx
#(     
    parameter    UART_BPS    =   'd9600 ,  
    parameter    CLK_FREQ    =   'd50_000_000 
 
)
(
    input   wire            sys_clk     , 
    input   wire            sys_rst_n   , 
    input   wire    [7:0]   pi_data     , 
    input   wire            pi_flag     , 
    
    output  reg             tx            
);
 
reg          work_en;
reg  [15:0]  baud_cnt;
reg          bit_flag;
reg  [3:0]   bit_cnt;
 
localparam  BAUD_CNT_MAX    =   CLK_FREQ/UART_BPS   ;
always @(posedge sys_clk or negedge sys_rst_n)
    if( !sys_rst_n )
	    work_en <= 1'b0;
    else   if(pi_flag == 1'b1)  
	    work_en <= 1'b1;
	else   if((bit_flag == 1'b1) && (bit_cnt == 4'd9) )
	    work_en <= 1'b0;
		
//传输一位数据需要花 5208 脉冲周期
always @(posedge sys_clk or negedge sys_rst_n)
    if( !sys_rst_n )
	    baud_cnt	<= 1'b0;
	else  if (baud_cnt == BAUD_CNT_MAX - 1'b1) 
	    baud_cnt	<= 1'b0;
    else  if (work_en	== 1'b1)
	    baud_cnt  <= baud_cnt + 1'b1;
	else  
  	    baud_cnt	<= 1'b0;
	
//bit_flag:接收数据标志	
always @(posedge sys_clk or negedge sys_rst_n)
    if( !sys_rst_n )
	   bit_flag	<= 1'b0;    		
	else  if (( baud_cnt == (BAUD_CNT_MAX/2)  - 1 ))
        bit_flag	<= 1'b1; 
	else	
	    bit_flag	<= 1'b0;
		
always @(posedge sys_clk or negedge sys_rst_n)
    if( !sys_rst_n )
	   bit_cnt <= 1'b0; 
	else if ((bit_flag	== 1'b1) && (bit_cnt < 4'd9))
       bit_cnt <= bit_cnt + 1'b1;
	else if ((bit_flag	== 1'b1) && (bit_cnt == 4'd9))
	   bit_cnt <= 1'b0;		
 
always @(posedge sys_clk or negedge sys_rst_n)
    if( !sys_rst_n )
        tx <=  1'b1;
	else if(bit_flag == 1'b1)
	    case(bit_cnt)
		    0    :   tx  <= 1'b0;        //起始位
	        1    :   tx  <= pi_data[bit_cnt - 1];
            2    :   tx  <= pi_data[bit_cnt - 1];
            3    :   tx  <= pi_data[bit_cnt - 1];
            4    :   tx  <= pi_data[bit_cnt - 1];
            5    :   tx  <= pi_data[bit_cnt - 1];
            6    :   tx  <= pi_data[bit_cnt - 1];
            7    :   tx  <= pi_data[bit_cnt - 1];
            8    :   tx  <= pi_data[bit_cnt - 1];
            9    :   tx  <= 1'b1 ;       //结束位
			default : tx <= 1'b1;
			endcase
 
endmodule
			

TX发送模块仿真代码:

`timescale  1ns/1ns
 
module  tb_uart_tx();
 
reg         sys_clk;
reg         sys_rst_n;
reg [7:0]   pi_data;
reg         pi_flag;
 
wire        tx;
 
initial begin
        sys_clk    = 1'b1;
        sys_rst_n <= 1'b0;
        #20;
        sys_rst_n <= 1'b1;
end
 
always #10 sys_clk = ~sys_clk;
 
initial begin
        pi_data <= 8'b0;
        pi_flag <= 1'b0;
        #200
        //发送数据0
        pi_data <= 8'd0;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
//每发送1bit数据需要5208个时钟周期,一帧数据为10bit
//所以需要数据延时(5208*20*10)后再产生下一个数据
        #(5208*20*10);
        //发送数据1
        pi_data <= 8'd1;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
        #(5208*20*10);
        //发送数据2
        pi_data <= 8'd2;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
        #(5208*20*10);
        //发送数据3
        pi_data <= 8'd3;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
        #(5208*20*10);
        //发送数据4
        pi_data <= 8'd4;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
        #(5208*20*10);
        //发送数据5
        pi_data <= 8'd5;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
        #(5208*20*10);
        //发送数据6
        pi_data <= 8'd6;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
        #(5208*20*10);
        //发送数据7
        pi_data <= 8'd7;
        pi_flag <= 1'b1;
        #20
        pi_flag <= 1'b0;
end
uart_tx uart_tx_inst(
        .sys_clk    (sys_clk    ),
        .sys_rst_n  (sys_rst_n  ),
        .pi_data    (pi_data    ),
        .pi_flag    (pi_flag    ),
        .tx         (tx         ) 
);
endmodule

RX和TX仿真波形图:

顶层模块代码:

module  rs232
(
    input   wire    sys_clk     ,   
    input   wire    sys_rst_n   ,   
    input   wire    rx          ,   //串口接收数据
    output  wire    tx              //串口发送数据
);
 
parameter   UART_BPS    =   20'd9600        ,   //波特率
            CLK_FREQ    =   26'd50_000_000  ;   //时钟频率
			
wire    [7:0]   po_data;
wire            po_flag;
//串口接受数据模块
uart_rx
#(
    .UART_BPS    (UART_BPS  ),  //串口波特率
    .CLK_FREQ    (CLK_FREQ  )   //时钟频率
)
uart_rx_inst
(
    .sys_clk    (sys_clk    ), 
    .sys_rst_n  (sys_rst_n  ), 
    .rx         (rx         ), 
            
    .po_data    (po_data    ), 
    .po_flag    (po_flag    )  
);
//串口发送数据模块
uart_tx
#(
    .UART_BPS    (UART_BPS  ),  //串口波特率
    .CLK_FREQ    (CLK_FREQ  )   //时钟频率
)
uart_tx_inst
(
    .sys_clk    (sys_clk    ),  
    .sys_rst_n  (sys_rst_n  ),  
    .pi_data    (po_data    ),  
    .pi_flag    (po_flag    ),  
                
    .tx         (tx         )   
);
endmodule

顶层仿真:

`timescale  1ns/1ns
 
module  tb_rs232();
 
wire    tx          ;
 
reg     sys_clk     ;
reg     sys_rst_n   ;
reg     rx          ;
 
initial begin
    sys_clk    = 1'b1;
    sys_rst_n <= 1'b0;
    rx        <= 1'b1;
    #20;
    sys_rst_n <= 1'b1;
end
//初始化任务函数
initial begin
    #200
    rx_byte();  //调用任务rx_byte
end
 
always #10 sys_clk = ~sys_clk;
 
//创建任务rx_byte,本次任务调用rx_bit任务,发送8次数据,分别为0~7
task    rx_byte();  //因为不需要外部传递参数,所以括号中没有输入
    integer	j;
    for(j=0; j<8; j=j+1)    //调用8次rx_bit任务,每次发送的值从0变化7。for 括号中最后执行的内容不可写成i= i++
        rx_bit(j);
endtask
 
//创建任务rx_bit,每次发送的数据有10位,data的值分别为0到7由j的值传递进来
task    rx_bit(input   [7:0]   data);
    integer i;
    for(i=0; i<10; i=i+1)   begin
        case(i)
            0: rx <= 1'b0;     //起始位为0
            1: rx <= data[0];
            2: rx <= data[1];
            3: rx <= data[2];
            4: rx <= data[3];
            5: rx <= data[4];
            6: rx <= data[5];
            7: rx <= data[6];
            8: rx <= data[7];
            9: rx <= 1'b1;    //终止位为1
        endcase
        #(5208*20);   //每发送1位数据延时5208个时钟周期
    end
endtask
rs232   rs232_inst
(
    .sys_clk    (sys_clk    ),  
    .sys_rst_n  (sys_rst_n  ),  
    .rx         (rx         ),  
    .tx         (tx         )   
);
endmodule

仿真波形: 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值