一、设计思路
1.端口设置
为实现UART串口接收,所设计的模块应具有如下端口:
时钟、复位、波特率设置、串行数据输入、并行数据输出、一次数据接收完成标志。如图:
module uart_byte_rx(
Clk, //模块时钟50M
Rst_n, //模块复位
baud_set, //波特率设置
Rs232_Rx, //RS232数据输入
data_byte, //并行数据输出
Rx_Done //一次数据接收完成标志
);
2.状态分析
空闲状态时,Tx必须为1.
开始发送数据时,发送的第一位数据为0,代表起始位。
紧接着的是8位的数据位。
数据位后面是1位的停止位,停止位的值为1。
一帧数据格式为“0 X X X X X X X X 1”。
3.其他注意事项
考虑到实际应用场景中可能会存在许多不正常的脉冲干扰,所以为了确保采集到的数据的准确性,我们将每位数据采集6次。
我们不应把每位数据等分为6份进行采集,这是因为数据产生时在跳变沿处可能会有极不稳定的状态。因此我们将数据分为16份,在中间的6份进行采集。
二、描述思路
1.代码设计思路
首先需要设置同步寄存器消除亚稳态,
reg s0_Rs232_Rx,s1_Rs232_Rx;//同步寄存器
reg tmp0_Rs232_Rx,tmp1_Rs232_Rx;//数据寄存器
//同步寄存器,消除亚稳态
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
s0_Rs232_Rx <= 1'b0;
s1_Rs232_Rx <= 1'b0;
end
else begin
s0_Rs232_Rx <= Rs232_Rx;
s1_Rs232_Rx <= s0_Rs232_Rx;
end
//数据寄存器
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
tmp0_Rs232_Rx <= 1'b0;
tmp1_Rs232_Rx <= 1'b0;
end
else begin
tmp0_Rs232_Rx <= s1_Rs232_Rx;
tmp1_Rs232_Rx <= tmp0_Rs232_Rx;
end
然后沿用与UART发送相同的思路,由输入端口设置波特率
//设置波特率(即给定计数器模长)
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_DR <= 16'd324;
else begin
case(baud_set)
0:bps_DR <= 16'd324;
1:bps_DR <= 16'd162;
2:bps_DR <= 16'd80;
3:bps_DR <= 16'd53;
4:bps_DR <= 16'd26;
default:bps_DR <= 16'd324;
endcase
end
计数器:
//计数器
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
div_cnt <= 16'd0;
else if(uart_state)begin
if(div_cnt == bps_DR)
div_cnt <= 16'd0;
else
div_cnt <= div_cnt + 1'b1;
end
else
div_cnt <= 16'd0;
利用此计数器,设置采集信号的高电平脉冲:
// bps_clk gen
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_clk <= 1'b0;
else if(div_cnt == 16'd1) //当计数器计到1时将波特率时钟拉高,其他时候拉低,这样就产生了相应频率的高脉冲
bps_clk <= 1'b1;
else
bps_clk <= 1'b0;
然后是对产生的高脉冲进行计数
//对产生的高脉冲进行计数
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_cnt <= 8'd0;
else if(bps_cnt == 8'd159 | (bps_cnt == 8'd12 && (START_BIT > 2)))
bps_cnt <= 8'd0;
else if(bps_clk)
bps_cnt <= bps_cnt + 1'b1;
else
根据高脉冲的计数值进行数据的采样:
//内部寄存器r_data_byte负责将6次采集到的值加起来,当采集到的1个数超过3时,第二位数据变为1,否则为0
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
START_BIT = 3'd0;
r_data_byte[0] <= 3'd0;
r_data_byte[1] <= 3'd0;
r_data_byte[2] <= 3'd0;
r_data_byte[3] <= 3'd0;
r_data_byte[4] <= 3'd0;
r_data_byte[5] <= 3'd0;
r_data_byte[6] <= 3'd0;
r_data_byte[7] <= 3'd0;
STOP_BIT = 3'd0;
end
else if(bps_clk)begin
case(bps_cnt)
0:begin
START_BIT = 3'd0;
r_data_byte[0] <= 3'd0;
r_data_byte[1] <= 3'd0;
r_data_byte[2] <= 3'd0;
r_data_byte[3] <= 3'd0;
r_data_byte[4] <= 3'd0;
r_data_byte[5] <= 3'd0;
r_data_byte[6] <= 3'd0;
r_data_byte[7] <= 3'd0;
STOP_BIT = 3'd0;
end
6,7,8,9,10,11:START_BIT <= START_BIT + s1_Rs232_Rx;
22,23,24,25,26,27:r_data_byte[0] <= r_data_byte[0] + s1_Rs232_Rx;
38,39,40,41,42,43:r_data_byte[1] <= r_data_byte[1] + s1_Rs232_Rx;
54,55,56,57,58,59:r_data_byte[2] <= r_data_byte[2] + s1_Rs232_Rx;
70,71,72,73,74,75:r_data_byte[3] <= r_data_byte[3] + s1_Rs232_Rx;
86,87,88,89,90,91:r_data_byte[4] <= r_data_byte[4] + s1_Rs232_Rx;
102,103,104,105,106,107:r_data_byte[5] <= r_data_byte[5] + s1_Rs232_Rx;
118,119,120,121,122,123:r_data_byte[6] <= r_data_byte[6] + s1_Rs232_Rx;
134,135,136,137,138,139:r_data_byte[7] <= r_data_byte[7] + s1_Rs232_Rx;
150,151,152,153,154,155:STOP_BIT <= STOP_BIT + s1_Rs232_Rx;
default:
begin
START_BIT = START_BIT;
r_data_byte[0] <= r_data_byte[0];
r_data_byte[1] <= r_data_byte[1];
r_data_byte[2] <= r_data_byte[2];
r_data_byte[3] <= r_data_byte[3];
r_data_byte[4] <= r_data_byte[4];
r_data_byte[5] <= r_data_byte[5];
r_data_byte[6] <= r_data_byte[6];
r_data_byte[7] <= r_data_byte[7];
STOP_BIT = STOP_BIT;
end
endcase
end
这里值得注意的是,当6次采集到的值相加大于3时,结果的第2位会变成1,因此只需要观察r_data_byte的第2位值即可确定采到的是0还是1。
最后将采集到的数据送到并行输出端
//设置并行数据输出
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
data_byte <= 8'd0;
else if(bps_cnt == 8'd159)begin //将采集结果的第2位赋值给并行输出端
data_byte[0] <= r_data_byte[0][2];
data_byte[1] <= r_data_byte[1][2];
data_byte[2] <= r_data_byte[2][2];
data_byte[3] <= r_data_byte[3][2];
data_byte[4] <= r_data_byte[4][2];
data_byte[5] <= r_data_byte[5][2];
data_byte[6] <= r_data_byte[6][2];
data_byte[7] <= r_data_byte[7][2];
end
2.源代码
module uart_byte_rx(
Clk, //模块时钟50M
Rst_n, //模块复位
baud_set, //波特率设置
Rs232_Rx, //RS232数据输入
data_byte, //并行数据输出
Rx_Done //一次数据接收完成标志
);
input Clk;
input Rst_n;
input [2:0]baud_set;
input Rs232_Rx;
output reg [7:0]data_byte;
output reg Rx_Done;
reg s0_Rs232_Rx,s1_Rs232_Rx;//同步寄存器
reg tmp0_Rs232_Rx,tmp1_Rs232_Rx;//数据寄存器
reg [15:0]bps_DR;//分频计数器计数最大值
reg [15:0]div_cnt;//分频计数器
reg bps_clk;//
reg [7:0]bps_cnt;
reg uart_state;
reg [2:0] r_data_byte [7:0];
//reg [7:0] tmp_data_byte;
reg [2:0] START_BIT,STOP_BIT;
wire nedege;
//同步寄存器,消除亚稳态
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
s0_Rs232_Rx <= 1'b0;
s1_Rs232_Rx <= 1'b0;
end
else begin
s0_Rs232_Rx <= Rs232_Rx;
s1_Rs232_Rx <= s0_Rs232_Rx;
end
//数据寄存器
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
tmp0_Rs232_Rx <= 1'b0;
tmp1_Rs232_Rx <= 1'b0;
end
else begin
tmp0_Rs232_Rx <= s1_Rs232_Rx;
tmp1_Rs232_Rx <= tmp0_Rs232_Rx;
end
assign nedege = !tmp0_Rs232_Rx & tmp1_Rs232_Rx;
//设置波特率(即给定计数器模长)
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_DR <= 16'd324;
else begin
case(baud_set)
0:bps_DR <= 16'd324;
1:bps_DR <= 16'd162;
2:bps_DR <= 16'd80;
3:bps_DR <= 16'd53;
4:bps_DR <= 16'd26;
default:bps_DR <= 16'd324;
endcase
end
//计数器
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
div_cnt <= 16'd0;
else if(uart_state)begin
if(div_cnt == bps_DR)
div_cnt <= 16'd0;
else
div_cnt <= div_cnt + 1'b1;
end
else
div_cnt <= 16'd0;
// bps_clk gen
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_clk <= 1'b0;
else if(div_cnt == 16'd1) //当计数器计到1时将波特率时钟拉高,其他时候拉低,这样就产生了相应频率的高脉冲
bps_clk <= 1'b1;
else
bps_clk <= 1'b0;
//对产生的高脉冲进行计数
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_cnt <= 8'd0;
else if(bps_cnt == 8'd159 | (bps_cnt == 8'd12 && (START_BIT > 2)))
bps_cnt <= 8'd0;
else if(bps_clk)
bps_cnt <= bps_cnt + 1'b1;
else
bps_cnt <= bps_cnt;
//设置标志接收完毕的信号
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
Rx_Done <= 1'b0;
else if(bps_cnt == 8'd159)
Rx_Done <= 1'b1;
else
Rx_Done <= 1'b0;
//设置并行数据输出
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
data_byte <= 8'd0;
else if(bps_cnt == 8'd159)begin //将采集结果的第2位赋值给并行输出端
data_byte[0] <= r_data_byte[0][2];
data_byte[1] <= r_data_byte[1][2];
data_byte[2] <= r_data_byte[2][2];
data_byte[3] <= r_data_byte[3][2];
data_byte[4] <= r_data_byte[4][2];
data_byte[5] <= r_data_byte[5][2];
data_byte[6] <= r_data_byte[6][2];
data_byte[7] <= r_data_byte[7][2];
end
//内部寄存器r_data_byte负责将6次采集到的值加起来,当采集到的1个数超过3时,第二位数据变为1,否则为0
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
START_BIT = 3'd0;
r_data_byte[0] <= 3'd0;
r_data_byte[1] <= 3'd0;
r_data_byte[2] <= 3'd0;
r_data_byte[3] <= 3'd0;
r_data_byte[4] <= 3'd0;
r_data_byte[5] <= 3'd0;
r_data_byte[6] <= 3'd0;
r_data_byte[7] <= 3'd0;
STOP_BIT = 3'd0;
end
else if(bps_clk)begin
case(bps_cnt)
0:begin
START_BIT = 3'd0;
r_data_byte[0] <= 3'd0;
r_data_byte[1] <= 3'd0;
r_data_byte[2] <= 3'd0;
r_data_byte[3] <= 3'd0;
r_data_byte[4] <= 3'd0;
r_data_byte[5] <= 3'd0;
r_data_byte[6] <= 3'd0;
r_data_byte[7] <= 3'd0;
STOP_BIT = 3'd0;
end
6,7,8,9,10,11:START_BIT <= START_BIT + s1_Rs232_Rx;
22,23,24,25,26,27:r_data_byte[0] <= r_data_byte[0] + s1_Rs232_Rx;
38,39,40,41,42,43:r_data_byte[1] <= r_data_byte[1] + s1_Rs232_Rx;
54,55,56,57,58,59:r_data_byte[2] <= r_data_byte[2] + s1_Rs232_Rx;
70,71,72,73,74,75:r_data_byte[3] <= r_data_byte[3] + s1_Rs232_Rx;
86,87,88,89,90,91:r_data_byte[4] <= r_data_byte[4] + s1_Rs232_Rx;
102,103,104,105,106,107:r_data_byte[5] <= r_data_byte[5] + s1_Rs232_Rx;
118,119,120,121,122,123:r_data_byte[6] <= r_data_byte[6] + s1_Rs232_Rx;
134,135,136,137,138,139:r_data_byte[7] <= r_data_byte[7] + s1_Rs232_Rx;
150,151,152,153,154,155:STOP_BIT <= STOP_BIT + s1_Rs232_Rx;
default:
begin
START_BIT = START_BIT;
r_data_byte[0] <= r_data_byte[0];
r_data_byte[1] <= r_data_byte[1];
r_data_byte[2] <= r_data_byte[2];
r_data_byte[3] <= r_data_byte[3];
r_data_byte[4] <= r_data_byte[4];
r_data_byte[5] <= r_data_byte[5];
r_data_byte[6] <= r_data_byte[6];
r_data_byte[7] <= r_data_byte[7];
STOP_BIT = STOP_BIT;
end
endcase
end
//控制uart_state寄存器
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
uart_state <= 1'b0;
else if(nedege)
uart_state <= 1'b1;
else if(Rx_Done || (bps_cnt == 8'd12 && (START_BIT > 2)))
uart_state <= 1'b0;
else
uart_state <= uart_state;
endmodule
3.顶层模块
module uart_rx_top(
Clk, //模块时钟
Rst_n, //模块复位
Rs232_Rx //RS232数据输入
);
input Clk;
input Rst_n;
input Rs232_Rx;
reg [7:0]data_rx_r;
wire [7:0]data_rx;
wire Rx_Done;
uart_byte_rx uart_byte_rx(
.Clk(Clk),
.Rst_n(Rst_n),
.baud_set(3'd0),
.Rs232_Rx(Rs232_Rx),
.data_byte(data_rx),
.Rx_Done(Rx_Done)
);
//这是一个探针
ISSP issp(
.probe(data_rx_r),
.source()
);
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
data_rx_r <= 8'd0;
else if(Rx_Done)
data_rx_r <= data_rx;
else
data_rx_r <= data_rx_r;
endmodule
4.testbench代码
`timescale 1ns/1ns
`define clk_period 20
module uart_byte_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 Rs232_Tx;
wire Tx_Done;
wire uart_state;
uart_byte_rx uart_byte_rx(
.Clk(Clk),
.Rst_n(Rst_n),
.baud_set(baud_set),
.Rs232_Rx(Rs232_Tx),
.data_byte(data_byte_r),
.Rx_Done(Rx_Done)
);
uart_byte_tx uart_byte_tx(
.Clk(Clk),
.Rst_n(Rst_n),
.data_byte(data_byte_t),
.send_en(send_en),
.baud_set(baud_set),
.Rs232_Tx(Rs232_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'haa;
send_en = 1'd1;
#`clk_period;
send_en = 1'd0;
@(posedge Tx_Done)
#(`clk_period*5000);
data_byte_t = 8'h55;
send_en = 1'd1;
#`clk_period;
send_en = 1'd0;
@(posedge Tx_Done)
#(`clk_period*5000);
$stop;
end
endmodule
三、仿真
仿真结果如图
四、小结
这次的学习可以算是“UART发送设计”的后续,发送与接收的大致思路差不多 。通过发送和接收我们可以实现FPGA与计算机之间的通信。
版权声明:本文为m0_51261356原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。