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位停止位,无校验位。
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
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
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
在实验板测试UART通信,接收数据个数等于发送数据个数,能满足一般的通信要求。如果想提高准确度,可提高采样频率、增加校验位或者增加停止位个数(1.5或2)。
此外,3个模块可以共使用一个系统时钟clk。接收器和发送器工作时钟可以作为clk的时钟使能信号,这样这个设计就只使用了一个时钟。