UART串口发送一个字节
一、简介
1、 什么是UART?
UART(Universal Asynchronous Receiver/Transmitter) 代表通用异步收发器
(异步串行通信协议中的一种)。它是微控制器内部的硬件外围设备。能够将传入和传出的数据转换为串行二进制流。使用串行到并行转换
的方式,能将从外围设备接收的8位串行数据(8位)转换为并行形式(一个字节)。
UART通用串行数据总线可双向通信,也可全双工传输和接收(如:rs232,两条信号线组成)。
2、什么是波特率?
由于设备的发送端和接收端需要采用同样的速率,防止数据的丢失,于是在这里提出了波特率作为信号的传输速率,即计算机在串口通信时的速率。波特率bps
:每秒钟传送的码元数(一个数字脉冲)
常见的波特率有:9600、115200……
如下表,系统时钟周期20ns,因此当波特率=9600时,波特率周期 = 1s / 9600 = 104167ns ,计数次数=(104167/20-1 = 5208-1
3、 UART串口通信协议
UART作为异步串口通信协议的一种,工作原理是将数据的每一个字符一位一位地传输
。其中包括起始位(START低电平)、数据位(Bit0-7)、终止位(STOP高电平)。
二、串口接收和发送模块
如下架构来完成设备间的信息交互。串并转换是接收和发送模块的基本功能,其中接收模块是串行数据转换成并行,发送模块是并行数据转换成串行。
三、串口发送一个字节模块设计
发送模块内部结构:
包括主要部件:1.生成波特率查找表(DR_LUT) ; 2.生成波特率计数器(Div_cnt、bos_cnt); 3.Tx数据发送模块 (MUX10) ;
verilog编写UART发送模块
module uart_tx_1(
input clk,
input rst_n,
input [7:0] uart_data,
input [2:0] uart_sel,
input data_en,
output reg tx_data
);
reg [7:0] uart_data_reg; //寄存一拍后的数据
reg [12:0] bps_cnt_max; //波特率计数最大值
reg [12:0] div_cnt;
reg cnt_flag;
reg TX_Done;
reg [3:0]bps_cnt;
reg uart_state;
//避免数据丢失或错误,寄存
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
uart_data_reg <= 8'b0;
end
else if (uart_state) begin
uart_data_reg <= uart_data;
end
else begin
uart_data_reg <= uart_data_reg;
end
end
//设计波特率查找表
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
bps_cnt_max <= 13'b0;
end
else begin
case(uart_sel)
0: bps_cnt_max <= 13'd5207; //9600
1: bps_cnt_max <= 13'd2603; //19200
2: bps_cnt_max <= 13'd1301; //38400
3: bps_cnt_max <= 13'd867; //57600
4: bps_cnt_max <= 13'd433; //115200
default:bps_cnt_max <= 13'd5207;
endcase
end
end
//发送数据有效信号(10位)
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
uart_state <= 1'b0;
end
else if (data_en) begin
uart_state <= 1'b1;
end
else if (TX_Done)begin //当10位发送完成时 TX_Done为高电平
uart_state <= 1'b0;
end
else begin
uart_state <= uart_state;
end
end
//设计分频计数器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
div_cnt <= 13'b0;
end
else if (uart_state) begin
if (div_cnt == bps_cnt_max)begin
div_cnt <= 13'b0;
end
else begin
div_cnt <= div_cnt + 1;
end
end
else begin
div_cnt <= 13'b0;
end
end
//每div_cnt计数到1的时候发出一个高脉冲,即每一位数据发送的标志信号
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_flag <= 1'b0;
end
else if (div_cnt == 1) begin
cnt_flag <= 1'b1;
end
else begin
cnt_flag <= 1'b0;
end
end
//产生bps_cnt
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
bps_cnt <= 4'b0;
end
else if (uart_state)begin
if (cnt_flag)begin
bps_cnt <= bps_cnt + 1;
end
else begin
bps_cnt <= bps_cnt;
end
end
else begin
bps_cnt <= 0;
end
end
//产生TX_Done信号
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
TX_Done <= 1'b0;
end
else if (bps_cnt == 4'd10) begin //计数到11的原因是得到一个完整的计数值9
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
tx_data <= 1'b1;
end
else if (uart_state)begin
case(bps_cnt)
0: tx_data <= 1'b1; //闲置位
1: tx_data <= 1'b0; //开始位
2: tx_data <= uart_data_reg[0];
3: tx_data <= uart_data_reg[1];
4: tx_data <= uart_data_reg[2];
5: tx_data <= uart_data_reg[3];
6: tx_data <= uart_data_reg[4];
7: tx_data <= uart_data_reg[5];
8: tx_data <= uart_data_reg[6];
9: tx_data <= uart_data_reg[7];
10: tx_data <= 1'b1;//结束位
default:tx_data <= 1'b1;
endcase
end
end
endmodule
tesebench测试文件:
`timescale 1ns/1ns
`define clk_period 20
module uart_tx_1_tb;
reg clk=1 ;
reg rst_n;
reg [7:0] uart_data;
reg [2:0] uart_sel ;
reg data_en;
wire tx_data;
uart_tx_1 inst_uart_tx(
.clk (clk),
.rst_n (rst_n),
.uart_data (uart_data),
.uart_sel (uart_sel),
.data_en (data_en),
.tx_data (tx_data)
);
//产生50Mhz时钟
always#(`clk_period/2) clk = ~clk;
//初始化输入信号
initial begin
rst_n = 1'b0;
uart_data = 8'b0;
uart_sel = 3'd4; //115200
data_en = 1'b0;
#(`clk_period*200+1)
rst_n = 1'b1;
#(`clk_period*100+1)
data_en = 1'b1;
uart_data = 8'b0101_0111;
#(`clk_period*50)//延迟200ns
data_en=1'd0;
//等待完成重新发送
#(`clk_period*5000)
uart_data = 8'b1101_0100;
data_en=1'd1;
#(`clk_period*50)
data_en=1'd0;
#(`clk_period*5000)
$stop;
end
endmodule
波形分析
当我们输入8位的01010111时,发送的时候会一位一位的发送,开始位+低位开始的数据位+停止位,也就是start0 +11101010 + stop1 ,可看到tx_data输出为0 + 11101010 + 1 ;同理第二个字节发送时,tx_data输出为0 + 00101011 + 1
四、接收模块设计
由于UART是异步通信,也就是接收和发送采用不同的时钟,因此涉及到跨时钟域处理的问题。如下将输入打三拍来降低亚稳态现象出现。
接收模块的verilog编写:
//接收 串转并
module uart_rx_1(
input clk,
input rst_n,
input uart_rx,
output reg done,
output reg [7:0] data
);
reg uart_rx_1;
reg uart_rx_2;
reg uart_rx_3;
reg [7:0] rx_data;
reg state;
wire start_neg;
reg [12:0] bps_cnt;
reg [3:0] bit_cnt;
reg bit_flag;
//针对异步亚稳态,打三拍寄存
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
uart_rx_1 <= 1'b0;
uart_rx_2 <= 1'b0;
uart_rx_3 <= 1'b0;
end
else begin
uart_rx_1 <= uart_rx;
uart_rx_2 <= uart_rx_1;
uart_rx_3 <= uart_rx_2;
end
end
//下降沿检测
assign start_neg = (uart_rx_3 & ~uart_rx_2);
//生成state信号,指示工作有效状态
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= 1'b0;
end
else if (start_neg) begin
state <= 1'b1;
end
else if (done) begin
state <= 1'b0;
end
else begin
state <= state;
end
end
//生成波特率计数器 bps_cnt
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
bps_cnt <= 1'b0;
end
else begin
if (bps_cnt == 13'd433) begin
bps_cnt <= 1'b0;
end
else begin
bps_cnt <= bps_cnt + 1'b1;
end
end
end
//生成bit_flag
//中间时刻接收数据 更稳定
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
bit_flag <= 1'b0;
end
else if(state)begin
if (bps_cnt == 13'd216) begin
bit_flag <= 1'b1;
end
else
bit_flag <= 1'b0;
end
else begin
bit_flag <= 1'b0;
end
end
//生成bit_cnt,指示每一位发送什么数据
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
bit_cnt <= 4'b0;
end
else if (bit_flag) begin
if (bit_cnt == 4'd10) begin
bit_cnt <= 1'b0;
end
else begin
bit_cnt <= bit_cnt + 1'b1;
end
end
else begin
bit_cnt <= bit_cnt;
end
end
//生成rx_data
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
rx_data <= 8'b0;
end
else if (bit_flag) begin
case(bit_cnt)
4'd0: rx_data <= 8'b0;
4'd1: rx_data[0] <= uart_rx_3;
4'd2: rx_data[1] <= uart_rx_3;
4'd3: rx_data[2] <= uart_rx_3;
4'd4: rx_data[3] <= uart_rx_3;
4'd5: rx_data[4] <= uart_rx_3;
4'd6: rx_data[5] <= uart_rx_3;
4'd7: rx_data[6] <= uart_rx_3;
4'd8: rx_data[7] <= uart_rx_3;
4'd9: rx_data <= rx_data;
default: rx_data <= 8'b0;
endcase
end
else begin
rx_data <= rx_data;
end
end
//生成done信号
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
done <= 1'b0;
end
else if (bit_flag && (bit_cnt == 4'd10))begin
done <= 1'b1;
end
else begin
done <= 1'b0;
end
end
//数据寄存
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
data <= 1'b0;
end
else if(bit_flag && (bit_cnt == 4'd10))begin
data <= rx_data;
end
else
data <= data;
end
endmodule
tb测试:
`timescale 1ns/1ns
module uart_rx_tb;
reg clk;
reg rst_n;
reg uart_rx;
wire done;
wire [7:0] data;
//模块例化
uart_rx_1 inst_uart_rx
(
.clk (clk),
.rst_n (rst_n),
.uart_rx (uart_rx),
.done (done),
.data (data)
);
//生成时钟
initial clk = 1;
always #10 clk = ~clk;
//输入信号
initial begin
rst_n = 1'b0;
uart_rx = 1'b0;
#200;
rst_n = 1'b1;
#1000;
uart_rx = 1'b1;
#20000;
uart_rx = 1'b0;
#20000;
uart_rx = 1'b1;
#20000;
uart_rx = 1'b0;
#20000;
uart_rx = 1'b1;
#10000;
uart_rx = 1'b0;
#10000;
uart_rx = 1'b1;
#20000;
uart_rx = 1'b0;
#20000;
uart_rx = 1'b1;
#30000;
uart_rx = 1'b0;
#1000;
uart_rx = 1'b1;
#200000;
$stop;
end
endmodule
波形:
发送模块(简化)
module uart_tx(
input clk,
input rst_n,
input start,
input [7:0] data,
output reg uart_tx,
output reg done
);
reg [7:0] r_data;
reg state;
reg [9:0] baud_cnt;
reg [3:0] bit_cnt;
//参数定义
parameter BAUD_CNT_MAX = 433; //波特率最大计数值
//数据寄存一拍
always@(posedge clk or negedge rst_n)
if(!rst_n)
r_data <= 0;
else if(start)
r_data <= data;
else
r_data <= r_data;
//生成state信号,表示传输有效阶段
always@(posedge clk or negedge rst_n)
if(!rst_n)
state <= 1'b0;
else if(start)
state <= 1'b1;
else if(done)
state <= 1'b0;
else
state <= state;
//生成baud_cnt计数器
always@(posedge clk or negedge rst_n)
if(!rst_n)
baud_cnt <= 10'b0;
else if(state)
if(baud_cnt == BAUD_CNT_MAX)
baud_cnt <= 10'b0;
else
baud_cnt <= baud_cnt + 1'b1;
else
baud_cnt <= 10'b0;
//生成bit_cnt计数器,用于计数每一位
always@(posedge clk or negedge rst_n)
if(!rst_n)
bit_cnt <= 4'b0;
else if(state && (baud_cnt == 10'd1))
bit_cnt <= bit_cnt + 1'b1;
else if(bit_cnt == 10)
bit_cnt <= 4'b0;
else
bit_cnt <= bit_cnt;
//uart_tx
always@(posedge clk or negedge rst_n)
if(!rst_n)
uart_tx <= 1;
else if(state)begin
case(bit_cnt)
4'd1: uart_tx <= 0; //开始位
4'd2: uart_tx <= r_data[0];
4'd3: uart_tx <= r_data[1];
4'd4: uart_tx <= r_data[2];
4'd5: uart_tx <= r_data[3];
4'd6: uart_tx <= r_data[4];
4'd7: uart_tx <= r_data[5];
4'd8: uart_tx <= r_data[6];
4'd9: uart_tx <= r_data[7];
default: uart_tx <= 1;
endcase
end
else
uart_tx <= uart_tx;
//done信号
always@(posedge clk or negedge rst_n)
if(!rst_n)
done <= 0;
else if(bit_cnt == 4'd10)
done <= 1;
else
done <= 0;
endmodule
tb文件
`timescale 1ns/1ns
module uart_tx_tb;
reg clk;
reg rst_n;
reg start;
reg [7:0] data;
wire uart_tx;
wire done;
uart_tx inst_uart_tx
(
.clk (clk),
.rst_n (rst_n),
.start (start),
.data (data),
.uart_tx (uart_tx),
.done (done)
);
//时钟激励
initial clk = 1;
always #10 clk = ~clk;
//输入信号
initial begin
rst_n = 0;
start = 0;
data = 8'b0;
#101;
rst_n = 1;
#101;
data = 8'b0111_1001;
#21;
start = 1;
#51;
start = 0;
#100000;
data = 8'b0101_1001;
#21;
start = 1;
#51;
start = 0;
#100000;
$stop;
end
endmodule
发送波形图