一.uart简介
- UART(universal asynchronous receiver-transmitter)是一种采用异步串行通信方式的通用异步收发传输器。
- 定义如上,那么出现问题了,什么叫异步串行通信?请关注下文原理。
二.原理
1.同步通信&&异步通信
1.1同步通信
- 发送方发送出数据后,等接收方响应后才发送下一个数据
1.2异步通信
- 发送方发出数据后,不等接收方发回相应,接着发送下一个数据
2.并行通信&&串行通信
2.1并行通信
- 并行通信是指数据各个位用多条数据线同时进行传输
2.2串行通信
- 串行通信是将数据分成一位一位的形式在一条线上逐个传输
3.全双工&&半双工
3.1全双工
- 双方都可以发送接收数据,并可以同时进行
3.2半双工
- 双方都可以发送接收数据,但是同一时刻只能一方发送,一方接收
4.协议层
4.1数据格式
- 解析上图可以看出:空闲位处于高电平,起始位为0(拉低);数据位可以是6-8bit,注意是低位先发;暂时不考虑校验位,停止位为1(拉高)。
4.2传输速率
- 串口通信的速率用波特率表示,它表示每秒传输二进制数据的位数,单位是bit/s(位/秒),简称bps。常用的波特率有9600、19200、38400、57600、115200等。本文中设计用的115200的波特率==>传输1bit需要计数50_000_000/115200 = 434次
二.初步设计
1.模块图
2.tx设计
- tx将8bit的数据转换为1bit数据输出
3.rx设计
文章来源:https://www.toymoban.com/news/detail-820228.html
- rx将1bit的数据转换为8bit数据输出
- 这里由于加入stop状态后面会导致状态跳崩了,于是我不要stop状态了
4.加入FIFO
- FIFO配置上,数据宽度选8bit,数据深度选32bit,其他配置可参考前文https://blog.csdn.net/weixin_67803687/article/details/132248964?spm=1001.2014.3001.5501中的单时钟前显模式
三.代码
1.uart_tx
/**************************************功能介绍***********************************
Date : 2023年8月16日17:07:11
Author : Alegg xy.
Version : 2.0
Description: FPGA向上位机发送数据【8bit变1bit(波形)】
*********************************************************************************/
//---------<模块及端口声名>------------------------------------------------------
module uart_tx(
input clk ,
input rst_n ,
input [7:0] tx_data ,
input tx_data_vld,
output ready ,
output reg tx
);
//---------<参数定义>---------------------------------------------------------
parameter MAX_1bit = 9'd434;//1bit要计434次
//状态机参数定义
localparam IDLE = 'b0001,//空闲状态
START = 'b0010,//起始位
DATA = 'b0100,//数据位
STOP = 'b1000;//停止位
//---------<内部信号定义>-----------------------------------------------------
reg [3:0] cstate ;//现态
reg [3:0] nstate ;//次态
wire IDLE_START;
wire START_DATA;
wire DATA_CHECK;
wire CHECK_STOP;
wire STOP_IDLE;
reg [8:0] cnt_baud ;//波特计数器,波特率115200
wire add_cnt_baud ;
wire end_cnt_baud ;
reg [2:0] cnt_bit ;//bit计数器,起始位1bit,数据位8bit,结束位1bit
wire add_cnt_bit ;
wire end_cnt_bit ;
reg [3:0] bit_max;//bit最大值,复用需要考察每个状态的bit值
reg [7:0] tx_data_r;
//计434次
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_baud <= 'd0;
end
else if(add_cnt_baud)begin
if(end_cnt_baud)begin
cnt_baud <= 'd0;
end
else begin
cnt_baud <= cnt_baud + 1'd1;
end
end
end
assign add_cnt_baud = cstate != IDLE;
assign end_cnt_baud = add_cnt_baud && cnt_baud == MAX_1bit - 1'd1;
//bit计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_bit <= 'd0;
end
else if(add_cnt_bit)begin
if(end_cnt_bit)begin
cnt_bit <= 'd0;
end
else begin
cnt_bit <= cnt_bit + 1'd1;
end
end
end
assign add_cnt_bit = end_cnt_baud;
assign end_cnt_bit = add_cnt_bit && cnt_bit == bit_max -1'd1;
//计数器复用
always @(*)begin
case (cstate)
IDLE :bit_max = 'd0;
START:bit_max = 'd1;//起始位1bit
DATA :bit_max = 'd8;//数据位8bit
STOP :bit_max = 'd1;//结束位1bit
default: bit_max = 'd0;
endcase
end
assign IDLE_START = (cstate == IDLE) && tx_data_vld;//考察到开始传输信号
assign START_DATA = (cstate == START) && end_cnt_bit;//计1bit数据
assign DATA_STOP = (cstate == DATA) && end_cnt_bit;
assign STOP_IDLE = (cstate == STOP) && end_cnt_bit;//计1bit数据
//第一段:时序逻辑描述状态转移
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cstate <= IDLE;
end
else begin
cstate <= nstate;
end
end
//第二段:组合逻辑描述状态转移规律和状态转移条件
always @(*) begin
case(cstate)
IDLE :begin
if (IDLE_START) begin
nstate = START;
end
else begin
nstate = cstate;
end
end
START :begin
if (START_DATA) begin
nstate = DATA;
end
else begin
nstate = cstate;
end
end
DATA :begin
if (DATA_STOP) begin
nstate = STOP;
end
else begin
nstate = cstate;
end
end
STOP :begin
if (STOP_IDLE) begin
nstate = IDLE;
end
else begin
nstate = cstate;
end
end
default : nstate = cstate;
endcase
end
//寄存一拍
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
tx_data_r <= 'd0;
end
else if (tx_data_vld) begin
tx_data_r <= tx_data;
end
else begin
tx_data_r <= tx_data_r;
end
end
//第三段:描述输出,时序逻辑或组合逻辑皆可
always @(*)begin
case (cstate)
IDLE : tx = 1'b1;
START: tx = 1'b0;//起始位为0
DATA : tx = tx_data_r[cnt_bit];
STOP : tx = 1'b1;//结束位为1
default: tx = 1'b1;
endcase
end
assign ready = cstate == IDLE;//当状态为IDLE时,表示tx端可以接收数据
endmodule
2.uart_rx
/**************************************功能介绍***********************************
Date : 2023年8月16日18:25:03
Author : Alegg xy.
Version : 1.0
Description: FPGA收上位机发来的数据【1bit(波形)变8bit】
*********************************************************************************/
//---------<模块及端口声名>------------------------------------------------------
module uart_rx(
input clk ,
input rst_n ,
input rx ,
output rx_data_vld,
output reg [7:0] rx_data
);
//---------<参数定义>---------------------------------------------------------
parameter MAX_1bit = 9'd434;//1bit要计434次
//状态机参数定义
localparam IDLE = 'b001,//空闲状态
START = 'b010,//起始位
DATA = 'b100;//数据位
//---------<内部信号定义>-----------------------------------------------------
reg [2:0] cstate ;//现态
reg [2:0] nstate ;//次态
wire IDLE_START;
wire START_DATA;
wire DATA_IDLE;
reg [8:0] cnt_baud ;//波特计数器,波特率115200
wire add_cnt_baud ;
wire end_cnt_baud ;
reg [2:0] cnt_bit ;//bit计数器,起始位1bit,数据位8bit,结束位1bit
wire add_cnt_bit ;
wire end_cnt_bit ;
reg [3:0] bit_max;//bit最大值,复用需要考察每个状态的bit值
//计434次
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_baud <= 'd0;
end
else if(add_cnt_baud)begin
if(end_cnt_baud)begin
cnt_baud <= 'd0;
end
else begin
cnt_baud <= cnt_baud + 1'd1;
end
end
end
assign add_cnt_baud = cstate != IDLE;
assign end_cnt_baud = add_cnt_baud && cnt_baud == MAX_1bit - 1'd1;
//bit计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_bit <= 'd0;
end
else if(add_cnt_bit)begin
if(end_cnt_bit)begin
cnt_bit <= 'd0;
end
else begin
cnt_bit <= cnt_bit + 1'd1;
end
end
end
assign add_cnt_bit = end_cnt_baud;
assign end_cnt_bit = add_cnt_bit && cnt_bit == bit_max -1'd1;
//计数器复用
always @(*)begin
case (cstate)
IDLE :bit_max = 'd0;
START:bit_max = 'd1;//起始位1bit
DATA :bit_max = 'd8;//数据位8bit
default: bit_max = 'd0;
endcase
end
assign IDLE_START = (cstate == IDLE) && rx == 0;//识别到起始位0
assign START_DATA = (cstate == START) && end_cnt_bit;//计1bit数据
assign DATA_IDLE = (cstate == DATA) && end_cnt_bit;//计8bit数据
//第一段:时序逻辑描述状态转移
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cstate <= IDLE;
end
else begin
cstate <= nstate;
end
end
//第二段:组合逻辑描述状态转移规律和状态转移条件
always @(*) begin
case(cstate)
IDLE :begin
if (IDLE_START) begin
nstate = START;
end
else begin
nstate = cstate;
end
end
START :begin
if (START_DATA) begin
nstate = DATA;
end
else begin
nstate = cstate;
end
end
DATA :begin
if (DATA_IDLE) begin
nstate = IDLE;
end
else begin
nstate = cstate;
end
end
default : nstate = IDLE;
endcase
end
//第三段:描述输出,时序逻辑或组合逻辑皆可
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
rx_data <= 0;
end
else if (cstate == DATA && cnt_baud == MAX_1bit >> 1) begin//电平中间值采样,边沿采样容易出错
rx_data[cnt_bit] <= rx;
end
else begin
rx_data <= rx_data;
end
end
assign rx_data_vld = DATA_IDLE;//开始传输数据信号为计满数据位
endmodule
3.ctrl(FIFO)
/**************************************功能介绍***********************************
Date : 2023年8月16日18:43:33
Author : Alegg xy.
Version : 1.0
Description: 调用FIFO
*********************************************************************************/
//---------<模块及端口声名>------------------------------------------------------
module ctrl(
input clk ,
input rst_n ,
input [7:0] rx_data ,
input rx_data_vld,
input ready ,
output tx_data_vld,
output [7:0] tx_data
);
//---------<参数定义>---------------------------------------------------------
wire fifo_rdempty;//FIFO读空信号
wire fifo_wrfull;//FIFO写满信号
//---------<内部信号定义>-----------------------------------------------------
fifo fifo_inst (
.aclr ( ~rst_n ),
.data ( rx_data ),
.rdclk ( clk ),
.rdreq ( ready && ~fifo_rdempty ),
.wrclk ( clk ),
.wrreq ( rx_data_vld && ~fifo_wrfull ),
.q ( tx_data ),
.rdempty ( fifo_rdempty ),
.wrfull ( fifo_wrfulll )
);
assign tx_data_vld = ready && ~fifo_rdempty;
endmodule
4.top
/**************************************功能介绍***********************************
Date : 2023年8月16日18:51:42
Author : Alegg xy.
Version : 1.0
Description: 顶层模块
*********************************************************************************/
//---------<模块及端口声名>------------------------------------------------------
module top(
input clk ,
input rst_n ,
input rx ,
output tx
);
//---------<参数定义>---------------------------------------------------------
wire [7:0] tx_data;
wire tx_data_vld;
wire [7:0] rx_data;
wire rx_data_vld;
wire ready;
//---------<内部信号定义>-----------------------------------------------------
uart_tx u_uart_tx(
.clk (clk),
.rst_n (rst_n),
.tx_data (tx_data),
.tx_data_vld(tx_data_vld),
.ready (ready),
.tx (tx)
);
uart_rx u_uart_rx(
.clk (clk),
.rst_n (rst_n),
.rx (rx),
.rx_data_vld(rx_data_vld),
.rx_data (rx_data)
);
ctrl u_ctrl(
.clk (clk),
.rst_n (rst_n),
.rx_data (rx_data),
.rx_data_vld(rx_data_vld),
.ready (ready),
.tx_data_vld(tx_data_vld),
.tx_data (tx_data)
);
endmodule
四.效果
文章来源地址https://www.toymoban.com/news/detail-820228.html
到了这里,关于uart串口环回(加FIFO)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!