本文基于FPGA实现了UART串口发送模块。
开发环境:Quartus17.1、Modelsim SE-64 10.2c、友善调试助手、Gvim编辑器
硬件:小梅哥AC6102_V2开发板
关于UART介绍参考了别人的csdn博客,博客地址--------UART介绍
1.什么是UART
通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),通常称作UATR,是一种异步收发传输器。将数据由串行通信与并行通信间做传输转换,作为并行输入称为串行输出的芯片。UART是一种通用串行数据总线,用于异步通信。该总线双向通信,可以实现全双工传输和接收。
2.UART通信协议
UART作为异步串口通信协议的一种,工作原理是将传输数据的每个字符一位接一位地传输。其中各位的意义如下:
起始位:先发出一个逻辑”0”的信号,表示传输字符的开始。
资料位:紧接着起始位之后。资料位的个数可以是4、5、6、7、8等,构成一个字符。通常采用ASCII码。从最低位开始传送,靠时钟定位。
奇偶校验位:资料位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验),以此来校验资料传送的正确性。
停止位:它是一个字符数据的结束标志。可以是1位、1.5位、2位的高电平。 由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。
空闲位:处于逻辑“1”状态,表示当前线路上没有资料传送。
3.UART工作原理
发送数据过程:空闲状态,线路处于高电平;当收到发送指令后,拉低线路的一个数据位的时间T,接着数据按低位到高位依次发送,数据发送完毕后,接着发送奇偶校验位和停止位,一帧数据发送完成。
数据接收过程:空闲状态,线路处于高电平;当检测到线路的下降沿(高电平变为低电平)时说明线路有数据传输,按照约定的波特率从低位到高位接收数据,数据接收完毕后,接着接收并比较奇偶校验位是否正确,如果正确则通知后续设备接收数据或存入缓冲。
由于UART是异步传输,没有传输同步时钟,为了保证数据的正确性,UART采用16倍数据波特率的时钟进行采样。每个数据有16个时钟采样,取中间的采样值,以保证采样不会滑码或误吗。一般UART一帧的数据位数为8,这样即使每个数据有一个时钟的误差,接收端也能正确地采样到数据。
UART的接收数据时序为:当检测到数据的下降沿时,表明线路上有数据进行传输,这是计数器CNT开始计数,当计数器为24=16+8时,采样的值为第0位数据;当计数器的值为40时,采样的值为第一位数据,依次类推,进行后面6个数据的采样。如果需要进行奇偶校验,则当计数器的值为152时,采样的值即为奇偶位;当计数器的值为168时,采样的值为“1”表示停止位,数据接收完成。
一个标准的10位异步串行通信协议(1个起始位、1个停止位和8个数据位)收发时序,如下图所示:
4.verilog实现UART发送模块
1.信号定义
下面列出比较重要的几个输入输出信号
①clk-------------(输入)系统时钟,开发板50M晶振,可以通过FPGA锁相环产生。
②baut_set-----(输入)波特率设置,设置波特率大小
③data_byte—(输入)要发送的八位一字节数据
④send_en-----(输入)发送使能信号,高电平有效,持续一个时钟脉冲
⑤rs_232_tx—(输出)uart串行输出
⑥uart_state—(输出)uart状态指示,1为繁忙,0位空闲
2.功能实现
现在我们要做的就是通过定义的信号来实现UART发送功能。时序图如下:
①检测到send_en发送使能信号,进入发送状态,定义一个寄存器flag_send来指示,1表示繁忙,0表示空闲。
②在flag_send为1的条件内,使用两个计数器,一个计数生成bps_clk,一个计数表示要发送数据的第几位。
③发送完停止位以后,flag_send置为0。
下面是发送模块的verilog代码:
module uart_tx(
input clk ,//50M
input rst_n ,
input [ 7: 0] data_byte ,
input send_en ,
input [ 3: 0] baud_set ,
output reg rs232_tx ,
output reg tx_done ,
output wire uart_state
);
//======================================================================\
//************** Define Parameter and Internal Signals *****************
//======================================================================/
localparam BEGIN_BIT = 1'b0 ;
localparam STOP_BIT = 1'b1 ;
localparam DATA_BITS = 10 ;
localparam BAUD_RATE_9600 = 5208 ;
localparam BAUD_RATE_19200 = 2604 ;
localparam BAUD_RATE_38400 = 1302 ;
localparam BAUD_RATE_115200= 434 ;
reg [12: 0] BAUD_RATE ;
reg bps_clk ;
reg [ 7: 0] data_byte_reg ;
reg flag_send ;
wire [10: 0] tx_data_temp ;
reg [19: 0] cnt0 ;
wire add_cnt0 ;
wire end_cnt0 ;
reg [ 3: 0] cnt1 ;
wire add_cnt1 ;
wire end_cnt1 ;
//======================================================================\
//**************************** Main Code *******************************
//======================================================================/
//data_byte_reg,寄存data_byte
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
data_byte_reg <= 8'd0;
end
else if(send_en)begin
data_byte_reg <= data_byte;
end
end
//BAUD_RATE
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
BAUD_RATE <= BAUD_RATE_9600;
end
else begin
case(baud_set)
4'd0: BAUD_RATE <= BAUD_RATE_9600;
4'd1: BAUD_RATE <= BAUD_RATE_19200;
4'd2: BAUD_RATE <= BAUD_RATE_38400;
4'd3: BAUD_RATE <= BAUD_RATE_115200;
default:BAUD_RATE <= BAUD_RATE_9600;
endcase
end
end
//flag_send
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
flag_send <= 1'b0;
end
else if(send_en)begin
flag_send <= 1'b1;
end
else if(tx_done)begin
flag_send <= 1'b0;
end
end
//cnt0
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt0 <= 0;
end
else if(add_cnt0)begin
if(end_cnt0)
cnt0 <= 0;
else
cnt0 <= cnt0 + 1;
end
else begin
cnt0 <= 0;
end
end
assign add_cnt0 = flag_send;
assign end_cnt0 = add_cnt0 && cnt0 == BAUD_RATE-1;
//cnt1
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt1 <= 0;
end
else if(add_cnt1)begin
if(end_cnt1)
cnt1 <= 0;
else
cnt1 <= cnt1 + 1;
end
end
assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && cnt1 == DATA_BITS-1;
//tx_done
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
tx_done <= 1'b0;
end
else if(end_cnt1)begin
tx_done <= 1'b1;
end
else begin
tx_done <= 1'b0;
end
end
//bps_clk
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
bps_clk <= 1'b0;
end
else if(cnt0 == 1'b1)begin
bps_clk <= 1'b1;
end
else begin
bps_clk <= 1'b0;
end
end
//tx_data_temp
assign tx_data_temp = {STOP_BIT, data_byte_reg, BEGIN_BIT };
//rs232_tx 串行发送数据
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rs232_tx <= 1'b1;
end
else if(flag_send && bps_clk)begin
rs232_tx <= tx_data_temp[cnt1];
end
else if(!flag_send)begin
rs232_tx <= 1'b1;
end
end
//uart_state
assign uart_state = flag_send;
endmodule
下面是整个uart串口发送模块的整个工程,可以通过按键发送,同时在串口调试软件上收到
下载链接:https://download.csdn.net/download/qq_31348733/11249021