RS232串口通信

RS232串口经常使用在PC机与FPGA通信中,用于两者之间的数据传输,因为UART协议简单、易实现,故经常使用。

DB9接口只需要使用3根线,RXD(2)、TXD(3)和GND(5),如下图所示。而用FPGA实现控制器时只需要利用RXD和TXD两根线即可完成串口通信。

UART的异步通信协议如下所示:

1. 首先接受双方提前定义好通信的速度和格式等信息;

2. 如果是空闲状态,发送器一直将数据线拉高;

3. 开始通信时,发送器将数据线拉低,从而接收器能知道数据字节即将过来;

4. 数据位通常是8位,低位先发送,高位后发送;

5. 停止通信时,发送器将数据线再次拉高。

控制器包括3部分:波特率发生器、发送器和接收器,本设计发送器和接收器都带有异步FIFO缓存数据。本设计完成如下功能;串口助手发送数据字节给FPGA,FPGA将数据返回给串口助手。波特率采用11520bps,数据格式1位开始位、8位数据位和1位停止位,无校验位。

image

1. 波特率发生器

波特率发生器需要对主时钟进行分频生成发送器和接收器的工作时钟。发送器的时钟就等于波特率,而接收器的时钟频率是波特率的8倍或者16倍,即对发送器发过来的数据进行过采样以获得准确的数据。下面是接收器和发送器的分频系数。

parameter   Div_rcv     = Clk_freq/Bandrate/16-1;  //8倍
parameter   Div_trans   = Clk_freq/Bandrate/2-1;

发送完一个数据字节后,等待FIFO_T非空或者FIFO_R非空。

 1 module clk_generater(clk_rcv,clk_trans,clk_in,reset);
 2 
 3  parameter   Clk_freq    = 50000000;
 4  parameter   Bandrate    = 115200;
 5  parameter   Div_rcv     = Clk_freq/Bandrate/16-1;
 6  parameter   Div_trans   = Clk_freq/Bandrate/2-1;
 7 
 8  output      clk_rcv,clk_trans;
 9  input       clk_in,reset;
10  
11  reg         clk_rcv,clk_trans;
12  reg [15:0]  counter_rcv;
13  reg [15:0]  counter_trans;
14 
15  always @ ( posedge clk_in )
16   if(reset == 1) begin
17     clk_rcv   <= 0;
18     clk_trans <= 0;
19     counter_rcv  <= 0;
20     counter_trans  <= 0;
21   end else begin
22     if( counter_rcv == Div_rcv )begin 
23       clk_rcv  <= ~clk_rcv;
24       counter_rcv <= 0;
25     end else counter_rcv <= counter_rcv + 1'b1;
26     
27     if( counter_trans == Div_trans )begin 
28       clk_trans  <= ~clk_trans;
29       counter_trans <= 0;
30     end else counter_trans <= counter_trans + 1'b1;
31  end
32 endmodule 
View Code

2. 发送器

发送器发送的数据从FIFO_T中读取,而FIFO_T数据是从FIFO_R获得。如果FIFO_T非空,读取一个数据字节并在末尾添加数据位0(开始通信位,拉低数据线),下一步进行9次右移操作,串行输出数据位。

