UART串口发送一个字节
- 什么是UART?
UART代表通用异步收发传输器。它是微控制器内部的硬件外围设备。能够将传入和传出的数据转换为串行二进制流。使用串行到并行转换,将从外围设备接收的8位串行数据转换为并行形式。UART是一种通用串行数据总线,用于异步通信,该总线双向通信,可以实现全双工传输和接收,并以定义的波特率传输。 - 什么是波特率?
由于设备的发送端和接收端需要采用同样的速率,防止数据的丢失,于是在这里提出了波特率,即计算机在串口通信时的速率。也可以说是对信号的传输速率,或者是说线路状态改变的次数。 - UART通信协议
UART作为异步串口通信协议的一种,工作原理是将传输数据的每一个字符一位一位地传输。其中包括起始位、数据位、终止位。
包含一个起始位START和八个数据位以及终止位STOP.
串口发生模块包括两个主要部件:
1.发送波特率生成模块
2.数据发送模块
根据模块化的方式进行verilog编写
module uart_byte_tx(
Clk,
Rst_n,
send_en,
data_byte,
band_set,
Rs232_Tx,
Tx_Done,
uart_state
);
input Clk;
input Rst_n;
input [7:0]data_byte;
input send_en;
input [2:0]band_set;
output reg Rs232_Tx;
output reg Tx_Done; //表示发送完成信号
output reg uart_state;
reg [15:0] div_cnt; //分频计数器
reg bps_clk;
reg [15:0] bps_DR;//分频计数最大值
reg [3:0] bps_cnt;//波特率计数时钟
reg [7:0] r_data_byte;
localparam START_BIT = 1'b0;
localparam STOP_BIT = 1'b1;
//r_uart_state模块的实现,为了产生控制信号,让分频计数模块进行工作
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
uart_state <= 1'b0;
else if(send_en)
uart_state <= 1'b1;
else if(Tx_Done)
uart_state <= 1'b0;
else
uart_state <= uart_state;
//实现当有send_en信号时,进行data_byte的寄存,不管外部信号怎么变都不会对数据改变,只有send_en出现
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
r_data_byte <= 8'b0;
else if(send_en)
r_data_byte <= data_byte;
else
r_data_byte <= r_data_byte;
//实现DR_LUT模块的功能 (查找表)产生分频计数值的输出
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_DR <= 16'd5207;
else begin
case(band_set)
0:bps_DR <= 16'd5207;
1:bps_DR <= 16'd2603;
2:bps_DR <= 16'd1301;
3:bps_DR <= 16'd867;
4:bps_DR <= 16'd433;
default: bps_DR <= 16'd5207;
endcase
end
//开始进行逻辑设计
//首先进行分频计数器的设计
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
div_cnt <= 16'd0;
else if(uart_state) begin //uart_state相当于en_cnt
if (div_cnt == bps_DR)
div_cnt <= 16'd0;
else
div_cnt <= div_cnt+1'b1;
end
else
div_cnt <= 16'd0;
//通过分频器进行输出,进而产生bps_clk信号
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_clk <= 1'b0;
else if (div_cnt == 16'b1)
bps_clk <= 1'b1;
else
bps_clk <= 1'b0;
//将bps_clk信号送到bsp_cnt模块
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_cnt <= 4'd0;
else if(Tx_Done)
bps_cnt <= 4'd0;
else if(bps_clk)
bps_cnt <= bps_cnt + 1'b1;
else
bps_cnt <= bps_cnt;
//编写r_Tx_Done ,产生发送完成信号Tx_Done
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
Tx_Done <= 1'b0;
else if(bps_cnt == 4'b11)//可以实现比较器的功能
Tx_Done <= 1'b1;
else
Tx_Done <= 1'b0;
//实现十选一的选择器模块
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
Rs232_Tx <= 1'b1;
else begin
case(bps_cnt)
0:Rs232_Tx <= 1'b1;
1:Rs232_Tx <= START_BIT;
2:Rs232_Tx <= r_data_byte[0];
3:Rs232_Tx <= r_data_byte[1];
4:Rs232_Tx <= r_data_byte[2];
5:Rs232_Tx <= r_data_byte[3];
6:Rs232_Tx <= r_data_byte[4];
7:Rs232_Tx <= r_data_byte[5];
8:Rs232_Tx <= r_data_byte[6];
9:Rs232_Tx <= r_data_byte[7];
10:Rs232_Tx <= STOP_BIT;
default: Rs232_Tx <= 1'b1;
endcase
end
endmodule
tesebench验证
`timescale 1ns/1ns
`define clk_period 20
module uart_byte_tx_tb;
reg Clk;
reg Rst_n;
reg [7:0]data_byte;
reg send_en;
reg [2:0]band_set;
wire Rs232_Tx;
wire Tx_Done; //表示发送完成信号
wire uart_state;
uart_byte_tx uart_byte_tx(
.Clk(Clk),
.Rst_n(Rst_n),
.send_en(send_en),
.data_byte(data_byte),
.band_set(band_set),
.Rs232_Tx(Rs232_Tx),
.Tx_Done(Tx_Done),
.uart_state(uart_state)
);
initial Clk=1;
always#(`clk_period/2) Clk=~Clk;
initial begin //要考虑哪些信号需求赋初值
Rst_n=1'b0;
data_byte=8'd0;
send_en=1'd0;
band_set=3'd4;
#(`clk_period*200+1) //+1的目的是不与时钟边沿对齐,便于观察
Rst_n=1'b1;
#(`clk_period*50) //延迟50个系统周期
data_byte=8'haa;
send_en=1'd1;
#`clk_period;
send_en=1'd0;
@(posedge Tx_Done)
//等待完成重新发送
#(`clk_period*5000)
data_byte=8'h55;
send_en=1'd1;
#`clk_period
send_en=1'd0;
@(posedge Tx_Done)
#(`clk_period*5000)
$stop;
end
endmodule
//为了进行板级调试设计的顶层top文件
module uart_tx_top(Clk,Rst_n,Rs232_Tx);
input Clk;
input Rst_n;
output Rs232_Tx;
uart_byte_tx uart_byte_tx(
.Clk(Clk),
.Rst_n(Rst_n),
.send_en(send_en),
.data_byte(data_byte),
.band_set(band_set),
.Rs232_Tx(Rs232_Tx),
.Tx_Done(Tx_Done),
.uart_state(uart_state)
);
endmodule