差别

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

到此差别页面的链接

两侧同时换到之前的修订记录 前一修订版
后一修订版
前一修订版
uart_verilog [2020/08/22 15:30]
gongyu [串行接口的Verilog实现]
uart_verilog [2021/02/06 16:25] (当前版本)
gongyusu
行 1: 行 1:
-## 串行接口RS-232 +## 异步收发器UART的Verilog代码 
-串口UART是PC和FPGA通信的最简单的方式,它是一种异步串行/​全双工的通信方式,尤其是目前的PC都是通过USB端口来进行UART数据的传输,可以实现更高的传输速率,比如1.5Mbps。+串口[[UART]]是PC和FPGA通信的最简单的方式,它是一种异步串行/​全双工的通信方式,尤其是目前的PC都是通过USB端口来进行UART数据的传输,可以实现更高的传输速率,比如1.5Mbps。
  
 --- ---
-###异步通信 +### 1. 异步通信的原理 
-####异步发送+ 
 +#### 1.1 异步发送
 {{ ::​serialtxdmodule.gif |}} {{ ::​serialtxdmodule.gif |}}
  
-####异步接收+#### 1.2 异步接收
 {{ ::​SerialRxDmodule.gif |}} {{ ::​SerialRxDmodule.gif |}}
  
行 13: 行 14:
  
 --- ---
-#### RS-232的串行接口是如何工作的?+#### 1.3 RS-232的串行接口是如何工作的?
 一个RS-232接口有如下的特性: 一个RS-232接口有如下的特性:
   * 使用9针的连接器"​DB-9"​ (更老的PCs使用25管脚的"​DB-25"​),现在都在使用USB-UART的接口方式   * 使用9针的连接器"​DB-9"​ (更老的PCs使用25管脚的"​DB-25"​),现在都在使用USB-UART的接口方式
   * 允许双向全双工通信(PC可以同时接收、发送数据).   * 允许双向全双工通信(PC可以同时接收、发送数据).
   * 最高的通信速率可以达到10KBytes/​s.   * 最高的通信速率可以达到10KBytes/​s.
-  * DB-9连接器 +  * DB-9连接器, 它有3根最重要的信号:​
- +
-RS-232有9根管脚, ​有3根最重要的信号:​+
 {{ ::​SerialConnector.jpg |}} {{ ::​SerialConnector.jpg |}}
-  ​* 管脚2: RxD(接收数据). +    ​* 管脚2: RxD(接收数据). 
-  * 管脚3: TxD(发送数据). +    * 管脚3: TxD(发送数据). 
-  * 管脚5: GND(地). +    * 管脚5: GND(地).
 只需要3根线,就可以进行数据的收、发。 只需要3根线,就可以进行数据的收、发。
  
 数据通常以多个8位(我们称之为一个Byte)来进行发送,先将其进行串行化:低位(数据的bit 0)先发送,接着是bit 1, ... 最后是最高位的bit 7。 数据通常以多个8位(我们称之为一个Byte)来进行发送,先将其进行串行化:低位(数据的bit 0)先发送,接着是bit 1, ... 最后是最高位的bit 7。
  
-#### 异步通信: 
 此接口采用异步协议,也就是没有时钟信号与数据一起发送,接收端必须能够对接收到的数据自行进行“定时”提取和判决。 此接口采用异步协议,也就是没有时钟信号与数据一起发送,接收端必须能够对接收到的数据自行进行“定时”提取和判决。
  
行 43: 行 40:
  
 {{ ::​SerialCommunication55.gif |}} {{ ::​SerialCommunication55.gif |}}
- 
  
 一个字节0x55的数值用二进制表示就是01010101,由于先发送最低位(bit-0),线路的变化为:​ 1-0-1-0-1-0-1-0. 一个字节0x55的数值用二进制表示就是01010101,由于先发送最低位(bit-0),线路的变化为:​ 1-0-1-0-1-0-1-0.
