常用的三大低速串口通信有UART,SPI,IIC。这里使用FPGA实现UART通信。UART通信只有两根线,一根是发送数据tx,一根是接受数据rx。PC和PFGA通过两个线实现数据通信。
完成FPGA部分的收发设计实现串口通信。
RS232
1、RS232 是 UART 的一种,没有时钟线,只有两根数据线,分别是 rx 和 tx,这两根线都是 1bit 位宽的。其中 rx 是接收数据的线,tx 是发送数据的线。
2、rx 位宽为 1bit,PC 机通过串口调试助手往 FPGA 发 8bit 数据时,FPGA 通过串口线rx 一位一位地接收,从最低位到最高位依次接收,最后在 FPGA 里面位拼接成 8 比特数据。
3、tx 位宽为 1bit,FPGA 通过串口往 PC 机发 8bit 数据时,FPGA 把 8bit 数据通过 tx线一位一位的传给 PC 机,从最低位到最高位依次发送,最后上位机通过串口助手按照RS232 协议把这一位一位的数据位拼接成 8bit 数据。
4、串口数据的发送与接收是基于帧结构的,即一帧一帧的发送与接收数据。每一帧除了中间包含 8bit 有效数据外,还在每一帧的开头都必须有一个起始位,且固定为 0;在每一帧的结束时也必须有一个停止位,且固定为 1,即最基本的帧结构(不包括校验等)有10bit。在不发送或者不接收数据的情况下,rx 和 tx 处于空闲状态,此时 rx 和 tx 线都保持高电平,如果有数据帧传输时,首先会有一个起始位,然后是 8bit 的数据位,接着有 1bit的停止位,然后 rx 和 tx 继续进入空闲状态,然后等待下一次的数据传输。如图 28-7 所示为一个最基本的 RS232 帧结构。
1bit表示二进制中1or0的一位。一个二进制的码元(1or0)就能携带1bit的信息(1or0)。一个八进制的码元就可以携带3bit的信息。
s232串口波特率就也是码元传输速率。9600Baud/s.表示每秒传输9600个码元。bit(bps)比特率=(Bps)波特数单个调制状态对应的二进制,上图就是9600/1。Ps:串口发送或者接受一个数据时间为波特。串口波特率为9600Bps,那么一个波特时间就为1/9600s。如果系统时钟为50Mhz,那么在9600波特下,一个波特就相当于1/9600s再除以20ns=5208个系统周期。也就是串口每传输一个bit的信息,系统时钟要保持5208个周期。
FPGA接受模块
module uart_rx
#(
parameter UART_BAUD = 'd9600, //波特率
parameter CLK_FRE = 'd50_000_000 //系统时钟
)
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire rx ,//由助手发过来的串行数据
output reg [7:0] po_data ,//传出的并行数据
output reg po_flag //数据发送的标志信号
);
parameter BAUD_MAX = CLK_FRE/UART_BAUD;//每传输一个bit数据所需要的传输时间
reg rx_reg1;//打三拍,减少亚稳态的影响
reg rx_reg2;//由于信号达到寄存器的建立时间和保持时间不满足要求
reg rx_reg3;//所以产生了亚稳态
reg start_nedge;
reg work_on;
reg [12:0] baud_cnt;
reg bit_flag;
reg [3:0] bit_cnt;
reg [7:0] rx_data;
reg rx_flag;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rx_reg1 <= 1'b1;
else
rx_reg1 <= rx;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rx_reg2 <= 1'b1;
else
rx_reg2 <= rx_reg1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rx_reg3 <= 1'b1;
else
rx_reg3 <= rx_reg2;
//检测到rx_reg的下降沿时,产生一个时钟的高电平。检测下降沿时,可以把信号延迟
//一个时钟,然后通过如下提取出一个标志信号来。
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
start_nedge <= 1'b0;
else if((rx_reg2 == 1'b0) && (rx_reg3 == 1'b1) && (work_on == 1'b0))
start_nedge <= 1'b1;
else
start_nedge <= 1'b0;
//可以通过提取出来的标志信号来确定一个使能信号。使用使能信号可以确定数据的采样范围
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
work_on <= 1'b0;
else if(start_nedge == 1'b1)
work_on <= 1'b1;
else if(bit_flag == 1'b1 && bit_cnt == 4'd8)
work_on <= 1'b0;
else
work_on <= work_on;
//波特率计数器计数,一个bit信息所需要的时间。50_000_000/9600=5208
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
baud_cnt <= 13'd0;
else if(baud_cnt == BAUD_MAX - 1 || work_on == 1'b0)
baud_cnt <= 13'd0;
else if(work_on == 1'b1)
baud_cnt <= baud_cnt + 1'b1;
//当计数器计数到中间时采样的数据最稳定
//此时拉高一个标志信息表示数据可以被取走
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
bit_flag <= 1'b0;
else if(baud_cnt == BAUD_MAX/2 - 1)
bit_flag <= 1'b1;
else
bit_flag <= 1'b0;
//有效数据个数的计数器,8个有效数据(不含起始位0和停止位1)
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
bit_cnt <= 4'd0;
else if(bit_cnt == 4'd8 && bit_flag == 1'b1)
bit_cnt <= 4'd0;
else if(bit_flag == 1'b1)
bit_cnt <= bit_cnt + 1'b1;
//输入的数据进行移位
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rx_data <= 8'h00;
else if(bit_cnt >= 4'd1 && bit_cnt <= 4'd8 && bit_flag == 1'b1)
rx_data <= {rx_reg3,rx_data[7:1]};
//输入的数据完成时,拉高一个时钟的电平
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rx_flag <= 1'b0;
else if(bit_cnt == 4'd8 && bit_flag == 1'b1)
rx_flag <= 1'b1;
else
rx_flag <= 1'b0;
//完成输出8位有效数据
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
po_data <= 8'h00;
else if(rx_flag == 1'b1)
po_data <= rx_data;
//输出数据有效标志位
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
po_flag <= 1'b0;
else
po_flag <= rx_flag;
endmodule
FPGA发送模块
module uart_tx
#(
parameter UART_BPS = 'd9600,//串口波特率
parameter CLK_FRE = 'd50_000_000//时钟频率
)
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire [7:0] pi_data ,//模块输入的8bit数据
input wire pi_flag ,//并行数据的有效标志信号
output reg tx //串转并的1bit数据
);
parameter BAUD_CNT = CLK_FRE/UART_BPS;//
reg work_on;
reg [12:0] baud_cnt;
reg bit_flag;
reg [3:0] bit_cnt;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
work_on <= 1'b0;
else if(pi_flag == 1'b1)
work_on <= 1'b1;
else if(bit_flag == 1'b1 && bit_cnt == 4'd9)
work_on <= 1'b0;
else
work_on <= work_on;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
baud_cnt <= 0;
else if(baud_cnt == BAUD_CNT - 1 || work_on == 1'b0)
baud_cnt <= 0;
else if(work_on == 1'b1)
baud_cnt <= baud_cnt + 1'b1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
bit_flag <= 1'b0;
else if(baud_cnt == 0 && work_on == 1'b1)
bit_flag <= 1'b1;
else
bit_flag <= 1'b0;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
bit_cnt <= 0;
else if(bit_cnt == 4'd9 && bit_flag == 1'b1)
bit_cnt <= 4'd0;
else if(bit_flag == 1'b1 && work_on == 1'b1)
bit_cnt <= bit_cnt + 1'b1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
tx <= 1'b1;
else if(bit_flag == 1'b1)
begin
case(bit_cnt)
0: tx <= 1'b0;
1: tx <= pi_data[0];
2: tx <= pi_data[1];
3: tx <= pi_data[2];
4: tx <= pi_data[3];
5: tx <= pi_data[4];
6: tx <= pi_data[5];
7: tx <= pi_data[6];
8: tx <= pi_data[7];
9: tx <= 1'b1;
default: tx <= 1'b1;
endcase
end
endmodule
顶层模块
module rs232
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire rx ,
output wire tx
);
wire [7:0] po_data;
wire po_flag;
uart_rx
#(
.UART_BAUD(9600), //波特率
.CLK_FRE(50_000_000) //系统时钟
)
uart_rx_inst
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.rx (rx),//由助手发过来的串行数据
.po_data (po_data),//传出的并行数据
.po_flag (po_flag) //数据发送的标志信号
);
uart_tx
#(
.UART_BPS(9600),
.CLK_FRE(50_000_000)
)
uart_tx_inst
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.pi_data (po_data),
.pi_flag (po_flag),
.tx (tx)
);
endmodule
仿真结果
串口助手调试结果