目录
学习目标:
了解uart基本概念,尝试描述出uart数据发送端与接收端并将其连接,随后使用上位机测试uart模块能否正常收发数据。
一、UART通信原理:
1.概念:
UART(universal asynchronous receiver-transmitter)是一种采用异步串行通信方式的通用异步收发传输器;它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。
2.结构:
串口通信需要两根信号线来实现,一根用于发送,另外一根接收。在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据;LSB低位;MSB高位。
3.标准:
UART通信需满足协议层与物理层的标准。协议层主要为通信协议(包括数据格式、传输速率等)。物理层主要为接口类型、电平标准等。
3.1协议层:
数据格式:1帧数据由四部分组成 |
起始位(1bit) |
数据位(6\7\8bit) |
奇偶校验位(1bit) |
停止位(1\1.5\2bit) |
传输速率: |
串口通信的速率用波特率表示,它表示每秒传输二进制数据的位数,
单位是 bit/s(位/秒),简称 bps;
常用的波特率有 9600、19200、38400、57600 以及 115200 等。
|
3.2物理层:
3.2.1接口标准:
3.2.2电平标准: |
TTL 电平的串口(3.3V)
|
RS232 电平的串口(+5~+12V 为低电平,-12~-5V 为高电平)
|
按电气标准可分为 |
RS-232-C:TXD/RXD/GND、15 米/9600bps
|
RS-422:TX+/TX-/RX+/RX-/GND
|
RS485:A/B/G 、1200 米/9600bps
|
二、UART发送端设计
1.需求分析
使用FPGA和PC进行串口通信(数据收发)
以9600波特率发送1byte数据,即8bit。故需要1bit起始位,1bit停止位。
2.系统框架
3.信号列表
4.描述预期功能时序图
5.发送端设计代码
/*************======================*****************\
filename:uart_tx
description:uart串口,发送端
up file:
reversion:
v1.0:2022-8-4 16:03 uart练习
tips:
author:106年
\*************======================*****************/
module uart_tx (
input clk , //系统时钟
input rst_n , //复位信号,低有效
input send_en , //
input [07:00] data_in ,
output reg txd , //串行数据发送信号
output wire tx_done //一帧数据发送完成信号
);
//参数定义
parameter CLK_FRE = 26'd50_000_000 ;
parameter
BAUD_9600 = CLK_FRE / 9600 , //波特率为9600
BAUD_19200 = CLK_FRE / 19200 , //波特率为19200
BAUD_57600 = CLK_FRE / 57600 , //波特率为57600
BAUD_115200 = CLK_FRE / 115200 ; //波特率为115200
//内部信号定义
reg [12:00] cnt_bps ; //产生波特周期
wire add_bps ;
wire end_bps ;
reg [03:00] cnt_bit ; //统计一帧数据发送计数器
wire add_bit ;
wire end_bit ;
reg [07:00] tx_data ; //欲发送的数据
reg work_state ; //系统工作使能信号
//逻辑描述
//_______功能描述
/*__系统工作使能信号__功能描述=============================================*/
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
work_state <= 1'b0;
end
else if(send_en)begin
work_state <= 1'b1; //发送使能信号到来时系统工作
end
else if(tx_done)begin
work_state <= 1'b0; //传输完成时系统停止工作
end
else begin
work_state <= work_state;
end
end
/*================================================================*/
//产生写入数据
//_产生写入数据___功能描述
/*__产生写入数据__功能描述=============================================*/
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
tx_data <= 8'd0;
end
else begin
tx_data <= data_in; //待传输数据,有空时改成标准化模块
end
end
/*================================================================*/
/*__波特周期计数器________计数器============================================*/
always @(posedge clk or negedge rst_n) begin
if (!rst_n)begin
cnt_bps <= 13'd0 ;
end
else if(add_bps)begin
if(end_bps)begin
cnt_bps <= 13'd0;
end
else begin
cnt_bps <= cnt_bps +13'd1;
end
end
else begin
cnt_bps <= 13'd0;
end
end
assign add_bps = work_state ;
assign end_bps = add_bps && (cnt_bps >= BAUD_9600 - 1) ;
/*=================================================================*/
/*___数据位置___计数器============================================*/
always @(posedge clk or negedge rst_n) begin
if (!rst_n)begin
cnt_bit <= 1'd0 ;
end
else if(add_bit)begin
if(end_bit)begin
cnt_bit <= 1'd0;
end
else begin
cnt_bit <= cnt_bit +1'd1;
end
end
else begin
cnt_bit <= cnt_bit;
end
end
assign add_bit = end_bps ;
assign end_bit = add_bit && (cnt_bit >= 4'd9) ;
/*=================================================================*/
//描述输出
//发送数据
/*========================================================*/
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
txd <= 1'b1;
end
else if(work_state)begin
case (cnt_bit)
0 : txd <= 1'b0 ; //起始位
1 : txd <= tx_data[0] ; //发送数据第 1 位
2 : txd <= tx_data[1] ; //发送数据第 2 位
3 : txd <= tx_data[2] ; //发送数据第 3 位
4 : txd <= tx_data[3] ; //发送数据第 4 位
5 : txd <= tx_data[4] ; //发送数据第 5 位
6 : txd <= tx_data[5] ; //发送数据第 6 位
7 : txd <= tx_data[6] ; //发送数据第 7 位
8 : txd <= tx_data[7] ; //发送数据第 8 位
9 : txd <= 1'b1 ; //停止位
default: ;
endcase
end
else begin
txd <= 1'b1;
end
end
assign tx_done = end_bit; //结束传输
/*================================================================*/
endmodule
6.接收端设计代码
/*************======================*****************\
filename:uart_rx
description:uart串口,接收端端
up file:
reversion:
v1.0:2022-8-5 09:45 uart练习
tips:
author:106年
\*************======================*****************/
module uart_rx (
input clk ,
input rst_n ,
input rxd ,
output reg [07:00] rx_data ,
output reg rx_done
);
// 参数定义
parameter CLK_FRE = 26'd50_000_000 ;
parameter
BAUD_9600 = CLK_FRE / 9600 ,
BAUD_19200 = CLK_FRE / 19200 ,
BAUD_57600 = CLK_FRE / 57600 ,
BAUD_115200 = CLK_FRE / 115200 ;
parameter //采样信号位置
CPD_9600 = 2604 ,
CPD_19200 = 1302 ,
CPD_57600 = 434 ,
CPD_115200 = 217 ;
//内部信号定义
//输入延迟信号
reg reg1; //输入信号延迟,避免亚稳态
reg reg2; //输入信号延迟,避免亚稳态
reg reg3; //输入信号延迟,避免亚稳态
wire key_neg; //起始信号下降沿
//工作状态信号
reg work_state; //系统工作状态
//计数器信号
reg [12:00] cnt_bps ; //波特率计数器
wire add_bps ; //波特率计数器
wire end_bps ; //波特率计数器
reg [03:00] cnt_bit ; //记录接收数据位置
wire add_bit ; //记录接收数据位置
wire end_bit ; //记录接收数据位置
reg cpd ; //采样标志信号
reg [07:00] keep_data ; //数据保持
//逻辑描述
//_______功能描述
/*___输入信号延迟采样___功能描述=============================================*/
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
reg1 <= 1'b1;
reg2 <= 1'b1;
reg3 <= 1'b1;
end
else begin
reg1 <= rxd;
reg2 <= reg1;
reg3 <= reg2;
end
end
assign key_neg = ~reg2 && reg3; //起始位下降沿检测
// assign rx_en = key_neg; //使能信号在起始位下降沿到来时为1
/*================================================================*/
/*___系统工作状态___功能描述=============================================*/
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
work_state <= 1'b0;
end
else if(key_neg)begin
work_state <= 1'b1;
end
else if(rx_done)begin
work_state <= 1'b0;
end
else begin
work_state <= work_state;
end
end
/*================================================================*/
/*__波特周期计数器________计数器============================================*/
always @(posedge clk or negedge rst_n) begin
if (!rst_n)begin
cnt_bps <= 13'd0 ;
end
else if(add_bps)begin
if(end_bps)begin
cnt_bps <= 13'd0;
end
else begin
cnt_bps <= cnt_bps +13'd1;
end
end
else begin
cnt_bps <= 13'd0;
end
end
assign add_bps = work_state ;
assign end_bps = add_bps && (cnt_bps >= BAUD_9600 - 1) ;
/*=================================================================*/
/*___数据位置___计数器============================================*/
always @(posedge clk or negedge rst_n) begin
if (!rst_n)begin
cnt_bit <= 1'd0 ;
end
else if(add_bit)begin
if(end_bit)begin
cnt_bit <= 1'd0;
end
else begin
cnt_bit <= cnt_bit +1'd1;
end
end
else begin
cnt_bit <= cnt_bit;
end
end
assign add_bit = end_bps ;
assign end_bit = add_bit && (cnt_bit >= 4'd9) ;
/*=================================================================*/
/*___采样标志信号___功能描述=============================================*/
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cpd <= 1'b0;
end
else if((cnt_bit >= 1) && (cnt_bit <=9)) begin
if(cnt_bps == CPD_9600)begin
cpd <= 1'b1;
end
else begin
cpd <= 1'b0;
end
end
else begin
cpd <= 1'b0;
end
end
/*================================================================*/
/*================================================================*/
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
keep_data <= 8'd0;
end
else if(cpd)begin
case(cnt_bit)
0 : keep_data <= reg3 ;
1 : keep_data[0] <= reg3 ;
2 : keep_data[1] <= reg3 ;
3 : keep_data[2] <= reg3 ;
4 : keep_data[3] <= reg3 ;
5 : keep_data[4] <= reg3 ;
6 : keep_data[5] <= reg3 ;
7 : keep_data[6] <= reg3 ;
8 : keep_data[7] <= reg3 ;
9 : keep_data <= keep_data;
default:;
endcase
end
else begin
keep_data <= keep_data;
end
end
/*================================================================*/
/*================================================================*/
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rx_done <= 0;
rx_data <= 8'd0;
end
else if(end_bit)begin
rx_data <= keep_data;
rx_done <= 1;
end
else begin
rx_data <= rx_data;
rx_done <= 0;
end
end
/*================================================================*/
endmodule
7.顶层文件描述
module top_uart(
input clk , //系统时钟,50MHz
input rst_n , //复位信号,低有效
input data_in , //接收线
output txd //发送线
);
//内部信号定义
wire [07:00] data_link1 ;
wire rx_line ;
//模块调用
//数据接收端
uart_rx U_uart_rx
(
.clk (clk) ,
.rst_n (rst_n) ,
.rxd (data_in) ,
.rx_data (data_link1) ,
.rx_done (rx_line)
);
//数据发送端
uart_tx U_uart_tx
(
.clk (clk) ,
.rst_n (rst_n) ,
.send_en (rx_line) ,
.data_in (data_link1) ,
.txd (txd)
);
endmodule
8.仿真代码
`timescale 1ns/1ns
module tb_top_uart();
//参数重定义
defparam U_top_uart.U_uart_tx.BAUD_9600 = 520 ,
U_top_uart.U_uart_rx.BAUD_9600 = 520 ,
U_top_uart.U_uart_rx.CPD_9600 = 260 ;
//激励信号
reg tb_clk ;
reg tb_rst_n ;
reg tb_rxd ;
//观测信号
wire tb_txd ;
top_uart U_top_uart(
.clk (tb_clk),
.rst_n (tb_rst_n),
.data_in (tb_rxd),
.txd (tb_txd)
);
//系统初始化
initial tb_clk = 1'b0;
always #10 tb_clk = ~tb_clk;
initial begin
tb_rst_n = 1'b0;
tb_rxd = 1'b0;
#33
tb_rst_n = 1'b1;
#20
tb_rxd = 1'b0;
#10400
tb_rxd = 1'b1;
#10400
tb_rxd = 1'b1;
#10400
tb_rxd = 1'b0;
#10400
tb_rxd = 1'b1;
#10400
tb_rxd = 1'b0;
#10400
tb_rxd = 1'b1;
#10400
tb_rxd = 1'b0;
#10400
tb_rxd = 1'b1;
#10400
tb_rxd = 1'b1;
#2_000_000
$stop(2);
end
endmodule
9.仿真截图
10.RTL视图
三、总结
使用Verilog HDL描述UART串口通信编码难度不高,重点需注意波特率的计数周期以及起始位和停止位的判断。本次完成UART数据回环并未加入缓存模块及按键控制模块,读者可根据自己需要自行添加。
在描述接收端时需注意该问题,本问题为作者遇到的,初学者可留意自己是否遇到同样问题。