FPGA 串口發送和接收程序

更新一篇博客吧,寫一寫關於這周自己對串口通信的理解。也會分享一下自己寫的代碼,

先簡單的介紹一下一些概念:

並行通信是指數據的各個位用多條數據線同時進行傳輸

串行通信是將數據分成一位一位的形式在一條傳輸線上逐個傳輸

同步通信帶時鐘同步信號的數據傳輸;發送方和接收方在同一時鐘的控制下,同步傳輸數據。

異步通信不帶時鐘同步信號的數據傳輸。發送方與接收方使用各自的時鐘控制數據的發送和接收過程。

單工   :數據只能沿一個方向傳輸

半雙工:數據傳輸可以沿兩個方向,但需要分時進行

全雙工:數據可以同時進行雙向傳輸

下面是串口通信的過程:

串口通信是全雙工,有Tx,Rx兩條線。Tx是發送,Rx是接收。

波特率:每秒鐘傳送數據位的數目來表示,9600,就是1秒,可以傳送9600bit個數據(0,1)

首先先介紹一下FPGA接收電腦串口助手發過來的數據過程,

首先,如果電腦還沒有發數據之前,Rx這條線上一直是高電平,處於空閒狀態,如果一旦電腦發數據過來, 這條線就會拉低,持續Period時間,這個Period時間就是一位數據需要的時間,這個時間計算:

例如:如果系統時鐘頻率clk=50000000hz,波特率baud=9600 ,Period=(50000000/9600)*20ns;

我們檢測Rx如果發生下降沿,我們就開始用一個計數器進行計時,如果記到Period這個時間到了,我們進入下個狀態,準備接收第1bit數據,如果電腦發過來的數據是1。1的ASCII碼就是:49,49對應的二進制爲:0011 0001,電腦會在Rx這條線上,先發低位數據,然後是高位數據,所以就是發過來的數據就是:1000_1100。然後FPGA接收數據時,最好在每位中間時刻去保存數據,這個時候的數據比較穩定。按照這樣一共接收八位數據。最後一位就是停止位。電腦會發過來1bit的停止位,爲高電平。我們一定要在1bit數據之前結束接收狀態。(給一個建議,在一半period那個時刻,就結束)。

 

//串口助手設置:波特率9600 無奇偶校驗位,8位數據位,一個停止位
//time:2019.11.13.22.41
module uart_rx(
 input            clk    ,
 input            rst_n  ,
 input            uart_rx,
 output reg       uart_rx_done,
 output reg	[7:0] data
 );

   
parameter  CLK_FREQ = 50000000;                 
parameter  UART_BPS = 9600;      //波特率                
localparam PERIOD   = CLK_FREQ/UART_BPS;        
                                              

//接收的數據
reg [7:0] rx_data; 


reg       rx1,rx2;
wire      start_bit;
reg       start_flag;

 reg   [15:0]   cnt0;
 wire           add_cnt0;
 wire           end_cnt0;

 reg   [3:0]    cnt1;
 wire           add_cnt1;
 wire           end_cnt1;
 
 
 //下降沿檢測 
assign  start_bit=(rx2)&(~rx1);
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
         rx1<=1'b0;
         rx2<=1'b0; 
    end
    else begin
         rx1<=uart_rx;
         rx2<=rx1;
    end
end

//開始標誌位
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        start_flag<=0;
    end
    else if(start_bit) begin
        start_flag<=1;
    end
    else if(end_cnt1) begin
        start_flag<=0;
    end
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt0 <= 0;
    end
	 else if(end_cnt1) begin
	     cnt0 <= 0;
	 end
	 else if(end_cnt0) begin
	     cnt0 <= 0;
	 end
    else if(add_cnt0)begin
        cnt0 <= cnt0 + 1;
    end
end
assign add_cnt0 = start_flag;       
assign end_cnt0 = add_cnt0 && cnt0==PERIOD-1;


always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt1 <= 0;
    end
	 else if(end_cnt1) begin
	     cnt1 <= 0;
	 end
    else if(add_cnt1)begin
        cnt1 <= cnt1 + 1;
    end
end

assign add_cnt1 = end_cnt0 ;    
assign end_cnt1 = (cnt0==((PERIOD-1)/2))&& (cnt1==10-1) ;  