行 54: 行 50:
 这些位数很难看出来,可以看出,让接收端知道数据的发送速率是非常重要的。 这些位数很难看出来,可以看出,让接收端知道数据的发送速率是非常重要的。
  
-#### 数据发送能够多? +#### 1.4 数据发送能够多? 
-发送的速度是以波特(每秒少个位)来标称的,例如1000波特意味着每秒1000位,或者说每一位持续时间为1毫秒+发送的速度是以波特(每秒少个位)来标称的,例如1000波特意味着每秒1000位,或者说每一位持续时间为1毫秒
  
 RS-232接口的传输速率不是任意的,它有一些固定的值: RS-232接口的传输速率不是任意的,它有一些固定的值:
行 73: 行 69:
  
  
-### 串行接口的Verilog实现+### 2. 串行接口的Verilog实现
 这里我们用115200波特率,FPGA一般运行在更高的频率,远高于115200Hz,我们需要用FPGA的时钟产生每秒115200个脉冲。 这里我们用115200波特率,FPGA一般运行在更高的频率,远高于115200Hz,我们需要用FPGA的时钟产生每秒115200个脉冲。
  
-传统上RS-232芯片采用1.8432MHz的时钟,可以通过/​16分频轻松到115200Hz以及其它波特率的频率。+传统上RS-232芯片采用1.8432MHz的时钟,可以通过/​16分频能够轻松到115200Hz以及其它波特率的频率。
  
 <code verilog> <code verilog>
行 105: 行 101:
  
  
-#### 参数化的FPGA波特率发生器 +#### 2.1 参数化的FPGA波特率发生器 
-下面的设计为25MHz的系统时钟,使用一个16位的累加器,代码可以通过调节参数灵活定制+下面的设计为25MHz的系统时钟,使用一个16位的累加器,代码可以通过简单地配置一下参数就可以灵活定制,适用不同的系统时钟。
  
 <code verilog> <code verilog>
  
-parameter ClkFrequency = 25000000; // 25MHz +parameter ClkFrequency = 25000000; // 此处为25MHz,使用不同的系统只需要修改这个参数 
-parameter Baud = 115200;+parameter Baud = 115200; ​ //​此处设置波特率为115200,如果使用不同的波特率只需要修改这个参数
 parameter BaudGeneratorAccWidth = 16; parameter BaudGeneratorAccWidth = 16;
 parameter BaudGeneratorInc = (Baud<<​BaudGeneratorAccWidth)/​ClkFrequency;​ parameter BaudGeneratorInc = (Baud<<​BaudGeneratorAccWidth)/​ClkFrequency;​
行 132: 行 128:
  
  
-#### RS-232 发送 +#### 2.2 RS-232的数据发送 
-异步发送的固定参数:8个数据位,2个停止位,无奇偶校验+异步发送的固定参数:8个数据位,2个停止位,无奇偶校验
  
-It works like that:+发送端获取8位的数据,将其串行化(当Txd_start信号被断言的时候),当传输发生的时候“busy”信号会被拉高,在此期间“TxD_start”信号被忽略。
  
-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). +
-Serializing the data +
-To go through the start bit, the 8 data bits, and the stop bits, a state machine seems appropriate.+
 <code verilog> <code verilog>
 reg [3:0] state; reg [3:0] state;
行 163: 行 156:
 </​code>​ </​code>​
  
-Now, we just need to generate the "​TxD" ​output.+现在,我们只需要产生"​TxD"​输出。
 <code verilog> <code verilog>
 reg muxbit; reg muxbit;
行 179: 行 172:
 endcase endcase
  
-// combine start, data, and stop bits together+//将起始位、数据位、停止位结合在一起
 assign TxD = (state<​4) | (state[3] & muxbit); assign TxD = (state<​4) | (state[3] & muxbit);
 </​code>​ </​code>​
  
