差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

两侧同时换到之前的修订记录 前一修订版
后一修订版
前一修订版
rs-232 [2017/03/26 19:42]
gongyu [发送器]
rs-232 [2017/03/26 20:00] (当前版本)
gongyu [应用案例]
行 170: 行 170:
 ====== 发送器 ====== ====== 发送器 ======
 We are building an "async transmitter"​ with fixed parameters: 8 data bits, 2 stop bits, no-parity. We are building an "async transmitter"​ with fixed parameters: 8 data bits, 2 stop bits, no-parity.
- +{{ :serialtxdmodule.gif |}}
-{{ :serialcommunication.gif |}}+
  
 It works like that: It works like that:
  
-The transmitter takes an 8-bits data inside the FPGA and serializes it (starting when the "​TxD_start"​ signal is asserted). +  * The transmitter takes an 8-bits data inside the FPGA and serializes it (starting when the "​TxD_start"​ signal is asserted). 
-The "​busy"​ signal is asserted while a transmission occurs (the "​TxD_start"​ signal is ignored during that time). +  ​* ​The "​busy"​ signal is asserted while a transmission occurs (the "​TxD_start"​ signal is ignored during that time).
-Serializing the data+
  
-To go through ​the start bit, the 8 data bits, and the stop bits, a state machine seems appropriate.+===== Serializing ​the data =====
  
 +To go through the start bit, the 8 data bits, and the stop bits, a state machine seems appropriate.
 +<code verilog>
 reg [3:0] state; reg [3:0] state;
  
行 200: 行 200:
   default: if(BaudTick) state <= 4'​b0000;​   default: if(BaudTick) state <= 4'​b0000;​
 endcase endcase
 +</​code>​
 +
 Now, we just need to generate the "​TxD"​ output. Now, we just need to generate the "​TxD"​ output.
  
 +<code verilog>
 reg muxbit; reg muxbit;
  
