三.常用外设接口
1.UART
串口介绍
串口大家都已经非常熟悉了这里简单带大家复习一下,以及补充一些我们在用单片机上使用串口与在FPGA上的区别
1.串口分为同步串口(USRT)与异步串口(UART),区别在于是否需要时钟线。
2.数据帧:一帧数据帧有十位起始低电平,停止高电平中间八位数据位,空闲状态为高电平。
3.波特率与比特率:波特率即每秒传输的码元数符号为Baud单位Bps波特每秒,由于串口是1bit进行传输,一个码元就是一个二进制数。每秒传输的信息量为传输速率简称比特率,单位为bps。 比特率 = 波特率 * 单个调制状态对应的二进制位数
老式的电脑配备有RS232或者RS485接口,RS232单端输入输出只是将电平转换,而RS485是单端转差分,如今大多是USB接口,将USB的差分信号与串口的TTL信号互相转化。
由于AX7020的USB转串口在PS端而我有懒得去找USB转TTL模块故本次只有仿真没有上板。
实验任务
本节实验任务是上位机通过串口调试助手发送数据给AX7020开发板,AX7020开发板 PL 端通过USB_UART 串口接收数据并将接收到的数据发送给上位机,完成串口数据环回。
程序设计
顶层模块设计
module uart_loopback(
input sys_clk ,
input sys_rst_n ,
//UART端口
input uart_rxd ,
output uart_txd
);
//parameter
parameter CLK_FREQ = 50000000;
parameter UART_BPS = 115200 ;
//wire define
wire uart_rx_done;
wire [7:0] uart_rx_data;
//**************************************
//** main code
//**************************************
//串口接收模块
uart_rx #(
.CLK_FREQ (CLK_FREQ),
.UART_BPS (UART_BPS)
)
u_uart_rx(
.clk (sys_clk ),
.rst_n (sys_rst_n ),
.uart_rxd (uart_rxd ),
.uart_rx_done (uart_rx_done ),
.uart_rx_data (uart_rx_data )
);
//串口发送模块
uart_tx #(
.CLK_FREQ (CLK_FREQ),
.UART_BPS (UART_BPS)
)
u_uart_tx(
.clk (sys_clk ),
.rst_n (sys_rst_n ),
.uart_tx_en (uart_rx_done),
.uart_tx_data(uart_rx_data),
.uart_txd (uart_txd ),
.uart_tx_busy( )
);
endmodule
串口接收模块
module uart_rx(
input clk ,
input rst_n ,
input uart_rxd ,
output reg uart_rx_done,
output reg [7:0] uart_rx_data
);
//parameter define
parameter CLK_FREQ = 50000000 ;
parameter UART_BPS = 115200 ;
localparam BAUD_CNT_MAX = CLK_FREQ / UART_BPS;
//reg define
reg uart_rxd_d0;
reg uart_rxd_d1;
reg uart_rxd_d2;
reg rx_flag ;
reg [3:0 ] rx_cnt ;
reg [15:0] baud_cnt ;
reg [7:0 ] rx_data_t ;
//wire define
wire start_en ;
//*********************************************
//** main code
//*********************************************
//捕获接收端口的下降沿(起始位),得到一个时钟周期的脉冲信号
assign start_en = uart_rxd_d2 & (~uart_rxd_d1) & (~rx_flag);
//针对异步信号处理
always @(posedge clk or negedge rst_n)begin
if (!rst_n) begin
uart_rxd_d0 <= 1'b0;
uart_rxd_d0 <= 1'b0;
uart_rxd_d0 <= 1'b0;
end
else begin
uart_rxd_d0 <= uart_rxd;
uart_rxd_d1 <= uart_rxd_d0;
uart_rxd_d2 <= uart_rxd_d1;
end
end
//给接收标志位
always @(posedge clk or negedge rst_n)begin
if (!rst_n)
rx_flag <= 1'b0;
else if (start_en)
rx_flag <= 1'b1;
else if ((rx_cnt == 4'd9) && (baud_cnt == BAUD_CNT_MAX / 2 - 1'b1))
rx_flag <= 1'b0;
else
rx_flag <= rx_flag;
end
//波特率的计数器赋值
always @(posedge clk or negedge rst_n)begin
if (!rst_n)
baud_cnt <= 16'd0;
else if (rx_flag)begin
if (baud_cnt < BAUD_CNT_MAX - 1'b1)
baud_cnt <= baud_cnt + 16'b1;
else
baud_cnt <= 16'd0;
end
else
baud_cnt <= 16'd0;
end
//对于接收数据计数器(rx_cnt)进行赋值
always @(posedge clk or negedge rst_n)begin
if (!rst_n)
rx_cnt <= 4'd0;
else if (rx_flag)begin
if (baud_cnt == BAUD_CNT_MAX - 1'b1)
rx_cnt <= rx_cnt +1'b1;
else
rx_cnt = rx_cnt;
end
else
rx_cnt <= 4'd0;
end
//根据rx_cnt来寄存rxd端口的数据
always@(posedge clk or negedge rst_n)begin
if (!rst_n)
rx_data_t <= 8'b0;
else if (rx_flag)begin
if (baud_cnt == BAUD_CNT_MAX / 2 - 1'b1)begin
case (rx_cnt)
4'd1:rx_data_t[0] <= uart_rxd_d2;
4'd2:rx_data_t[1] <= uart_rxd_d2;
4'd3:rx_data_t[2] <= uart_rxd_d2;
4'd4:rx_data_t[3] <= uart_rxd_d2;
4'd5:rx_data_t[4] <= uart_rxd_d2;
4'd6:rx_data_t[5] <= uart_rxd_d2;
4'd7:rx_data_t[6] <= uart_rxd_d2;
4'd8:rx_data_t[7] <= uart_rxd_d2;
default:;
endcase
end
else
rx_data_t <= rx_data_t;
end
else
rx_data_t <= 8'b0;
end
//给接收完成信号核接收到的数据赋值
always @(posedge clk or negedge rst_n)begin
if (!rst_n)begin
uart_rx_done <= 1'b0;
uart_rx_data <= 8'b0;
end
else if (rx_cnt == 4'd9 && baud_cnt == BAUD_CNT_MAX / 2 - 1'b1)begin
uart_rx_done <= 1'b1 ;
uart_rx_data <= rx_data_t;
end
else begin
uart_rx_done <= 1'b0;
uart_rx_data <= uart_rx_data;
end
end
endmodule
串口发送模块设计
module uart_tx(
input clk ,
input rst_n ,
input uart_tx_en ,
input [7:0] uart_tx_data,
output reg uart_txd ,
output reg uart_tx_busy
);
//parameter define
parameter CLK_FREQ = 50000000;
parameter UART_BPS = 115200 ;
localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS;
//reg define
reg [7:0 ] tx_data_t;
reg [3:0 ] tx_cnt ;
reg [15:0] baud_cnt ;
//******************************************
//** main code
//******************************************
//当uart_tx_en为高时,寄存器输出并行数据,并拉高BUSY信号
always @(posedge clk or negedge rst_n)begin
if (!rst_n) begin
tx_data_t <= 8'b0;
uart_tx_busy <= 1'b0;
end
//发送使能时,寄存要发送的数据,并拉高BUSY信号
else if (uart_tx_en)begin
tx_data_t <= uart_tx_data;
uart_tx_busy <= 1'b1;
end
//当计数到停止位结束时,停止发送过程
else if (tx_cnt == 4'd9 && baud_cnt == BAUD_CNT_MAX / 16)begin
tx_data_t <= 8'b0;
uart_tx_busy <= 1'b0;
end
else begin
tx_data_t <= tx_data_t;
uart_tx_busy <= uart_tx_busy;
end
end
//波特率的计数器赋值
always @(posedge clk or negedge rst_n)begin
if (!rst_n)
baud_cnt <= 16'd0;
else if (uart_tx_busy) begin
if(baud_cnt < BAUD_CNT_MAX - 1'b1)
baud_cnt <= baud_cnt + 16'b1;
else
baud_cnt <= 16'd0;
end
else
baud_cnt <= 16'd0;
end
//tx_cnt进行赋值
always @(posedge clk or negedge rst_n)begin
if (!rst_n)
tx_cnt <= 4'd0;
else if (uart_tx_busy)begin
if (baud_cnt == BAUD_CNT_MAX - 1'b1)
tx_cnt <= tx_cnt + 1'b1;
else
tx_cnt <= tx_cnt;
end
else
tx_cnt <= 4'd0;
end
//根据tx_cnt来给uart发送端口赋值
always @(posedge clk or negedge rst_n)begin
if (!rst_n)
uart_txd <= 1'b1;
else if (uart_tx_busy)begin
case (tx_cnt)
4'd0:uart_txd <= 1'b0 ;
4'd1:uart_txd <= tx_data_t[0];
4'd2:uart_txd <= tx_data_t[1];
4'd3:uart_txd <= tx_data_t[2];
4'd4:uart_txd <= tx_data_t[3];
4'd5:uart_txd <= tx_data_t[4];
4'd6:uart_txd <= tx_data_t[5];
4'd7:uart_txd <= tx_data_t[6];
4'd8:uart_txd <= tx_data_t[7];
4'd9:uart_txd <= 1'b1 ;
default:uart_txd <= 1'b1;
endcase
end
else
uart_txd <= 1'b1;
end
endmodule
tb文件
`timescale 1ns/1ns
module tb_uart_loopback();
//parameter define
parameter CLK_PERIOD = 20;
//reg define
reg sys_clk ;
reg sys_rst_n ;
reg uart_rxd ;
//wire define
wire uart_txd ;
//*************************************
// main code
//*************************************
//发送8'h55 8'b0101_0101
initial begin
sys_clk <= 1'b0;
sys_rst_n <= 1'b0;
uart_rxd <= 1'b1;
#200
sys_rst_n <= 1'b1;
#1000
uart_rxd <= 1'b0; //起始位
#8680
uart_rxd <= 1'b1; //D0
#8680
uart_rxd <= 1'b0; //D1
#8680
uart_rxd <= 1'b1; //D2
#8680
uart_rxd <= 1'b0; //D3
#8680
uart_rxd <= 1'b1; //D4
#8680
uart_rxd <= 1'b0; //D5
#8680
uart_rxd <= 1'b1; //D6
#8680
uart_rxd <= 1'b0; //D7
#8680
uart_rxd <= 1'b1; //停止位
#8680
uart_rxd <= 1'b1; //空闲状态
end
always #(CLK_PERIOD / 2) sys_clk = ~sys_clk;
//例化顶层模块
uart_loopback u_uart_loopback(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.uart_rxd (uart_rxd ),
.uart_txd (uart_txd )
);
endmodule
仿真验证
关于HDMI的超详细教程等我学会了马上就发(: