UART Verilog发送实现
通用异步接收器/收发器,通常成为UART,是一种串行、异步、全双工通信协议:固定为逻辑 0(低电平),持续 1 bit 时间,告诉接收方"帧开始了"。:8 bit,从 LSB(D0)到 MSB(D7)依次发送,图中以10110101为例。:奇偶校验,可配置为 None / Even / Odd,:固定为逻辑 1(高电平),1 bit 或 2 bit,帧结束后线路回归空闲。异步通信中没有时钟信号,因
一、UART协议详解
1.1 基本概念
通用异步接收器/收发器,通常成为UART,是一种串行、异步、全双工通信协议
1.2接口形式和电气标准
1.2.1 TTL 电平
| 参数 | 3.3V系统 | 5.0V系统 |
| 逻辑1(空闲) | 3.3V | 5.0V |
| 逻辑0 | 0V | 0V |
| 传输距离 | <1m | <1m |
1.2.2 RS232
| 参数 | 数值 |
| 逻辑1(空闲) | -3v~-15v |
| 逻辑0 | +3v~+15v |
| 典型电压 | ±12v |
| 最大速率 | 115200bps |
| 传输距离 | ≤15m |
| 接口形式 | DB9 |
RS232电平与TTL电平极性相反,需要采用芯片转换,最常用的芯片时MAX232
1.2.3 RS485
| 参数 | 数值 |
| 信号方式 | 差分信号(A、B 两线) |
| 逻辑1 | A − B > +200mV |
| 逻辑0 | A − B < −200mV |
| 最大速率 | 10 Mbps(短距) |
| 传输距离 | 最远 1200m(低速时) |
| 挂载节点 | 最多 32 个(增强型可达 256) |
| 通信方式 | 半双工(一对差分线)或全双工(两对) |
RS-485 抗干扰能力强
1.3 UART数据帧结构

