下面是串口接收代码。实际运行时,发现i有时候会从10跳变为2或8,但程序中却没有这样的语句,让人百思不得其解。
module UARTReceiver #(
parameter SYSCLK = 50000000
)(
input clock, // 系统时钟
input nrst, // 模块复位
input rx, // 串口接收引脚
input [23:0] baud_rate, // 波特率
output reg received, // 是否收到了数据
output reg [7:0] data // 收到的数据内容 (received信号为上升沿或者高电平时才可以读取)
);
wire _rx = rx;
integer counter; // 波特率计数器
reg [3:0] bit_i; // 当前正在接收第几位数据 (0为起始位, 1-8为数据位, 9为停止位, 10为空闲)
wire bit_sample = (counter == SYSCLK / baud_rate / 2 - 1); // 采样信号
wire bit_end = (counter == SYSCLK / baud_rate - 1); // 位结束信号
// 接收数据
always @(posedge clock, negedge nrst) begin
if (!nrst) begin
// 模块复位
bit_i <= 10;
counter <= 0;
received <= 0;
end
else begin
if (bit_i <= 9) begin
if (bit_end) begin
// 当前位结束, 准备接收下一位
bit_i <= bit_i + 1'b1;
counter <= 0;
end
else begin
if (bit_sample) begin
// 当前位数据采样
if (bit_i == 0 && _rx)
bit_i <= 10; // 起始位错误, 停止接收
else if (bit_i >= 1 && bit_i <= 8)
data[bit_i - 1] <= _rx; // 收到数据位
else if (bit_i == 9) begin
// 收到停止位
bit_i <= 10; // 接收完毕
if (_rx)
received <= 1;
end
end
counter <= counter + 1;
end
end
else if (!_rx) begin
// 检测到起始位, 开始接收
bit_i <= 0;
counter <= 1;
received <= 0;
end
else begin
// 空闲
if (bit_end) begin
counter <= 0;
received <= 0;
end
else if (counter != 0)
counter <= counter + 1;
end
end
end
endmodule
而且这个错误出现的概率非常大,连续发几次长一点的串口数据就会出错。 第二张图片中,本来0x68后面该接收到的是0x70,就是因为i由10变成了8,完全打乱了后面的接收流程,数据被解码成了0xdc。
解决办法是给串口输入引脚加一级寄存器缓冲,将wire _rx = rx改为:
// 用寄存器缓冲一下串口输入引脚rx, 以免输入信号对bit_i的赋值产生影响
// (比如收到起始位时bit_i本应从10变为0, 但是实际运行时bit_i却从10直接跳到2或8, 致使后面的数据全部出错)
reg _rx;
always @(posedge clock, negedge nrst) begin
if (!nrst)
_rx <= 1;
else
_rx <= rx;
end
注意必须用非阻塞赋值(给次态赋值),不能用阻塞赋值(给现态赋值)。
加了一级寄存器缓冲后,问题就解决了,i清零时,不会再出现i由10(二进制1010)变为2(二进制0010)或8(二进制1000)的错误了。
这可能是因为串口输入引脚rx的下降沿不太陡,程序中又有两个if语句用到了rx,导致逻辑混乱。
加了寄存器缓冲,_rx就是FPGA内部的信号,就是标准的下降沿。