UART串口通信verilog实现
介绍
空闲位:
UART协议规定,当总线处于空闲状态时信号线的状态为“1’即高电平,表示当前线路上没有数据传输.
起始位:
每开始一次通信时发送方先发出一个逻辑”0"的信号(低电平),表示传输字符的开始。
数据位:
起始位之后就是我们所要传输的数据,数据位可以是5、6、7、8,9位等,构成一个字符(一般都是8位)。先发送最低位,最后发送最高位。
奇偶校验位:
数据位加上这一位后,使得"1"的位数应为偶数(偶校验)或奇数(奇校验),以此来校验数据传送的正确性。校验位其实是调整个数,串口校验分几种方式:
1、无校验(no parity)
2、奇校验(odd parity):如果数据位中“1”的数目是偶数,则校验位为"1”,如果”1”的数目是奇数,校验位为"0”。
3、偶校验(even parity):如果数据为中“1”的数目是偶数,则校验位为“0”,如果为奇数,校验位为“1”。
4、mark parity:校验位始终为1(不常用)。
5、parity:校验位始终为0(不常用)。
停止位:
它是一个字符数据的结束标志。可以是1位、1.5位、2位的高电平。 由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备之间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟的机会。停止位个数越多,数据传输越稳定,但是数据传输速度也越慢。
verilog代码
参数设置:
数据位为8位
停止位为1位,无校验位
波特率为9600bps
时钟为50MHz
顶层模块
`timescale 1ns / 1ps
module uart_top(
input clk,
input rst,
input uart_rxd,
input uart_txd
);
parameter FREQ = 50000000;
parameter BPS = 9600;
wire en;
wire [7:0] uart_data;
uart_recv #( //串口接收模块
.FREQ (FREQ), //设置系统时钟频率
.BPS (BPS))
u_uart_recv(
.clk (clk),
.rst (rst),
.uart_rxd (uart_rxd),
.uart_txd (uart_data),
.rx_done (en)
);
uart_send #( //串口发送模块
.FREQ (FREQ), //设置系统时钟频率
.BPS (BPS))
u_uart_send(
.clk (clk),
.rst (rst),
.uart_din (uart_data),
.tx_en (en),
.uart_txd (uart_txd)
);
endmodule
仿真模块
`timescale 1ns / 1ps
module uart_top_tb(
);
reg clk;
reg rst;
reg uart_rxd;
initial begin
clk = 1'd0;
rst = 1'd0;
uart_rxd = 1'd1;
end
always #10 clk = ~clk;
wire uart_txd;
uart_top u_uart_top(
.clk(clk),
.rst(rst),
.uart_rxd(uart_rxd),
.uart_txd(uart_txd)
);
reg [7:0]data = 8'b11011001;
localparam delay_time = 104160;
initial begin
#100
rst = 1'd1;
#20000
uart_rxd = 1'd0;
#delay_time
uart_rxd = data[0];
#delay_time
uart_rxd = data[1];
#delay_time
uart_rxd = data[2];
#delay_time
uart_rxd = data[3];
#delay_time
uart_rxd = data[4];
#delay_time
uart_rxd = data[5];
#delay_time
uart_rxd = data[6];
#delay_time
uart_rxd = data[7];
#delay_time
uart_rxd = 1'd1;
#delay_time
#delay_time
uart_rxd = 1'd0;
#delay_time
uart_rxd = data[0];
#delay_time
uart_rxd = data[1];
#delay_time
uart_rxd = data[2];
#delay_time
uart_rxd = data[3];
#delay_time
uart_rxd = data[4];
#delay_time
uart_rxd = data[5];
#delay_time
uart_rxd = data[6];
#delay_time
uart_rxd = data[7];
#delay_time
uart_rxd = 1'd1;
end
endmodule
接收模块
`timescale 1ns / 1ps
module uart_recv(
input clk,
input rst,
input uart_rxd,
output reg [7:0] uart_txd,
output reg rx_done
);
parameter BPS = 9600;
parameter FREQ = 50000000;
localparam BPS_CNT = FREQ/BPS;
//检测uart_rxd下降沿
reg uart_txd_d0;
reg uart_txd_d1;
wire start_flag;
always @ (posedge clk or negedge rst) begin
if(!rst) begin
uart_txd_d0 <= 1'd0;
uart_txd_d1 <= 1'd0;
end
else begin
uart_txd_d0 <= uart_rxd;
uart_txd_d1 <= uart_txd_d0;
end
end
assign start_flag = uart_txd_d1 & (~uart_txd_d0);
reg [15:0] bps_cnt;
reg [3:0] rx_cnt;
reg rx_flag;
//时钟计数和接收数据计数
always @ (posedge clk or negedge rst) begin
if(!rst) begin
bps_cnt <= 16'd0;
rx_cnt <= 4'd0;
end
else if(rx_flag) begin
if(bps_cnt < BPS_CNT - 1'd1) begin
bps_cnt <= bps_cnt + 16'd1;
rx_cnt <= rx_cnt;
end
else begin
bps_cnt <= 16'd0;
rx_cnt <= rx_cnt + 4'd1;
end
end
else begin
bps_cnt <= 16'd0;
rx_cnt <= 4'd0;
end
end
//数据接收标志位
always @ (posedge clk or posedge rst) begin
if(!rst)
rx_flag <= 1'd0;
else
if(start_flag)
rx_flag <= 1'd1;
else if((bps_cnt == BPS_CNT/2) && (rx_cnt == 4'd9))
rx_flag <= 1'd0;
else
rx_flag <= rx_flag;
end
//接收数据
reg [7:0] tx_data;
always @ (posedge clk or negedge rst) begin
if(!rst)
tx_data <= 8'd0;
else if(rx_flag)
if(bps_cnt == BPS_CNT/2) begin
case(rx_cnt)
4'd1:tx_data[0] <= uart_txd_d1;
4'd2:tx_data[1] <= uart_txd_d1;
4'd3:tx_data[2] <= uart_txd_d1;
4'd4:tx_data[3] <= uart_txd_d1;
4'd5:tx_data[4] <= uart_txd_d1;
4'd6:tx_data[5] <= uart_txd_d1;
4'd7:tx_data[6] <= uart_txd_d1;
4'd8:tx_data[7] <= uart_txd_d1;
endcase
end
else
tx_data <= tx_data;
else
tx_data <= 8'd0;
end
//输出
always @ (posedge clk or negedge rst) begin
if(!rst) begin
rx_done <= 1'd0;
uart_txd <= 8'd0;
end
else if(rx_cnt == 4'd9) begin
rx_done <= 1'b1;
uart_txd <= tx_data;
end
else begin
rx_done <= 1'd0;
uart_txd <= 8'd0;
end
end
endmodule
发送模块
`timescale 1ns / 1ps
module uart_send(
input clk,
input rst,
input [7:0] uart_din,
input tx_en,
output reg uart_txd,
output reg uart_txd_busy
);
parameter BPS = 9600;
parameter FREQ = 50000000;
localparam BPS_CNT = FREQ/BPS;
//检测tx_en上升沿
reg tx_en_d0;
reg tx_en_d1;
wire start_flag;
always @ (posedge clk or negedge rst) begin
if(!rst) begin
tx_en_d0 <= 1'd0;
tx_en_d1 <= 1'd0;
end
else begin
tx_en_d0 <= tx_en;
tx_en_d1 <= tx_en_d0;
end
end
assign start_flag = tx_en_d0 & (~tx_en_d1);
reg [15:0] bps_cnt;
reg [3:0] tx_cnt;
reg tx_flag;
reg [7:0] tx_data;
//时钟计数和接收数据计数
always @ (posedge clk or negedge rst) begin
if(!rst) begin
bps_cnt <= 16'd0;
tx_cnt <= 4'd0;
end
else if(tx_flag) begin
if(bps_cnt < BPS_CNT - 1'd1) begin
bps_cnt <= bps_cnt + 16'd1;
tx_cnt <= tx_cnt;
end
else begin
bps_cnt <= 16'd0;
tx_cnt <= tx_cnt + 4'd1;
end
end
else begin
bps_cnt <= 16'd0;
tx_cnt <= 4'd0;
end
end
always @ (posedge clk or negedge rst) begin
if(!rst) begin
tx_flag <= 1'b0;
tx_data <= 8'd0;
end
else if(start_flag) begin
tx_flag <= 1'b1;
tx_data <= uart_din;
end
else if((tx_cnt==4'd9) && (bps_cnt==BPS_CNT/2)) begin
tx_flag <= 1'b0;
tx_data <= 8'd0;
end
else begin
tx_flag <= tx_flag;
tx_data <= tx_data;
end
end
always@(posedge clk or negedge rst) begin
if(!rst)
uart_txd <= 1'b1;
else if(tx_flag)
case(tx_cnt)
4'd0:uart_txd <= 1'b0;
4'd1:uart_txd <= tx_data[0];
4'd2:uart_txd <= tx_data[1];
4'd3:uart_txd <= tx_data[2];
4'd4:uart_txd <= tx_data[3];
4'd5:uart_txd <= tx_data[4];
4'd6:uart_txd <= tx_data[5];
4'd7:uart_txd <= tx_data[6];
4'd8:uart_txd <= tx_data[7];
4'd9:uart_txd <= 1'b1;
default:uart_txd <= 1'b1;
endcase
else
uart_txd <= 1'b1;
end
endmodule