//數據接收
always  @(posedge clk or negedge rst_n)begin
      if(rst_n==1'b0)begin
         rx_data<=8'd0;
      end
      else if(start_flag) begin
		  if(cnt0== PERIOD/2)begin
           case(cnt1)
            4'd1:rx_data[0]<=rx2;
            4'd2:rx_data[1]<=rx2;
            4'd3:rx_data[2]<=rx2;
            4'd4:rx_data[3]<=rx2;
            4'd5:rx_data[4]<=rx2;
            4'd6:rx_data[5]<=rx2;
            4'd7:rx_data[6]<=rx2;
            4'd8:rx_data[7]<=rx2;
		    default:rx_data<=rx_data;   
          endcase
        end
        else begin
            rx_data<=rx_data;
        end
     end
     else begin
         rx_data<=8'd0;
     end   
  end 

  //數據接收
 always  @(posedge clk or negedge rst_n)begin
      if(rst_n==1'b0)begin
           data<=0;
      end
      else if(end_cnt1)begin
          data<=rx_data;
      end  
  end
  
  //接收完成標誌
 always  @(posedge clk or negedge rst_n)begin
     if(rst_n==1'b0)begin
         uart_rx_done<=0;
     end
     else if(end_cnt1)begin
         uart_rx_done<=1;
     end
     else begin
        uart_rx_done<=0;
     end
 end
endmodule

發送代碼:

//串口助手設置:波特率9600 無奇偶校驗位,8位數據位,一個停止位
//time:2019.11.13.22.41
module uart_tx(
 input            clk    ,
 input            rst_n  ,
 output   reg     uart_tx,
 input    [7:0]   data,
 input            tx_start
 );
 
 
parameter   CLK_FREQ = 50000000;                 
parameter   UART_BPS = 9600;     //波特率                 
localparam  PERIOD   = CLK_FREQ/UART_BPS;  
 
reg [7:0] tx_data;        //發送的數據
reg start_tx_flag;        //發送數據標誌位

//記算一位數據需要多長時間PERIOD
reg   [15:0]   cnt0;
wire           add_cnt0;
wire           end_cnt0;

//發送幾個數據
reg   [3:0]    cnt1;
wire           add_cnt1;
wire           end_cnt1;
 

 //發送標誌位
 always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        start_tx_flag<=0;
		  tx_data<=0;
    end
    else if(tx_start) begin
        start_tx_flag<=1;    
		  tx_data<=data;      //把發送的數據存到這裏來
		  
    end
    else if(end_cnt1) begin
        start_tx_flag<=0;
    end
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt0 <= 0;
    end
	 else if(end_cnt1) begin
	     cnt0 <= 0;
	 end
	 else if(end_cnt0) begin
	     cnt0 <= 0;
	 end
    else if(add_cnt0)begin
        cnt0 <= cnt0 + 1;
    end
end
assign add_cnt0 = start_tx_flag;       
assign end_cnt0 = add_cnt0 && cnt0==PERIOD-1;   //一位時間

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt1 <= 0;
    end
	 else if(end_cnt1) begin
	     cnt1 <= 0;
	 end
    else if(add_cnt1)begin
        cnt1 <= cnt1 + 1;
    end
end

assign add_cnt1 = end_cnt0 ;    
assign end_cnt1 = (cnt0==((PERIOD-1)/2))&& (cnt1==10-1);   //發送10位,包括停止位,空閒位

always  @(posedge clk or negedge rst_n)begin
      if(rst_n==1'b0)begin
         uart_tx<=1;               //空閒狀態
      end
      else if(start_tx_flag) begin
		  if(cnt0==0)begin
           case(cnt1)
            4'd0:uart_tx<=0;         //起始位      
            4'd1:uart_tx<=tx_data[0]; 
				4'd2:uart_tx<=tx_data[1]; 
            4'd3:uart_tx<=tx_data[2];
            4'd4:uart_tx<=tx_data[3];
            4'd5:uart_tx<=tx_data[4];
            4'd6:uart_tx<=tx_data[5];
            4'd7:uart_tx<=tx_data[6];
            4'd8:uart_tx<=tx_data[7];
            4'd9:uart_tx<=1;       //停止位  
				default:;   
          endcase
        end  
      end 
end
endmodule

 

頂層模塊:

//注意:
//串口助手設置:波特率9600 無奇偶校驗位,8位數據位,一個停止位
//time:2019.11.13.22.41
module uart(
    input  clk    ,
    input  rst_n  ,
	input  uart_rx  ,
	output  uart_tx  ,
   output wire [7:0] seg_sel,
   output wire [7:0] segment
	
);

wire [7:0] data; 
wire uart_rx_done;
																
//數碼管				
seg_num seg_disp_uut(
    .clk       (clk),
    .rst_n     (rst_n),
    .seg_sel   (seg_sel),
    .segment   (segment),
	 .data      (data)
 );


//FPGA接收串口助手發來的數據 
uart_rx uart_rx_uut(
     .clk    (clk),
    .rst_n   (rst_n),
    .uart_rx (uart_rx),
    .uart_rx_done(uart_rx_done),
	 .data    (data)
);
	 
//FPGA把接收的數據發送到串口助手上	 	 
uart_tx uart_tx_uut(
   .clk     (clk),
   .rst_n   (rst_n),
   .uart_tx (uart_tx),
   .data    (data),
   .tx_start(uart_rx_done)
 );
	 
endmodule

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章