XMT_shftreg <= { 1'b1,XMT_shftreg[word_size:1]};

assign Serial_out = XMT_shftreg[0];

  1 module UART_Transmitter(              //UART transmitter with 16Byte transmit FIFO
  2 output                  Serial_out,       //serial output to data channel
  3 output                  Busy,             //used to indicate FIFO is full
  4 output reg              Done,             //used to indicate FIFO is empty
  5 input [word_size - 1:0] Data_Bus,         //host data bus
  6 input                   Load_XMT_datareg, //used by host to load data to FIFO
  7 input                   clk_trans,
  8 input                   Clk,              //clk input
  9 input                   reset            //reset input
 10 );
 11 parameter  word_size = 8;                 //size of data word
 12 parameter  one_hot_count = 3;             //number of one-hot states
 13 parameter  state_count = one_hot_count;   //munber of bits in state register
 14 parameter  size_bit_count = 3;            //size of the bit counter
 15                                           //must count to word_size + 1
 16 
 17 parameter  idle = 3'b001;                 //one-hot state encoding
 18 parameter  waiting = 3'b010;
 19 parameter  sending = 3'b100;
 20 parameter  all_ones = 9'b1_1111_1111;     //word+1 extra bit 
 21 
 22 reg [word_size:0]       XMT_shftreg;      //transmit shift register
 23 reg                     Load_XMT_shftreg; //flag to load XMT_shftreg
 24 reg [state_count - 1:0] state,next_state; //state machine controller
 25 reg [size_bit_count:0]  bit_count;        //counts the bits that are transmitting
 26 reg                     clear;            //clears bit_count after last bit is sent 
 27 reg                     shift;            //causes shift of data in XMT_shftreg
 28 reg                     start;            //signals start of transmission
 29 
 30 wire[word_size - 1:0]   FIFO_Data_Bus;    //output of FIFO
 31 wire                    empty;            //indicates the FIFO is empty
 32 wire                    Byte_ready;       
 33 
 34 fifo_receiver FIFO_TRS(   
 35     .aclr(reset),                      //Dual clock FIFO generated by Altera megafunction wizard
 36     .data(Data_Bus),//
 37     .rdclk(clk_trans),//
 38     .rdreq(Load_XMT_shftreg),//
 39     .wrclk(Clk),//
 40     .wrreq(Load_XMT_datareg),//
 41     .q(FIFO_Data_Bus),//
 42     .rdempty(empty),//
 43     .wrfull(Busy));//
 44 
 45 assign Serial_out = XMT_shftreg[0];
 46 assign Byte_ready = ~empty;
 47 
 48 always@(state or Byte_ready or bit_count )begin:Output_and_next_state
 49  Load_XMT_shftreg = 0;
 50  clear = 0;
 51  shift = 0;
 52  start = 0;
 53  Done  = 0;
 54  next_state = state;
 55  case(state)
 56   idle:          if(Byte_ready == 1)begin
 57                    Load_XMT_shftreg = 1;
 58                    next_state = waiting;
 59                  end 
 60                                  
 61   waiting:       begin
 62                    start = 1;
 63                    next_state = sending;
 64                  end
 65                 
 66   sending:       if(bit_count != word_size + 1)
 67                     shift = 1;
 68                  else if(Byte_ready == 1)begin
 69                     clear = 1;
 70                     Load_XMT_shftreg = 1;
 71                     next_state = waiting;
 72                  end else begin
 73                     clear = 1;
 74                     Done  = 1;
 75                     next_state = idle;
 76                  end
 77                  
 78   default:       next_state = idle;
 79  endcase
 80 end
 81 
 82 always@(posedge clk_trans or posedge reset)begin:State_Transitions
 83   if(reset == 1)state <= idle; else state <= next_state; end
 84   
 85 always@(posedge clk_trans or posedge reset)begin:Register_Transfers
 86   if(reset == 1)begin
 87     XMT_shftreg <= all_ones;
 88     bit_count <= 0;
 89   end else begin   
 90      
 91     if(start == 1)                               //starts the transmission
 92         XMT_shftreg <= {FIFO_Data_Bus,1'b0};     //添加一位起始位0
 93       
 94     if(clear == 1) bit_count <= 0;               
 95     else if(shift == 1)bit_count <= bit_count + 1'b1;
 96     
 97     if(shift == 1)
 98       XMT_shftreg <= { 1'b1,XMT_shftreg[word_size:1]}; //Shift riht, fill with 1's
 99   end
100 end
101 
102 endmodule 
View Code

3. 接收器

本设计采样时钟频率是波特率的8倍,数据有效的中间时刻采样数据,即在第4个采样时钟获取数据。先采样到起始数据0之后,通过一个计数器计满8个时钟采样下一个有效数据。当采样获得8位数据后将8位数据写入FIFO_R,从而产生非空信号准备供发送器FIFO_T读取。

RCV_shftreg <= {Serial_in,RCV_shftreg[word_size - 1:1]};

  1 // ============================================================
  2 // File Name: UART_Receiver.v
  3 // Module Name(s):
  4 //             UART Receiver with FIFO(8x256)
  5 //
  6 // Version: V1.0
  7 // Date: 08/11/2014 
  8 // Author: Wang Zhongwei
  9 // Copyright (C) 1896-2009 Beijing Jiao Tong University
 10 // ************************************************************
 11 // CAUTION:
 12 //   This module can only be used in Altera Quarus II environment.
 13 //   Use megafuncton wizard to generate a dual clock FIFO 
 14 //   called "fifo_receiver" before synthesis this module.
 15 // =============================================================
 16 module UART_Receiver(
 17 output      [word_size - 1:0]      Data_bus_out,
 18 //output      [word_size - 1:0]      RCV_datareg;
 19 output                             empty,
 20 //                                   Error1,Error2;//Error1:asserts if host is not ready to receive data after last bit
 21                                                  //Error2:asserts if the stop-bit is missing
 22 input       Serial_in,Sample_clk,clk_in,reset,read
 23 );
 24 //(Data_bus_out,RCV_datareg,empty,Error1,Error2,Serial_in,read,Sample_clk,clk_in,reset);
 25 //Samlpe _clk is 8x Bit_clk
 26 
 27 parameter   word_size              = 8;
 28 parameter   half_word              = word_size/2;
 29 parameter   Num_counter_bits       = 4;
 30 parameter   Num_state_bits         = 3;
 31 parameter   idle                   = 3'b001;
 32 parameter   starting               = 3'b010;
 33 parameter   receiving              = 3'b100;
 34 
 35 //reg         [word_size - 1:0]      Data_bus_out;            
 36 //reg         [word_size - 1:0]      RCV_datareg;
 37 reg         [word_size - 1:0]      RCV_shftreg;
 38 reg         [Num_counter_bits-1:0] Sample_counter;
 39 reg         [Num_counter_bits:0]   Bit_counter;
 40 reg         [Num_state_bits-1:0]   state,next_state;
 41 reg                                inc_Bit_counter,clr_Bit_counter;
 42 reg                                inc_Sample_counter,clr_Sample_counter;
 43 reg                                shift,load;
 44 //reg                                Error1,Error2;
 45 
 46 //wire                               read_not_ready_in;
 47 //instance of receive FIFO
 48 fifo_receiver FIFO_RCV(
 49     .aclr(reset),
 50     .data(RCV_shftreg),
 51     .rdclk(clk_in),
 52     .rdreq(read),
 53     .wrclk(Sample_clk),
 54     .wrreq(load),
 55     .q(Data_bus_out),
 56     .rdempty(empty),
 57     .wrfull());  //read_not_ready_in
 58 
 59 //we can also use the clk_50M as the clock,while Sample_clk is the enable signal
 60 
 61 reg[3:0] RXD_reg = 4'b1111;
 62 //synchronize the Serial_in(RX),
 63 always @ (posedge Sample_clk or posedge reset)
 64   if(reset) RXD_reg <= 4'b1111;
 65   else RXD_reg <= {RXD_reg[2:0],Serial_in};
 66 
 67 always @ (posedge Sample_clk or posedge reset)
 68   if(reset) state <= idle; else state <= next_state;
 69     
 70 //Combinational logic for next state and conditional outputs
 71 always @ (*) begin
 72   clr_Sample_counter = 1'b0;
 73   clr_Bit_counter = 1'b0;
 74   inc_Sample_counter = 1'b0;
 75   inc_Bit_counter = 1'b0;
 76   shift = 1'b0;
 77 //  Error1 = 1'b0;
 78 //  Error2 = 1'b0;
 79   load = 1'b0;
 80   next_state = state;
 81   
 82   case(state)
 83    idle:         if(RXD_reg == 0) begin next_state = receiving; end  //register 4 seria_in datas to capture the middle sample point of the data
 84                  
 85     receiving:    if(Sample_counter < word_size - 1) inc_Sample_counter = 1'b1;
 86                   else begin
 87                         clr_Sample_counter = 1'b1;
 88                    if(Bit_counter != word_size)begin
 89                      shift = 1'b1;
 90                      inc_Bit_counter = 1'b1;
 91                    end
 92                    else begin
 93                      next_state = idle;
 94                      clr_Bit_counter = 1'b1;
 95                             load = 1'b1;
 96                             //read_not_ready_out = 1'b1;
 97 //                     if(read_not_ready_in == 1'b1) Error1'b1 = 1'b1;
 98 //                     else if(Serial_in == 0) Error2 = 1'b1;
 99 //                     else load = 1'b1;
100                     end
101                  end
102    default:       next_state = idle;
103    
104   endcase
105 end
106 
107 always @ (posedge Sample_clk or posedge reset)begin
108   if(reset)begin
109     Sample_counter <= 3'd0;
110     Bit_counter <= 4'd0;
111 //    RCV_datareg <= 0;
112     RCV_shftreg <= 8'd0;
113   end
114   else begin
115     if(clr_Sample_counter) Sample_counter <= 3'd0;
116     else if(inc_Sample_counter) Sample_counter <= Sample_counter + 1'b1;
117     
118     if(clr_Bit_counter)Bit_counter <= 4'd0;
119     else if(inc_Bit_counter) Bit_counter <= Bit_counter + 1'b1;
120     
121     if(shift) RCV_shftreg <= {Serial_in,RCV_shftreg[word_size - 1:1]};  //RXD_reg[2] RXD_reg[1] RXD_reg[0] Serial_in are all ok
122     
123    // if(load == 1) RCV_datareg <= RCV_shftreg;
124   end
125 end
126 endmodule
127      
View Code
4. 实验结果

在实验板测试UART通信,接收数据个数等于发送数据个数,能满足一般的通信要求。如果想提高准确度,可提高采样频率、增加校验位或者增加停止位个数(1.5或2)。

此外,3个模块可以共使用一个系统时钟clk。接收器和发送器工作时钟可以作为clk的时钟使能信号,这样这个设计就只使用了一个时钟。

原文地址:https://www.cnblogs.com/aikimi7/p/3904133.html