实现一个串口接收,通过上位机发送数据,查看串口接收模块是否收到数据。
学习UART通信原理及其硬件电路设计,使用FPGA实现UART通信中的数据接收部分设计,并使用ISSP工具来进行板级验证。
在上篇串口发送模块中提到串口发送一个字节的时序图,如下图所示:
其实串口接收模块在接收这样的时序字节时,是每一位进行采样的,一般情况下,我们认定每一位数据的中间点是最稳定,因此认定采集中间时刻的电平即此位数据电平,具体串口接收时序如下图所示:
但在实际应用中,只采一次该位数据中间位是不可靠的,很容易被干扰导致数据出错,本次实验单bit位数据接收方式使用多次采样比较电平发生概率法。(src:《FPGA系统设计与验证实战指南_V1.2》)
由上图知,单bit数据平均分成16小段,在该位数据刚发生和即将变化(上图深灰色部分)认定是极有不稳定时期,因此判定采集电平无效。
在中间时期(上图浅灰色部分)认定数据处于稳定状态,即中间采样方法,但也不能否定该期间受到干扰而产生错误电平,因此对该时段电平多次采样,本次对中间时刻采样6次,采样后对高低电平求发生概率,多次出现电平作为该位数据电平。
对于实现整个串口接收模块,理解以下输入输出端口:
1.时钟Clk及复位信号Rst_n必不可少;
2.如串口发送一样,选择不同波特率信号baud_set[2:0];
3.由串口发送得知,数据是一位位的传过来的,一次接收模块接收信号Rx232_Rx信号为1位即可;
4.数据接收完毕,应需要1个时钟周期的高脉冲Rx_Done信号;
5.接收数据组成[7:0]data_byte信号输出查看。
在按键消抖篇提到按键信号需做异步信号同步处理,同理,Rx232_Rx信号跨越时钟域时,也需做异步信号同步处理,以及如何去做边沿检测,实现具体原理在按键消抖篇已详细介绍,这里不做赘述。
与串口发送模块一致,串口接收模块的主要组成部分也是波特率时钟生成设计。
不同的是,这里接收采用单bit位16点采样,速度提升到原来的16倍,假设原来发送波特率是9600bps,现在接收波特率为9600*16=153600bps;
根据上篇串口发送波特率表格,计算得出接收模块设计分频计数值,如下表所示
代码借助上篇串口发送的波特率对应关系,即利用LUT查找表设计选择器和产生波特率时钟的分频计数器,首先一套三板斧下来,代码如下图所示,除去bps_cnt的计数值不一样外,其余部分与串口发送一致。
当我们所需要采样时钟bps_clk已经具备了,下一步就要开始对时钟上升沿开始计数了。
如上图所示,起始位+8位数据位+停止位,每位采样16次,需要采样160次后再给计数值清零。
还有就是起始位的检测,我们知道起始位START_BIT其实是一个持续的低电平,但是如何去判断该起始位是干扰信号而不是真正的起始位呢?
现在当数据线上出现了下降沿,假设此时是起始位,那么中间采样6次所得的计算值累加结果大于2,那么可知中间6次采样中至少有一半是高电平,不满足起始信号持续低电平要求,故可以判断此时数据线上的信号是干扰信号。(src:《FPGA系统设计与验证实战指南_V1.2》)
当我们接收到的数据经过同步处理后,最终需要传到data_byte[7:0]中,但是我们对每一位采样16次,取中间6次,因此需要中间过渡取值过程。
因此首先第一步先将10位数据的每位采样6次值累加赋给每一位,然后在与4(当出现111000,该次设计不做处理,发现1或0的次数为4次,110000,111100)比较,得出该位数据。
对照上图时序图值,当bps_cnt计数值为6,7,8,9,10,11时,为中间6次采样,我们需要将这6次值累加赋值给START_BIT位,余下位数据同理。
因此在这里我们需要定义一个ram,将中间8位数据寄存,起始位和停止位单独定义,故该ram具有8位地址,而且每一位位宽是3位。
中间8位数据位如何去判断是什么数据呢?这里(src:《FPGA系统设计与验证实战指南_V1.2》)提供将高位比较,因为3(011)和4(100)会出现高位转换,因此当数据位寄存值最高位是1还是0就可以得出该位是1还是0。
1.在这里使用上篇串口发送数据模块的输出来实现产生待测模块数据的输入,因此只需要将发送模块的Rx232_Tx连接到接收模块上的Rx232_Rx上即可。
//--------------------------------------------------------------------------------------------
// Component name : uart_rx_tb
// Author : 硬件嘟嘟嘟
// time : 2020.04.23
// Description : 串口发送模块
// src : FPGA系统设计与验证实战指南_V1.2
//--------------------------------------------------------------------------------------------
`timescale 1ns/1ns
`define clk_period 20
module uart_rx_tb;
reg Clk;
reg Rst_n;
wire [7:0]data_byte_r;
wire Rx_Done;
reg [7:0]data_byte_t;
reg send_en;
reg [2:0]baud_set;
wire Rx232_Tx;
wire Tx_Done;
wire uart_state;
uart_rx uart_rx(
.Clk(Clk),
.Rst_n(Rst_n),
.baud_set(baud_set),
.Rx232_Rx(Rx232_Tx),
.data_byte(data_byte_r),
.Rx_Done(Rx_Done)
);
uart_tx uart_tx(
.Clk(Clk),
.Rst_n(Rst_n),
.data_byte(data_byte_t),
.send_en(send_en),
.baud_set(baud_set),
.Rx232_Tx(Rx232_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_t = 8'd0;
send_en = 1'd0;
baud_set = 3'd4;
#(`clk_period*20 + 1 );
Rst_n = 1'b1;
#(`clk_period*50);
data_byte_t = 8'hbb;
send_en = 1'd1;
#`clk_period;
send_en = 1'd0;
@(posedge Tx_Done)
#(`clk_period*5000);
data_byte_t = 8'h66;
send_en = 1'd1;
#`clk_period;
send_en = 1'd0;
@(posedge Tx_Done)
#(`clk_period*5000);
$stop;
end
endmodule
在ISSP中能够正确读到串口所发信息。