一个标准的UART数据帧通常由以下几个部分组成:
起始位(Start Bit):固定为逻辑 0(低电平),持续 1 bit 时间,告诉接收方"帧开始了"。
数据位(Data Bits):8 bit,从 LSB(D0)到 MSB(D7)依次发送,图中以 10110101 为例。
校验位(Parity Bit,可选):奇偶校验,可配置为 None / Even / Odd,
停止位(Stop Bit):固定为逻辑 1(高电平),1 bit 或 2 bit,帧结束后线路回归空闲。
1.4 数据传输
异步通信中没有时钟信号,因此两个通信设备需要就波特率达成一致。常见的有9600、115200等。接收方根据约定的波特率来接收数据,在接收时,起始位会被检测到,并开始接收数据位,直到停止位为止。
1.5 信号线
UART通信协议通常需要两条线:
·TX(Transmit,传输线):发送端用来发送数据。
·RX (Receive,接收线) :接收端用来接收数据。
这两个信号线连接在通信双方的设备之间,数据在这两条线中单向传输。
1.6 UART的工作过程
(1)发送方:
- 当数据准备好时,发送方将数据从并行形式转换为串行数据。
- 在数据的前面,发送方会发送一个起始位(逻辑0),表示数据的开始。
- 接着是数据位,通常是8位二进制数据。
- 根据协议,发送方可能会发送一个校验位(如果启用的话)来进行错误检测。
- 最后,发送停止位(通常是1位或2位逻辑1)来表示数据帧的结束。
(2)接收方
- 接收方在接收到起始位时开始接收数据。
- 接收方按照规定的波特率逐位读取数据,直到接收到完整的数据帧。
- 接收方检查校验位(如果存在)来确认数据是否正确。
- 一旦接收到停止位,接收方认为数据传输完成,可以处理接收到的数据。
1.7 波特率和时钟同步
1.8 校验与错误检测
在UART通信中,为了减少数据传输中的错误,常常会使用校验位:
- 无校验:没有校验位,发送方和接收方无需进行任何错误检测。
- 奇校验:校验位的选择使得数据位中“1”的数量为奇数。
- 偶校验:校验位的选择使得数据位中“1”的数量为偶数。
1.9 UART与全双工和半双工
全双工:UART通常支持全双工通信,即发送和接收可以同时进行。发送方通过TX发送数据,接收方通过RX接收数据
半双工:某些情况下,UART可以配置为半双工模式,即只能在某一时刻进行发送或接收
二、Verilog实现UART发送
2.1 Verilog实现UART发送
串口发送模块整体框图
信号名称:
clk:时钟信号
data_byte:待发送数据
baud_set:波特率选择
send_en:发送使能信号
rst_n:复位信号
uart_tx:串口发送信号输出
tx_done:发送一字节数据结束信号,持续1clk的高电平
uart_state:串口发送状态,处于1表示串口正在处于发送状态
module Uart_Byte_tx
#(
parameter CLK_FREQ=50_000_000 //时钟信号频率 单位Hz
)
(
rst_n ,
clk ,
send_en ,
baud_set,
data_byte,
uart_tx,
tx_done,
uart_state
);
input rst_n ; //复位信号
input clk ; //时钟信号
input [2:0]baud_set ; //波特率选择
input [7:0]data_byte ; //待发送字节信号
input send_en ; //发送使能信号
output reg uart_tx ; //输出信号
output reg tx_done ; //发送完成信号
output reg uart_state ; //发送状态,1:发送中
localparam START_BIT=0,
STOP_BIT =1;
//根据时钟频率 CLK_FREQ 计算波特率分频系数
wire [15:0]baud_div_9600=CLK_FREQ/9600-1;
wire [15:0]baud_div_19200=CLK_FREQ/19200-1;
wire [15:0]baud_div_38400=CLK_FREQ/38400-1;
wire [15:0]baud_div_57600=CLK_FREQ/57600-1;
wire [15:0]baud_div_115200=CLK_FREQ/115200-1;
//根据baud_set;选择波特率
reg [15:0]bps_DR;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
bps_DR<=baud_div_9600;
end
else begin
case(baud_set)
3'b000:bps_DR<=baud_div_9600 ;
3'b001:bps_DR<=baud_div_19200 ;
3'b010:bps_DR<=baud_div_38400 ;
3'b011:bps_DR<=baud_div_57600 ;
3'b100:bps_DR<=baud_div_115200 ;
default:bps_DR<=baud_div_9600 ;
endcase
end
end
reg [15:0]div_cnt; //波特率分频计数器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
div_cnt<=16'b0;
end
else if(uart_state)begin
if(div_cnt==bps_DR)begin
div_cnt<=16'b0;
end
else begin
div_cnt<=div_cnt+1'b1;
end
end
else begin
div_cnt<=16'b0;
end
end
reg bps_clk; //波特率时钟
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
bps_clk<=1'b0;
end
else if(div_cnt==16'd1)begin
bps_clk<=1'b1;
end
else begin
bps_clk<=1'b0;
end
end
reg [3:0]bps_cnt; //发送计数器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
bps_cnt<=4'b0;
end
else if(bps_cnt==4'd11)begin
bps_cnt<=4'b0;
end
else if(bps_clk)begin
bps_cnt<=bps_cnt+1'b1;
end
else begin
bps_cnt<=bps_cnt;
end
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
tx_done<=1'b0;
end
else if(bps_cnt==4'd11)begin //发送完成,tx_done拉高一个时钟周期
tx_done<=1'b1;
end
else begin
tx_done<=1'b0;
end
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
uart_state<=1'b0;
end
else if(send_en)begin //发送过程中,为1
uart_state<=1'b1;
end
else if(bps_cnt==4'd11)begin //发送完成,置 0
uart_state<=1'b0;
end
else begin
uart_state<=uart_state;
end
end
reg [7:0]data_byte_reg; //数据寄存器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
data_byte_reg<=8'b0;
end
else if(send_en)begin
data_byte_reg<=data_byte;
end
else begin
data_byte_reg<=data_byte_reg;
end
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
uart_tx<=1'b0;
end
else begin
case(bps_cnt)
0 :uart_tx<=1'b1;
1 :uart_tx<=START_BIT;
2 :uart_tx<=data_byte_reg[0];
3 :uart_tx<=data_byte_reg[1];
4 :uart_tx<=data_byte_reg[2];
5 :uart_tx<=data_byte_reg[3];
6 :uart_tx<=data_byte_reg[4];
7 :uart_tx<=data_byte_reg[5];
8 :uart_tx<=data_byte_reg[6];
9 :uart_tx<=data_byte_reg[7];
10:uart_tx<=STOP_BIT;
default:uart_tx<=1'b1;
endcase
end
end
endmodule
频率为50Mhz时,其test_bench和仿真结果如下:
`timescale 1ns/1ps
module test_bench();
reg clk;
reg rst_n;
reg send_en;
reg [2:0]baud_set;
reg [7:0]data_byte;
wire tx_done;
wire uart_tx;
wire uart_state;
Uart_Byte_tx
#(
.CLK_FREQ(50_000_000) //时钟信号频率 单位Hz
)
Uart_Byte_tx_inst(
.rst_n (rst_n),
.clk (clk),
.send_en(send_en),
.baud_set(baud_set),
.data_byte(data_byte),
.uart_tx(uart_tx),
.tx_done(tx_done),
.uart_state(uart_state)
);
initial clk=1'b1;
always #10 clk=~clk;
initial begin
rst_n=0;
send_en=0;
data_byte=0;
#203;
rst_n=1;
#200;
send_en=1;
baud_set=3'b100;
data_byte=8'h55;
#20;
send_en=0;
@(posedge tx_done);
#200;
send_en=1;
baud_set=3'b100;
data_byte=8'haa;
#20;
send_en=0;
@(posedge tx_done);
#200;
send_en=1;
baud_set=3'b100;
data_byte=8'h5c;
#20;
send_en=0;
@(posedge tx_done);
#20000;
$stop;
end
endmodule

频率为100Mhz时,其test_bench和仿真结果如下:

更多推荐


所有评论(0)