通用异步收发传输器(Universal Asynchronous Receiver / Transmitter),通常称作UART,是一种异步收发传输器。它在串行通信和并行通信之间转换要传输的数据。作为将并行输入信号转换为串行输出信号的芯片,UART通常集成在其他通信接口的连接上。
UART异步通信是一种通用串行数据总线。该总线的双向通信可实现全双工传输和接收。嵌入式设计,UART用于通信主机和辅助设备,如汽车音响和外部AP通信,和PC通信包括监控调试器和其他设备。
两个芯片或设备之间的传输信息称为通信。可能有很多信息,比如传输ASCII码(8bit)。在设计中,我们可以在两个通信设备之间设计8条数据线bit同时发送数据,对方同时接收8位数据。这种同时发送多个数据的传输方式称为并行通信。
由于某些原因,设备A和设备B不能设计多条数据线,只能设计一条数据线。如果此时仍需要传输ASCII码,那该怎么办?
可将设备AASCII代码的8位,按一定顺序逐一发送到数据线。设备B按设备A发送的顺序逐一接收,然后拼接成8位。这种通信方式成为串行通信。
将8个或多个数据分成一个发送的过程称为并转串。将一个接收到的数据合并为8个或多个数据的过程称为串转并。
对于串行通信设备,发送方正在执行并转串,接收方正在执行串行并转串。
UART该设备是串行通信设备。
全双工通信是指信息可以同时双向传输。例如:打电话,同时听。
半双工通信是指信息只能同时单向传输,双方都可以发送和接收,但不能同时发送和接收。例如:对讲机通信。
单工通信是指在通信过程中,只能发送设备A,接收设备B。例如:听收音机。
SANXIN – B01的开发板上的UART接口设备可实现半双工通信。
两个设备之间相互通信的基本条件是相同的电平标准。UART接口标准有很多,包括RS232、RS485等等。
台式PC一般上面会有一个DB9、接口标准为RS232。
各工业板上也有许多接口。随着技术的发展,PC上的DB9的接口逐渐被淘汰,换成了USB接口。
选择使用我们的开发板USB界面,方便大家学习,方便和谐PC进行通信。
FPGA芯片RS485、RS232、USB接口电平等。在大多数板卡设计中FPGA将电平转换器添加到外围,并将其添加到外围FPGA将电平标准转换为通信电平标准。
在我们的开发板上使用USB <->UART(LVCOMS/LVTTL)电平转换芯片CP2102。因此,开发板上的供电端口不仅可以供电,还可以通信。
对于开发者来说,不需要考虑线路的电平标准,只需要考虑如何发送和接收逻辑。
当双方进行通信时,他们并不总是进行通信,而且大多数通信都是突然的。此时,发送设备需要在发送有效性前提前通知接收设备接收信息。有些人会有问题,发送者有信息发送,没有信息停止;接收设备检测信息发送接收,没有信息不接收;不是吗?为什么要提前通知信息?
由于双方可以进行通信,通信线路必然会在中间建立。当发送人有信息时,发送信息;停止发送无信息。对于发送人来说,没有问题。接收人相对困难。在通信线路中,当发送人不发送信息时,接收人也会在线路上收到信息,因为接收人不知道发送人是否发送信息,这将导致接收人无法判断信息或噪音。
为解决上述问题,我们规定了通信协议。
在UART在通信协议中,我们规定:
- 不通信时,发送高电平。
- 发送信息时,位(1)bit、低电平)。可以理解为告诉接收方应该接收信息。
- 发送数据位,由于是串行通信,规定从低开始(协议规定信息位可以是4、5、6、7、8)。
- 校验位(1bit)。可采用奇校验、偶校验、直发1、直发0、不发等五种情况。
- 停止位(1bit、1.5bit、2bit。高电平)。
- 空闲位(1bit,高电平)
1bit时间宽度是多少?
在UART在协议中,常用的波特率(BAUD)为300、600、1200、2400、4800、9600、19200、38400、43000、56000、57600、115200。1秒除以波特率。bit时间宽度。
验证位有什么作用?如何验证?
在发送信息时,由于需要通过长线,中间很可能会受到干扰,导致某些信息位置反转,最终导致通信失败。验证位的作用是在接收到数据后进行检查。如果检查失败,则视为接收数据错误,可直接丢弃。
在验证过程中,您可以选择奇怪的验证和偶像验证。奇怪的验证是要求发送的数据位和验证位1的数量,偶像验证是要求发送的数据位和验证位1的数量。
在发送器中添加缓冲器。即上游模块将要发送的数据写入发送器FIFO检测到发送器的控制逻辑FIFO当有数据时,读取并发送。由于发送器发送速度慢,添加FIFO之后,上游模块可以直接写入后续数据,无需等待上一个数据发送完成。
发送器的控制逻辑根据事先约定的波特率和验证方法进行读取FIFO按数据UART向外发送协议。
在接收器中添加缓冲器。也就是说,接收器的控制逻辑在接收到信息后发送到缓冲器。由于缓冲器的存在,主控制器可以检查缓冲器中是否有数据,而无需始终检查接收状态。
在接收过程中,开始位置的低电平持续时间应超过半个周期,以避免线路干扰和错误接收。
在接收数据位、验证位和停止位时,采用倍频(16倍频)采样,中间采样值为6、7、8、9、10。当采样依据。当五次全部为同一电平时,本位为此电平值;当四次相同,一次不同时,本位与四次相同的电平值相同;的电平值相同;当发生其他情况时,认为线路干扰
接收完成后,进行帧检测和验证,满足设计要求时,将数据写入FIFO中。
该模块命名为uart_drive,由四个模块组成。
tx_fifo模块:发送缓冲区256深度,宽度为8,设计高电平有效复位。负责缓存上游要发送的数据。
tx_ctrl模块:发送逻辑控制部分。tx_fifo根据中间的数据UART发送协议规定。
rx_ctrl模块:接收逻辑控制部分。根据外部数据在线数据负责UART解析协议规定,存储到tx_fifo中。
rx_fifo模块:接收缓冲区256的深度和宽度为8,设计高电平有效复位。负责缓存接收逻辑控制部分分析的数据,等待控制器件的读取。
调用tx_fifo和7.四节方法相似,其他步骤不同,下面给出具体说明。
如果在设计中不使用许多标志信号,则不再引出。
引出清除信号(高电平有效),使清除信号与读时钟同步。
调用rx_fifo和7.四节方法相似,其他步骤不同,下面给出具体说明。
如果在设计中不使用许多标志信号,则不再引出。
引出清除信号(高电平有效),使清除信号与写时钟同步。
参数PARITY为选择的验证方法,1为奇验证,0为偶验证。
参数BAUD波特率的选择。
参数F_clk参考时钟频率。
参数T是波特率规定的时间,需要计数多个参考时钟周期。
设计代码为:
module tx_ctrl ( input wire clk, input wire rst_n, input wire tx_fifo_rdempty, output reg tx_fifo_rden, input wire [7:0] tx_fifo_rdata, output reg uart_txd ); parameter PARITY = 1; parameter BAUD = 9600; parameter F_clk = 50_000_000; localparam T = F_clk/BAUD; reg tx_en; reg tx_done; reg [25:0] baud_cnt; reg [3:0] bit_cnt; always @ (posedge clk, negedge rstn) begin
if (rst_n == 1'b0)
baud_cnt <= 26'd0;
else
if (tx_en == 1'b1 && baud_cnt < T - 1'b1)
baud_cnt <= baud_cnt + 1'b1;
else
baud_cnt <= 26'd0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
bit_cnt <= 4'd0;
else
if (tx_en == 1'b1)
if (baud_cnt == 26'd1)
bit_cnt <= bit_cnt + 1'b1;
else
bit_cnt <= bit_cnt;
else
bit_cnt <= 4'd0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
tx_done <= 1'b0;
else
if (bit_cnt == 4'd13 && baud_cnt == 26'd1)
tx_done <= 1'b1;
else
tx_done <= 1'b0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
tx_en <= 1'b0;
else
if (tx_en == 1'b0 && tx_fifo_rdempty == 1'b0)
tx_en <= 1'b1;
else
if (tx_done == 1'b1)
tx_en <= 1'b0;
else
tx_en <= tx_en;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
tx_fifo_rden <= 1'b0;
else
if (tx_en == 1'b0 && tx_fifo_rdempty == 1'b0)
tx_fifo_rden <= 1'b1;
else
tx_fifo_rden <= 1'b0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
uart_txd <= 1'b1;
else
case (bit_cnt)
4'd0 : uart_txd <= 1'b1; // no busy
4'd1 : uart_txd <= 1'b0; // start
4'd2 : uart_txd <= tx_fifo_rdata[0]; // bit0
4'd3 : uart_txd <= tx_fifo_rdata[1];
4'd4 : uart_txd <= tx_fifo_rdata[2];
4'd5 : uart_txd <= tx_fifo_rdata[3];
4'd6 : uart_txd <= tx_fifo_rdata[4];
4'd7 : uart_txd <= tx_fifo_rdata[5];
4'd8 : uart_txd <= tx_fifo_rdata[6];
4'd9 : uart_txd <= tx_fifo_rdata[7]; // bit7
4'd10 : uart_txd <= PARITY ? ~(^tx_fifo_rdata) : ^tx_fifo_rdata; // parity bit
4'd11 : uart_txd <= 1'b1; // stop
4'd12 : uart_txd <= 1'b1; // no busy
4'd13 : uart_txd <= 1'b1; // no busy
default : uart_txd <= 1'b1;
endcase
end
endmodule
tx_en为发送标志信号,当发送逻辑处于不发送状态时,并且tx_fifo中不空,就将tx_en拉高,启动发送逻辑。当发送完成后,拉高tx_done,将tx_en拉低。其他时间tx_en保持不变。
tx_fifo_rden为tx_fifo的读使能信号,拉高一拍,读出一个数据,所以每次只能拉高一拍。在tx_en为低器件,且外部tx_fifo中有数据时,拉高tx_fifo_rden。tx_fifo_rden和tx_en拉高的条件相同,故而会同步拉高,下一拍时,tx_en会变为高电平,所以此时tx_fifo_rden只会拉高一拍。
baud_cnt是为了记录每发送1bit时间宽度的计数器。在发送使能tx_en拉高后,baud_cnt就开始不断的计数即可。
bit_cnt为此时应该发送UART协议中哪一位的计数器,此计数器在发送使能拉高后,baud_cnt每次计数到1时,bit_cnt进行加1。由于baud_cnt为循环计数,无论在什么时刻bit_cnt加1,后续加1的时间间隔都是一个bit时间宽度。为了能够使tx_en一旦拉高,发送逻辑能够快速发送起始位,所以本设计中选择1。
tx_done信号为发送完成信号,当bit_cnt等于13(起始位1bit、数据位8bit、校验位1bit,停止位1bit和空闲位1bit,共计12bit。本设计中bit_cnt为1时,发送起始位;bit_cnt为12时,发送空闲位)时,证明所有的bit位都已经发送完成,将tx_done拉高。
在算术运算中,假设data的位宽为3,^data=data[1] ^ data[1] ^ data[0],这种运算规则称为缩减运算符。缩减运算符还有“&”和“|”。如果data中1的个数为奇数个,那么缩减异或之后的记过为1,否则为0。当采用奇校验时,数据位和校验位的1个数为奇数,所以校验位应该是~(^tx_fifo_rdata)。当采用偶校验时,数据位和校验位的1个数为偶数,所以校验位应该是^tx_fifo_rdata。
参数PARITY为选择的校验方式,1表示为奇校验,0表示为偶校验。
参数BAUD为选择的波特率。
参数F_clk为参考的时钟频率。
参数T为需要计数多个参考时钟周期才可以到16倍波特率规定的时间。
由于外部uart_rxd的信号为异步信号,首先需要打两拍。
设计代码为:
module rx_ctrl (
input wire clk,
input wire rst_n,
input wire uart_rxd,
output reg [7:0] rx_fifo_data,
output reg rx_fifo_wren
);
parameter PARITY = 1;
parameter BAUD = 9600;
parameter F_clk = 50_000_000;
localparam T = F_clk/(16*BAUD);
reg rxd_r;
reg rxd_rr;
reg rx_en;
reg rx_done;
reg [25:0] start_cnt;
reg [25:0] baudx16_cnt;
reg [7:0] cap_cnt;
reg [151:0] cap_buf;
reg [8:0] rx_buf;
reg [8:0] rx_error;
reg rx_done_r;
initial rxd_r = 1'b1;
initial rxd_rr = 1'b1;
always @ (posedge clk) rxd_r <= uart_rxd;
always @ (posedge clk) rxd_rr <= rxd_r;
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
start_cnt <= 26'd0;
else
if (rx_en == 1'b0 && rxd_rr == 1'b0)
start_cnt <= start_cnt + 1'b1;
else
start_cnt <= 26'd0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
rx_en <= 1'b0;
else
if (start_cnt == 8 * T)
rx_en <= 1'b1;
else
if (rx_done == 1'b1)
rx_en <= 1'b0;
else
rx_en <= rx_en;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
baudx16_cnt <= 26'd0;
else
if (rx_en == 1'b1 && baudx16_cnt < T - 1'b1)
baudx16_cnt <= baudx16_cnt + 1'b1;
else
baudx16_cnt <= 26'd0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
cap_buf <= 152'd0;
else
if (rx_en == 1'b1 && baudx16_cnt == T - 1'b1)
cap_buf <= {cap_buf[150:0], rxd_rr};
else
cap_buf <= cap_buf;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
cap_cnt <= 8'd0;
else
if (rx_en == 1'b1)
if (baudx16_cnt == T - 1'b1)
cap_cnt <= cap_cnt + 1'b1;
else
cap_cnt <= cap_cnt;
else
cap_cnt <= 8'd0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
rx_done <= 1'b0;
else
if (cap_cnt == 8'd152 && baudx16_cnt == 26'd2)
rx_done <= 1'b1;
else
rx_done <= 1'b0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0) begin
rx_buf <= 9'd0;
rx_error <= 9'd0;
end
else
if (rx_done == 1'b1) begin
{rx_error[8],rx_buf[8]} <= decoder_rxd(cap_buf[10:6]);
{rx_error[7],rx_buf[7]} <= decoder_rxd(cap_buf[26:22]);
{rx_error[6],rx_buf[6]} <= decoder_rxd(cap_buf[42:38]);
{rx_error[5],rx_buf[5]} <= decoder_rxd(cap_buf[58:54]);
{rx_error[4],rx_buf[4]} <= decoder_rxd(cap_buf[74:70]);
{rx_error[3],rx_buf[3]} <= decoder_rxd(cap_buf[90:86]);
{rx_error[2],rx_buf[2]} <= decoder_rxd(cap_buf[106:102]);
{rx_error[1],rx_buf[1]} <= decoder_rxd(cap_buf[122:118]);
{rx_error[0],rx_buf[0]} <= decoder_rxd(cap_buf[138:134]);
end
else begin
rx_error <= rx_error;
rx_buf <= rx_buf;
end
end
function [1:0] decoder_rxd;
input [4:0] data;
reg [2:0] num;
begin
num = data[4] + data[3] + data[2] + data[1] + data[0];
decoder_rxd[1] = (num < 3'd4)&&(num > 3'd1) ? 1'b1 : 1'b0;
decoder_rxd[0] = (num > 3'd3) ? 1'b1 : 1'b0;
end
endfunction
initial rx_done_r = 1'b0;
always @ (posedge clk) rx_done_r <= rx_done;
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
rx_fifo_data <= 8'd0;
else
if (rx_done_r == 1'b1)
rx_fifo_data <= rx_buf[7:0];
else
rx_fifo_data <= rx_fifo_data;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
rx_fifo_wren <= 1'b0;
else
if (rx_done_r == 1'b1 && (|rx_error == 1'b0) && ^rx_buf == PARITY)
rx_fifo_wren <= 1'b1;
else
rx_fifo_wren <= 1'b0;
end
endmodule
start_cnt为记录在没有启动接收时,低电平的持续时间。
当start_cnt的低电平持续时间等于8个T时(16个T为一个bit的时间宽度),认为此起始位有效,拉高rx_en。当接收完成后,rx_en拉低,其他时间,rx_en保持不变。
当rx_en拉高后,baudx16_cnt不断开始计数,最大值为16倍频的宽度值。
当rx_en拉高后,且baudx16_cnt为最大值时,就开始进行移位采样。由于起始位只判断一半,所以半个起始位、8个数据位、1个奇偶校验位,在16倍频采样的情况下,一共会采样152次。
cap_cnt为采样的计数,当采样到152时,且baudx16_cnt等于2时,认为采样结束。利用baudx16_cnt等于2只是为了产生的tx_done为一个脉冲。
采样结束后,利用函数的特性,得出8个数据位、1个校验位,并且得出9个线路是不是出现干扰的标志rx_error。
得到最终结果后,将数据进行输出。
产生rx_fifo_wren时,进行了线路干扰检测判断,停止位判断,以及奇偶校验判断,当都符合预期后,输出为1。其他情况输出为0。
顶层设计只负责将上述四个模块按照架构图的方式进行连接。
设计代码为:
module uart_drive (
input wire clk,
input wire rst_n,
input wire tx_clk,
input wire tx_en,
input wire [7:0] tx_data,
output wire tx_full,
output wire uart_txd,
input wire uart_rxd,
input wire rx_clk,
output wire rx_empty,
input wire rx_en,
output wire [7:0] rx_data
);
parameter PARITY = 1;
parameter BAUD = 9600;
parameter F_clk = 50_000_000;
wire tx_fifo_rdempty;
wire tx_fifo_rden;
wire [7:0] tx_fifo_rdata;
wire rx_fifo_wren;
wire [7:0] rx_fifo_data;
tx_fifo tx_fifo_inst (
.aclr ( ~rst_n ),
.data ( tx_data ),
.rdclk ( clk ),
.rdreq ( tx_fifo_rden ),
.wrclk ( tx_clk ),
.wrreq ( tx_en ),
.q ( tx_fifo_rdata ),
.rdempty ( tx_fifo_rdempty ),
.wrfull ( tx_full )
);
tx_ctrl # (
.PARITY (PARITY),
.BAUD (BAUD),
.F_clk (F_clk)
)tx_ctrl_inst(
.clk (clk),
.rst_n (rst_n),
.tx_fifo_rdempty(tx_fifo_rdempty),
.tx_fifo_rden (tx_fifo_rden),
.tx_fifo_rdata(tx_fifo_rdata),
.uart_txd (uart_txd)
);
rx_ctrl # (
.PARITY (PARITY),
.BAUD (BAUD),
.F_clk (F_clk)
) rx_ctrl_inst (
.clk (clk),
.rst_n (rst_n),
.uart_rxd (uart_rxd),
.rx_fifo_data (rx_fifo_data),
.rx_fifo_wren (rx_fifo_wren)
);
rx_fifo rx_fifo_inst (
.aclr ( ~rst_n ),
.data ( rx_fifo_data ),
.rdclk ( rx_clk ),
.rdreq ( rx_en ),
.wrclk ( clk ),
.wrreq ( rx_fifo_wren ),
.q ( rx_data ),
.rdempty ( rx_empty )
);
endmodule
parameter所定义的参数,在例化时,可以对它进行重新赋值,方便我们参数化设计。
在仿真中,将uart_rxd和uart_txd相连接,实现自发自收。
对于tx_clk和rx_clk都采用clk连接。
仿真代码如下:
`timescale 1ns/1ps
module uart_drive_tb;
reg clk;
reg rst_n;
reg tx_en;
reg [7:0] tx_data;
wire tx_full;
wire uart_sda;
reg rx_en;
wire rx_empty;
wire [7:0] rx_data;
uart_drive uart_drive_inst(
.clk (clk),
.rst_n (rst_n),
.tx_clk (clk),
.tx_en (tx_en),
.tx_data (tx_data),
.tx_full (tx_full),
.uart_txd (uart_sda),
.uart_rxd (uart_sda),
.rx_clk (clk),
.rx_empty (rx_empty),
.rx_en (rx_en),
.rx_data (rx_data)
);
initial clk = 1'b0;
always # 10 clk = ~clk;
initial begin
rst_n = 1'b0;
tx_data = 8'd0;
tx_en = 1'b0;
rx_en = 1'b0;
# 201
rst_n = 1'b1;
# 200;
repeat (5) begin
@ (posedge clk);
# 2;
tx_data = {$random} % 256;
tx_en = 1'b1;
end
@ (posedge clk);
# 2;
tx_data = 8'd0;
tx_en = 1'b0;
repeat (5) begin
@ (negedge rx_empty);
# 2;
rx_en = 1'b1;
@ (posedge clk);
# 2;
rx_en = 1'b0;
# 100;
end
# 50000;
$stop;
end
endmodule
复位结束后,采用写入随机数的方式,写入了五个数据。从RTL仿真图中可以看到这个五个数据为24、81、09、63、0d。
接收端口时刻监测rx_empty是不是为假值,一旦为假值,就证明有接收到数据,立刻拉高一拍rx_en,进行读出。大概经过十几毫秒后,仿真会自动停止。
从RTL仿真图中可以看到,读出的数据为24、81、09、63、0d这五个数据,和我们写入的相同。
由于此设计外设接口众多,并且在使用时,都是由上游控制器进行控制。本小节编写上游控制器,实现回环测试(将接收到的数据,全部在发送出去)。
在测试时,rx_clk和tx_clk都采用系统时钟。
本模块命名为uart_drive_example。
test_ctrl模块负责监控rx_empty是否为假值,一旦有数据接收到就可以读出,发送到发送缓冲区中。
此模块采用状态实现。共分为WAIT_RX(等待UART接收数据),WAIT_RD(等待读数据),SEND(发送数据)。
将rx_en置高后,rx_data需要等待一拍才会有效。
状态转移图如下:
设计代码如下:
module test_ctrl (
input wire clk,
input wire rst_n,
input wire rx_empty,
output reg rx_en,
input wire [7:0] rx_data,
output reg tx_en,
output reg [7:0] tx_data
);
localparam WAIT_RX = 3'b001;
localparam WAIT_RD = 3'b010;
localparam SEND = 3'b100;
reg [2:0] c_state;
reg [2:0] n_state;
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
c_state <= 3'd0;
else
c_state <= n_state;
end
always @ * begin
case (c_state)
WAIT_RX : begin
if (rx_empty == 1'b1)
n_state = WAIT_RX;
else
n_state = WAIT_RD;
end
WAIT_RD : begin
n_state = SEND;
end
SEND : begin
n_state = WAIT_RX;
end
default : n_state = WAIT_RX;
endcase
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
rx_en <= 1'b0;
else
if (c_state == WAIT_RX && rx_empty == 1'b0)
rx_en <= 1'b1;
else
rx_en <= 1'b0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
tx_en <= 1'b0;
else
if (c_state == SEND)
tx_en <= 1'b1;
else
tx_en <= 1'b0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
tx_data <= 8'd0;
else
if (c_state == SEND)
tx_data <= rx_data;
else
tx_data <= 8'd0;
end
endmodule
uart_drive_example负责将test_ctrl和uart_drive联系起来。
设计代码如下:
module uart_drive_example (
input wire clk,
input wire rst_n,
input wire uart_rxd,
output wire uart_txd
);
wire rx_empty;
wire rx_en;
wire [7:0] rx_data;
wire tx_en;
wire [7:0] tx_data;
test_ctrl test_ctrl_inst(
.clk (clk),
.rst_n (rst_n),
.rx_empty (rx_empty),
.rx_en (rx_en),
.rx_data (rx_data),
.tx_en (tx_en),
.tx_data (tx_data)
);
uart_drive uart_drive_inst(
.clk (clk),
.rst_n (rst_n),
.tx_clk (clk),
.tx_en (tx_en),
.tx_data (tx_data),
.tx_full (),
.uart_txd (uart_txd),
.uart_rxd (uart_rxd),
.rx_clk (clk),
.rx_empty (rx_empty),
.rx_en (rx_en),
.rx_data (rx_data)
);
endmodule
将uart_drive_example设置为顶层。
在file界面,右击uart_drive_example文件,选择set as top level……。
进行综合分析后,分配管脚,形成配置文件。
将开发板与电脑相连接,打开设备管理器。可以看到在其他设备中出现了CP2102 USB to UART Bridge Controller,并且前面有一个黄色的感叹号,标志着此端口还不能使用。
在我们的开发板上,使用的USB <->UART的芯片就是CP2102,所以在此需要安装驱动。
打开04_串口驱动,安装CP210x_windows_drivers。
文件中有两个安装程序。一个是CP210xVCPInstaller_x64,另外一个是CP210xVCPInstaller_x86。此时我们需要查看自己电脑的系统是多少位的,打开控制面板中的系统就可以看自己的电脑是多少位的操作系统。
64位的操作系统,安装CP210xVCPInstaller_x64,32位的操作系统安装CP210xVCPInstaller_x86。
双击对应的安装程序后,点击下一步。
点击“我接受”,点击下一步。
等待一段时间后,选择完成即可。
此时对开发板进行断电再上电的处理,就可以在设备管理的端口(COM和LPT)中看到安装好的程序,并且记住后面的COM口的编号,一会儿需要使用。在此,笔者的PC上的COM口为COM3。大家好,我是【FPGA功夫熊猫】精益求精,不断推荐好文章。