-The complete code can be found here.+下面是完整的代码[[async_verilog_source|异步串行通信的Verilog源代码]]也可以直接下载解压使用:{{:​async.zip|异步通信的Verilog和C程序完整源代码}}。
  
-### Serial interface 4 - RS-232 ​receiver +#### 2.3 RS-232的数据接收 
-We are building an "async receiver":​ +现在我们来看看数据的接收端。
-Our implementation works like that:+
  
-The module assembles data from the RxD line as it comes. +该模块从RxD线上收集数据。当接收到一个字节时,它出现在“数据”总线上。 一旦接收到一个完整的字节,“data_ready”就会被拉高一个时钟。 
-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. +注意,只有在断言“ ​data_ready”时,“ ​data”才有效。 其余时间,请不要使用它,因为可能会出现新数据,从而使数据混乱。
-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. +接收器通常以16倍波特率对输入信号进行过采样。 我们在这里使用了8...对于115200波特,采样率为921600Hz
-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 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.+假设我们有一个可用的“ ​Baud8Tick”信号,该信号每秒拉高921600次。
  
-The design +具体的设计 
-First, the incoming "RxD" signal has no relationship with our clock. +首先,传入的“ ​RxD”信号与我们的时钟无关。我们使用两个D触发器对其进行过采样,并将其同步到我们的时钟域。
-We use two flip-flops to oversample it, and synchronize it to our clock domain.+
  
 <code verilog> <code verilog>
- 
 reg [1:0] RxD_sync; reg [1:0] RxD_sync;
 always @(posedge clk) if(Baud8Tick) RxD_sync <= {RxD_sync[0],​ RxD}; always @(posedge clk) if(Baud8Tick) RxD_sync <= {RxD_sync[0],​ RxD};
 </​code>​ </​code>​
  
-We filter the data, so that short spikes on the RxD line aren't mistaken with start bits.+我们对数据进行过滤,以免RxD线上的短尖峰误认为起始位。 
 <code verilog> <code verilog>
 reg [1:0] RxD_cnt; reg [1:0] RxD_cnt;
行 230: 行 218:
 </​code>​ </​code>​
  
-A state machine allows us to go through each bit received, once a "​start"​ is detected.+一旦检测到“开始”,状态机就允许我们遍历接收到的每个位。 
 <code verilog> <code verilog>
 reg [3:0] state; reg [3:0] state;
行 251: 行 240:
 </​code>​ </​code>​
  
-Notice that we used a "next_bit" signal, to go from bit to bit.+请注意,我们使用了“ ​next_bit”信号,从一个位到另一个位。
  
 <code verilog> <code verilog>
行 266: 行 255:
 </​code>​ </​code>​
  
-Finally a shift register collects the data bits as they come.+最后,移位寄存器在数据位到来时收集它们。
  
 <code verilog> <code verilog>
行 274: 行 263:
 </​code>​ </​code>​
  
-The complete code can be found here. +下面是完整的代码[[async_verilog_source|异步串行通信的Verilog源代码]]也可以直接下载解压使用:{{:​async.zip|异步通信的Verilog和C程序完整源代码}}。
-It has a few improvements;​ follow the comments in the code.+
  
-Links +#### 2.4 使用RS-232发送和接收数据 
-More details on Asynchronous Communication+在这里我们设计允许通过PC(通过PC的串行端口)控制几个FPGA引脚。
  
-Serial interface 5 - How to use the RS-232 transmitter and receiver +它在FPGA(端口名为“ GPout”)上创建8个输出,GPout由FPGA接收的任何字符更新。FPGA上还有8个输入(端口名为“ GPin”)。 每当FPGA接收到字符时,便发送GPin。
-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. +GP输出可用于从PC远程控制任何东西,可能是LED或咖啡机...等等
-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> <code verilog>
行 306: 行 291:
 </​code>​ </​code>​
  
-Remember to grab the async_receiver and async_transmitter modules here, and to update the clock frequency values inside.