目录
part 1的链接放这里:FPGA串口接收与发送 详解 (part 1 )_居安士的博客-CSDN博客
part 2来继续详解FPGA的串口
一、单个数据接收程序
串口数据接收程序输入为10位串行数据RX,输出为8位并行数据dout
接收波特率生成程序会给接收波特率使能RX_baud_en,而本程序需要给接收波特率生成程序开始接收数据标志位RX_start
此外,我们还需要设置一个数据输出完成使能信号dout_en,设置10位数据寄存器[9:0] data_save,计数10位数据[3:0] data_num
单个数据接收步骤:
(1)开始接收信号rx_start的生成:把RX延时3拍,取RX的下降沿(因为起始位为低电平,从高电平到低电平意味着开始通信)作为开始接收信号时刻rx_start=1,把10位都接收完(data_num=10)作为接收完成的标志rx_start=0。
(2)确定data_num计数的个数:来一个RX_baud_en计数一次
(3) 接收数据:来一个clk,采样一次,并且存入data_save,拼位data_save
(4)输出8位并行数据:把data_save的1-8位依次存入dout
(5)输出传输完成信号dout_en:把rx_start=0与rx_clk延时两次后=1作为结束信号(确保dout_en 输出是在rx_start=0即接收完成之后)
module data_RX(
input clk_25m,
input reset,
input RX,//10位串行数据
input RX_baud_en,//接收数据波特率
output reg [7:0] dout,//8位并行数据
output reg RX_start,//开始接收数据标志位
output reg dout_en //数据输出完成使能信号
);
reg [3:0] data_num;//计数10位数据
reg [9:0] data_save;//10位数据寄存器
/*把RX延时3拍*/
reg RX0;//把串行数据RX信号延时3拍
reg RX1;
reg RX2;
always @(posedge clk_25m)begin
if(reset)begin
RX0<=1'b1;
RX1<=1'b1;
RX2<=1'b1;
end
else begin
RX0<=RX;
RX1<=RX0;
RX2<=RX1;
end
end
/*产生RX_start*/
always@(posedge clk_25m)begin
if(reset)begin
RX_start<=1'd0;
end
else if(RX2&&!RX1)begin//RX2下降沿开始接收
RX_start<=1'd1;
end
else if(data_num==4'd10)begin//当data_num=10时,停止接收,保证所有数据都接收完成
RX_start<=1'd0;
end
else begin
RX_start<=RX_start;
end
end
/*计数data_num*/
always@(posedge clk_25m)begin
if(reset)begin
data_num<=4'd0;
end
else if(RX_start)begin
if(RX_baud_en)begin
data_num<=data_num+4'd1;
end
else begin
data_num<=data_num;
end
end
else begin
data_num<=4'd0;
end
end
/*数据接收,产生data_save*/
always@(posedge clk_25m)begin
if(reset)begin
data_save<=10'd0;
end
else if(RX_start)begin
if(RX_baud_en)begin
data_save<={RX2,data_save[9:1]};//把RX2的数据一位一位移位进data_save
end
else begin
data_save<=data_save;//不需要清零,下一次的数据会挤掉上一次的
end
end
else begin
data_save<=10'd0;
end
end
/*数据接收,产生dout*/
always @(posedge clk_25m)begin
if(reset)begin
dout<=8'd0;
end
else begin
dout<=data_save[8:1];//把1到8位给dout
end
end
/*输出数据接收完成信号*/
reg RX_baud_en_dly;//RX_baud_en打一拍
always@(posedge clk_25m)begin
if(reset)begin
RX_baud_en_dly<=1'd0;
end
else begin
RX_baud_en_dly<=RX_baud_en;
end
end
always@(posedge clk_25m)begin
if(reset)begin
dout_en<=1'd0;
end
else if(!RX_start&&RX_baud_en_dly)begin//接收数据完成
dout_en<=1'd1;
end
else begin
dout_en<=1'd0;
end
end
endmodule
二、单个数据顶层代码
将发送数据波特率生成程序,单个数据发送程序,接收数据波特率生成程序,单个数据接收程序,都例化到顶层文件里
module uart_top(
input clk_25m,
input reset,
input [7:0] din,
input din_en,
input RX,
output [7:0]dout
output dout_en,
output TX
);
wire TX_start;
wire TX_baud_en;
wire RX_start;
wire RX_baud_en;
TX_baud inst_TX_baud(
.clk_25m (clk_25m),
.reset (reset),
.TX_start (TX_start),//开始发送数据标志位
.TX_baud_en(TX_baud_en) //发送数据波特率使能
);
data_TX inst_data_TX(
.clk_25m (clk_25m),
.reset (reset),
.TX_baud_en(TX_baud_en),//发送数据波特率使能
.din (din),//8位数据
.din_en (din_en),//数据使能
.TX_start (TX_start),//开始发送数据标志位
.TX (TX) //串行数据
);
RX_baud inst_RX_baud(
.clk_25m (clk_25m),
.reset (reset),
.RX_start (RX_start),//开始接收数据标志位
.RX_baud_en (RX_baud_en) //接收波特率使能
);
data_RX inst_data_RX(
.clk_25m (clk_25m),
.reset (reset),
.RX (RX),//10位串行数据
.RX_baud_en (RX_baud_en),//接收数据波特率
.dout (dout),//8位并行数据
.RX_start (RX_start),//开始接收数据标志位
.dout_en (dout_en) //数据输出完成使能信号
);
endmodule
对顶层文件进行仿真,这里我们可以把TX和RX连接起来,也就是说,将8位数据通过TX变成串行数据,将此串行数据给RX,RX转化成原先发送的8位数据
module TB_uart_top(
);
reg clk_25m=0;
reg reset;
reg [7:0]din;
reg din_en;
wire [7:0]dout;
wire dout_en;
wire RX;
wire TX ;
assign RX=TX;
uart_top inst_uart_top(
.clk_25m (clk_25m) ,
.reset (reset) ,
.din (din) ,
.din_en (din_en) ,
.RX (RX),
.dout (dout),
.dout_en (dout_en),
.TX (TX)
);
initial begin
clk_25m=0;
reset=1;
din_en=0;
din=8'h55;
#1000;
reset=0;
din_en=1;
#30;
din_en=0;
end
always #20 clk_25m=~clk_25m;
endmodule
由于使用了assign语法,所以TX和RX都需要定义为wire型
首先看一下波特率:
RX确实在TX周期的中间采样
再看一下数据发送与接收 :
dout和din是相同的,都是8'd55