差别
这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
uart_verilog [2020/08/22 16:15] 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个停止位,无奇偶校验。 | ||
- | 发送端获取8位的数据,将其串行化(当Txd_start信号被断言的时候),当传输发生的时候“busy”信号会被断言,在此期间“TxD_start”信号被忽略。 | + | 发送端获取8位的数据,将其串行化(当Txd_start信号被断言的时候),当传输发生的时候“busy”信号会被拉高,在此期间“TxD_start”信号被忽略。 |
采用状态机进行发送比较合适: | 采用状态机进行发送比较合适: | ||
行 180: | 行 176: | ||
</code> | </code> | ||
- | 下面是完整的代码: | + | 下面是完整的代码[[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 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. | + | 假设我们有一个可用的“ Baud8Tick”信号,该信号每秒拉高921600次。 |
- | The design | + | 具体的设计 |
- | First, the incoming "RxD" signal has no relationship with our clock. | + | 首先,传入的“ RxD”信号与我们的时钟无关。我们使用两个D触发器对其进行过采样,并将其同步到我们的时钟域。 |
- | We use two D 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; | ||
行 227: | 行 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; | ||
行 248: | 行 240: | ||
</code> | </code> | ||
- | Notice that we used a "next_bit" signal, to go from bit to bit. | + | 请注意,我们使用了“ next_bit”信号,从一个位到另一个位。 |
<code verilog> | <code verilog> | ||
行 263: | 行 255: | ||
</code> | </code> | ||
- | Finally a shift register collects the data bits as they come. | + | 最后,移位寄存器在数据位到来时收集它们。 |
<code verilog> | <code verilog> | ||
行 271: | 行 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> | ||
行 303: | 行 291: | ||
</code> | </code> | ||
- | Remember to grab the async_receiver and async_transmitter modules here, and to update the clock frequency values inside. |