行 218: 行 221:
 // combine start, data, and stop bits together // combine start, data, and stop bits together
 assign TxD = (state<​4) | (state[3] & muxbit); assign TxD = (state<​4) | (state[3] & muxbit);
 +
 +</​code>​
  
 ====== 接收器 ====== ====== 接收器 ======
 +We are building an "async receiver":​
 +
 +{{ :​serialrxdmodule.gif |}}
 +
 +Our implementation works like that:
 +
 +  * The module assembles data from the RxD line as it comes.
 +  * As a byte is being received, it appears on the "​data"​ bus. Once a complete byte has been received, "​data_ready"​ is asserted for one clock.
 +
 +Note that "​data"​ is valid only when "​data_ready"​ is asserted. The rest of the time, don't use it as new data may come that shuffles it.
 +
 +===== Oversampling =====
 +
 +An asynchronous receiver has to somehow get in-sync with the incoming signal (it normally doesn'​t have access to the clock used by the transmitter).
 +
 +To determine when a new data byte is coming, we look for the "​start"​ bit by oversampling the signal at a multiple of the baud rate frequency.
 +Once the "​start"​ bit is detected, we sample the line at the known baud rate to acquire the data bits.
 +Receivers typically oversample the incoming signal at 16 times the baud rate. We use 8 times here... For 115200 bauds, that gives a sampling rate of 921600Hz.
 +
 +Let's assume that we have a "​Baud8Tick"​ signal available, asserted 921600 times a second.
 +
 +===== The design =====
 +
 +First, the incoming "​RxD"​ signal has no relationship with our clock.
 +We use two D flip-flops to oversample it, and synchronize it to our clock domain.
 +
 +<code verilog>
 +reg [1:0] RxD_sync;
 +always @(posedge clk) if(Baud8Tick) RxD_sync <= {RxD_sync[0],​ RxD};
 +</​code>​
 +
 +We filter the data, so that short spikes on the RxD line aren't mistaken with start bits.
 +<code verilog>
 +reg [1:0] RxD_cnt;
 +reg RxD_bit;
 +
 +always @(posedge clk)
 +if(Baud8Tick)
 +begin
 +  if(RxD_sync[1] && RxD_cnt!=2'​b11) RxD_cnt <= RxD_cnt + 1;
 +  else 
 +  if(~RxD_sync[1] && RxD_cnt!=2'​b00) RxD_cnt <= RxD_cnt - 1;
 +
 +  if(RxD_cnt==2'​b00) RxD_bit <= 0;
 +  else
 +  if(RxD_cnt==2'​b11) RxD_bit <= 1;
 +end
 +</​code>​
 +
 +A state machine allows us to go through each bit received, once a "​start"​ is detected.
 +<code verilog>
 +reg [3:0] state;
 +
 +always @(posedge clk)
 +if(Baud8Tick)
 +case(state)
 +  4'​b0000:​ if(~RxD_bit) state <= 4'​b1000;​ // start bit found?
 +  4'​b1000:​ if(next_bit) state <= 4'​b1001;​ // bit 0
 +  4'​b1001:​ if(next_bit) state <= 4'​b1010;​ // bit 1
 +  4'​b1010:​ if(next_bit) state <= 4'​b1011;​ // bit 2
 +  4'​b1011:​ if(next_bit) state <= 4'​b1100;​ // bit 3
 +  4'​b1100:​ if(next_bit) state <= 4'​b1101;​ // bit 4
 +  4'​b1101:​ if(next_bit) state <= 4'​b1110;​ // bit 5
 +  4'​b1110:​ if(next_bit) state <= 4'​b1111;​ // bit 6
 +  4'​b1111:​ if(next_bit) state <= 4'​b0001;​ // bit 7
 +  4'​b0001:​ if(next_bit) state <= 4'​b0000;​ // stop bit
 +  default: state <= 4'​b0000;​
 +endcase
 +</​code>​
 +
 +Notice that we used a "​next_bit"​ signal, to go from bit to bit.
 +<code verilog>
 +reg [2:0] bit_spacing;​
 +
 +always @(posedge clk)
 +if(state==0)
 +  bit_spacing <= 0;
 +else
 +if(Baud8Tick)
 +  bit_spacing <= bit_spacing + 1;
 +
 +wire next_bit = (bit_spacing==7);​
 +Finally a shift register collects the data bits as they come.
 +
 +reg [7:0] RxD_data;
 +always @(posedge clk) if(Baud8Tick && next_bit && state[3]) RxD_data <= {RxD_bit, RxD_data[7:​1]};​
 +</​code>​
 +
 +
 ====== 应用案例 ====== ====== 应用案例 ======
 +This design allows controlling a few FPGA pins from your PC (through your PC's serial port).
 +
 +  * It create 8 outputs on the FPGA (port named "​GPout"​). GPout is updated by any character that the FPGA receives.
 +  * Also 8 inputs on the FPGA (port named "​GPin"​). GPin is transmitted every time the FPGA receives a character.
 +
 +The GP outputs can be used to control anything remotely from your PC, might be LEDs or a coffee machine...
 +<code verilog>
 +module serialGPIO(
 +    input clk,
 +    input RxD,
 +    output TxD,
 +
 +    output reg [7:0] GPout, ​ // general purpose outputs
 +    input [7:0] GPin  // general purpose inputs
 +);
 +
 +wire RxD_data_ready;​
 +wire [7:0] RxD_data;
 +async_receiver RX(.clk(clk),​ .RxD(RxD), .RxD_data_ready(RxD_data_ready),​ .RxD_data(RxD_data));​
 +always @(posedge clk) if(RxD_data_ready) GPout <= RxD_data;
 +
 +async_transmitter TX(.clk(clk),​ .TxD(TxD), .TxD_start(RxD_data_ready),​ .TxD_data(GPin));​
 +endmodule
 +
 +</​code>​
 +
 +Remember to grab the async_receiver and async_transmitter modules here, and to update the clock frequency values inside.