1.UART简介
UART是一种采用异步串行通信方式的通用异步收发传输器(universal asynchronous receiver-transmitter),它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。
UART串口通信需要两根信号线来实现,一根TX用于串口发送,一根RX负责串口接收。
起始位:空闲时为“1”,当检测到“0”,认为数据传输开始
数据位:传输开始后传递的需要接收和发送的数据值
奇偶校验位:奇偶校验,通过来校验传输数据中“1”的个数为奇数个(奇校验)或偶数个(偶校验)来指示传输数据是否正确
停止位:数据传输结束,传输线恢复常“1”状态
此外,还需关注数据传输波特率,波特率表示一秒内传输了多少个码元数量,一般波特率为300,1200,2400,9600,19200,38400,115200等。例如9600 Baud表示一秒内传输了9600个码元信息,当一个码元只含1 bit信息时,波特率=比特率
2.模块框图
串口协议用于与其他模块之间的信息交互,包含接收模块和发送模块,信号传输线上根据波特率完成码元的接收与发送,因而接收模块主要完成并串转换,串并转换是接收和发送模块必备的基本功能,发送模块完成并串转换,接收模块完成串并转换。
3.代码
3.1顶层模块
module uart_top(
input sys_clk,
input sys_rst_n,
input uart_rxd,
output uart_txd
);
parameter FREQ = 50000000;
parameter BPS = 9600;
wire en;
wire [7:0] uart_data;
uart_recv #( //串口接收模块
.FREQ (FREQ), //设置系统时钟频率
.BPS (BPS))
u_uart_recv(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.uart_rxd (uart_rxd),
.uart_txd (uart_data),
.rx_done (en)
);
uart_send #( //串口发送模块
.FREQ (FREQ), //设置系统时钟频率
.BPS (BPS))
u_uart_send(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.uart_din (uart_data),
.tx_en (en),
.uart_txd (uart_txd)
);
endmodule
3.2接收模块
module uart_recv(
input sys_clk,
input sys_rst_n,
input uart_rxd,
output reg[7:0] uart_txd,
output reg rx_done
);
parameter BPS = 9600;
parameter FREQ = 50000000;
localparam BPS_CNT = FREQ / BPS;
wire start_flag; //对uart_rxd下降沿进行检测
reg uart_rxd_d0;
reg uart_rxd_d1;
reg rx_flag; //接收数据标志信号
reg [3:0] rx_cnt; //对接收数据计数
reg [15:0] bps_cnt; //对时钟的计数
reg [7:0] tx_data; //对接收数据进行寄存
assign start_flag = uart_rxd_d1 & (~uart_rxd_d0); //下降沿检测电路
//对uart_rxd延迟两个时钟单位
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
uart_rxd_d0 <= 1'b0;
uart_rxd_d1 <= 1'b0;
end
else begin
uart_rxd_d0 <= uart_rxd;
uart_rxd_d1 <= uart_rxd_d0;
end
end
//当start_flag来临时,开始接收数据
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
rx_flag <= 1'b0;
else
if(start_flag)
rx_flag <= 1'b1;
else if((bps_cnt == BPS_CNT/2) && (rx_cnt == 4'd9)) //接收了8个bit后,接收状态关闭
rx_flag <= 1'b0;
else
rx_flag <= rx_flag;
end
//进入接收过程后,启动系统时钟计数器与接收数据计数器
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
bps_cnt <= 16'd0;
rx_cnt <= 4'd0;
end
else if(rx_flag)
if(bps_cnt < BPS_CNT -1'b1)begin
bps_cnt <= bps_cnt + 1'b1;
rx_cnt <= rx_cnt;
end
else begin
bps_cnt <= 16'd0;
rx_cnt <= rx_cnt + 1'b1;
end
else begin
bps_cnt <= 16'd0;
rx_cnt <= 4'd0;
end
end
//通过计数器来寄存接收到的数据,tx_data
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
tx_data <= 8'd0;
else if(rx_flag)
if(bps_cnt == BPS_CNT/2)begin
case(rx_cnt)
4'd1 : tx_data[0] <= uart_rxd_d1;
4'd2 : tx_data[1] <= uart_rxd_d1;
4'd3 : tx_data[2] <= uart_rxd_d1;
4'd4 : tx_data[3] <= uart_rxd_d1;
4'd5 : tx_data[4] <= uart_rxd_d1;
4'd6 : tx_data[5] <= uart_rxd_d1;
4'd7 : tx_data[6] <= uart_rxd_d1;
4'd8 : tx_data[7] <= uart_rxd_d1;
default : ;
endcase
end
else
tx_data <= tx_data;
else
tx_data <= 8'd0;
end
//接收数据寄存到uart_txd,并输出接收完成标志位
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
rx_done <= 1'b0;
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'b0;
uart_txd <= 8'd0;
end
end
endmodule
3.3发送模块
module uart_send(
input sys_clk,
input sys_rst_n,
input [7:0] uart_din,
input tx_en,
output reg uart_txd
);
parameter BPS = 9600;
parameter FREQ = 50000000;
localparam BPS_CNT = FREQ / BPS;
wire start_flag;
reg tx_en_d0;
reg tx_en_d1;
reg tx_flag; //发送过程标志
reg [15:0] bps_cnt;
reg [3:0] tx_cnt;
reg [7:0] tx_data;
assign start_flag = tx_en && (~tx_en_d1);
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
tx_en_d0 <= 1'b0;
tx_en_d1 <= 1'b0;
end
else begin
tx_en_d0 <= tx_en;
tx_en_d1 <= tx_en_d0;
end
end
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
bps_cnt <= 16'd0;
tx_cnt <= 4'd0;
end
else if(bps_cnt < BPS_CNT-1)begin
bps_cnt <= bps_cnt+1'b1;
tx_cnt <= tx_cnt;
end
else begin
bps_cnt <= 16'd0;
tx_cnt <= tx_cnt+1'b1;
end
end
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)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 sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
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