我是靠谱客的博主 内向荷花,最近开发中收集的这篇文章主要介绍FPGA学习之UART串口通信(当UART控制器从上位机接收到数据后,立即将数据输出,并发送回上位机,完成“回环测试”),觉得挺不错的,现在分享给大家,希望可以做个参考。
概述
FPGA需要控制的仅为两条信号线:RXD和TXD,即数据接收线和数据发送线,因此我们只需要关注数据接收和发送时的时序图。传送的一帧数据里面包括起始位(0),8位数据位,奇偶校验位,停止位(1)。当空闲时,10位数据均为高电平,在检测到起始位低电平时,开始传输。
波特率是衡量数据传输速率的指标,表示每秒传输的二进制位数,在此例中设为9600,所以50M/9600==5207,所以分频时要计数到5207.
1.串口接收数据的速率控制模块:用于控制串口接收的速率
module bps_rx(
input clk,
input rst_n,
input rx_en, //bps_rx开始计数的使能信号
output reg rx_sel_data, //控制数据采集的尖峰脉冲信号
output reg [3:0] rx_num //有效数据位的计数值
);
parameter bps_div_1 = 13'd5207; //50M/5207=9600频率(波特率)
parameter bps_div_2 = 13'd2603; //数据在波特率的中间值时采集
reg flag; //接收标志位,当接收到rx_en时,拉高
reg [12:0] cnt; //波特率计数
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt <= 13'b0;
else if(flag && cnt < bps_div_1)
cnt <= cnt + 1'b1;
else
cnt <= 13'b0;
always@(posedge clk or negedge rst_n)
if(!rst_n)
flag <= 0;
else if(rx_en)
flag <= 1;
else if (rx_num == 4'd10) //当数据接收完毕时,标志位清零
flag <= 0;
always @(posedge clk or negedge rst_n) //规定接收数据的范围,即一帧数据 两个起始位 8位数据位
if(!rst_n)
rx_num = 4'd0;
else if(rx_sel_data && flag)
rx_num = rx_num + 1'b1;
else if (rx_num == 4'd10)
rx_num = 4'd0;
always @(posedge clk or negedge rst_n) //数据在波特率的中间值时采集
if(!rst_n)
rx_sel_data = 1'b0;
else if(cnt == bps_div_2) //生成尖峰脉冲,尖峰脉冲为采集数据的使能信号
rx_sel_data = 1'b1;
else
rx_sel_data = 1'b0;
endmodule
2.串口串行数据的接收模块:用于接收串口串行数据
module uart_rx(
input clk,
input rst_n,
input rx_sel_data, //控制数据采集的尖峰脉冲信号
input [3:0] rx_num, //有效数据位的计数值
input rs232_rx, //输入串行数据
output rx_en, //使能信号:启动接收波特率计数
output reg tx_en, //使能信号:在接收完数据后,开始发送数据
output reg [7:0] rx_d //将采集的8位数据转换为并行数据
);
reg in_1, in_2;
reg [7:0] rx_d_r; //数据寄存器
always @(posedge clk or negedge rst_n)
if (!rst_n)
begin
in_1 <= 1'b1; //因为起始位为0,所以初值设为1
in_2 <= 1'b1;
end
else
begin
in_1 <= rs232_rx;
in_2 <= in_1;
end
assign rx_en = in_2 && (~in_1); //当in_1变为0时,说明已经开始接收数据,就可以拉高接收使能信号
//确保在一帧数据的中间8位进行数据读取
always @(posedge clk or negedge rst_n)
if (!rst_n)
begin
rx_d_r <= 8'b0;
rx_d <= 8'b0;
end
else if (rx_sel_data) //数据采集的尖峰脉冲信号来的时候开始接收数据
case (rx_num)
0:; //起始位不处理
1: rx_d_r[0] <= rs232_rx;
2: rx_d_r[1] <= rs232_rx;
3: rx_d_r[2] <= rs232_rx;
4: rx_d_r[3] <= rs232_rx;
5: rx_d_r[4] <= rs232_rx;
6: rx_d_r[5] <= rs232_rx;
7: rx_d_r[6] <= rs232_rx;
8: rx_d_r[7] <= rs232_rx;
9: rx_d <= rx_d_r; //将数据传给输出
default :;
endcase
always @(posedge clk or negedge rst_n)
if(!rst_n)
tx_en <= 0;
else if (rx_num == 9 && rx_sel_data) //在接收完停止位后 把发送标志位拉高
tx_en <= 1;
else
tx_en <= 0;
endmodule
3.串口发送数据的速率控制模块:用于控制串口发送的速率
module bps_tx(
input clk,
input rst_n,
input tx_en, //bps_tx开始计数的使能信号
output reg tx_sel_data, //控制数据采集的尖峰脉冲信号
output reg [3:0] tx_num //有效数据位的计数值
);
parameter bps_div_1 = 13'd5207; //50M/5207=9600频率(波特率)
parameter bps_div_2 = 13'd2603; //数据在波特率的中间值时采集
reg flag; //接收标志位,当接收到tx_en时,拉高
reg [12:0] cnt; //波特率计数
always@(posedge clk or negedge rst_n)
if(!rst_n)
flag <= 0;
else if(tx_en)
flag <= 1;
else if (tx_num == 4'd10) //当数据接收完毕时,标志位清零
flag <= 0;
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt <= 13'b0;
else if(flag && cnt < bps_div_1)
cnt <= cnt + 1'b1;
else
cnt <= 13'b0;
always @(posedge clk or negedge rst_n) //规定接收数据的范围,即一帧数据 两个起始位 8位数据位
if(!rst_n)
tx_num = 4'd0;
else if(tx_sel_data && flag)
tx_num = tx_num + 1'b1;
else if (tx_num == 4'd10)
tx_num = 4'd0;
always @(posedge clk or negedge rst_n) //数据在波特率的中间值时采集
if(!rst_n)
tx_sel_data = 1'b0;
else if(cnt == bps_div_2) //生成尖峰脉冲,尖峰脉冲为采集数据的使能信号
tx_sel_data = 1'b1;
else
tx_sel_data = 1'b0;
endmodule
4.串口串行数据的发送模块:用于发送串口串行数据
module uart_tx(
input clk,
input rst_n,
input tx_sel_data, //控制数据采集的尖峰脉冲信号
input [3:0] tx_num, //有效数据位的计数值
input [7:0] rx_d, //输入8wei数据
output reg rs232_tx //发送数据
);
//确保在一帧数据的中间8位进行数据读取
always @(posedge clk or negedge rst_n)
if (!rst_n)
rs232_tx <= 1'b1;
else if (tx_sel_data) //数据采集的尖峰脉冲信号来的时候开始接收数据
case (tx_num)
0:rs232_tx <= 1'b0; //起始位为0
1: rs232_tx <= rx_d[0];
2: rs232_tx <= rx_d[1];
3: rs232_tx <= rx_d[2];
4: rs232_tx <= rx_d[3];
5: rs232_tx <= rx_d[4];
6: rs232_tx <= rx_d[5];
7: rs232_tx <= rx_d[6];
8: rs232_tx <= rx_d[7];
9: rs232_tx <= 1'b1; //停止位1
default :rs232_tx <= 1'b1;
endcase
endmodule
5.顶层模块:
module uart(
input clk,
input rst_n,
input rs232_rx,
output rs232_tx
);
wire rx_en;
wire tx_en;
wire [7:0] rx_d;
wire [3:0] rx_num,tx_num;
//模块例化
bps_rx bps_rx(
.clk(clk),
.rst_n(rst_n),
.rx_en(rx_en),
.rx_num(rx_num),
.rx_sel_data(rx_sel_data)
);
uart_rx uart_rx(
.clk(clk),
.rst_n(rst_n),
.rx_en(rx_en),
.rx_num(rx_num),
.rx_sel_data(rx_sel_data),
.rx_d(rx_d),
.rs232_rx(rs232_rx),
.tx_en(tx_en)
);
bps_tx bps_tx(
.clk(clk),
.rst_n(rst_n),
.tx_en(tx_en),
.tx_num(tx_num),
.tx_sel_data(tx_sel_data)
);
uart_tx uart_tx(
.clk(clk),
.rst_n(rst_n),
.rx_d(rx_d),
.tx_num(tx_num),
.rs232_tx(rs232_tx),
.tx_sel_data(tx_sel_data)
);
endmodule
6.测试文件
`timescale 1 ns/ 1 ns
module uart_tb();
parameter T = 20;
reg clk;
reg rst_n;
reg rs232_rx;
wire rs232_tx;
initial
begin
clk = 1'b0;
rst_n = 1'b0;
rs232_rx = 1;
#200
rst_n = 1'b1;
//模拟发送一帧数据
#200 rs232_rx = 0; //起始位
#110000 rs232_rx = 0;
#110000 rs232_rx = 1;
#110000 rs232_rx = 1;
#110000 rs232_rx = 0;
#110000 rs232_rx = 0;
#110000 rs232_rx = 1;
#110000 rs232_rx = 0;
#110000 rs232_rx = 1; //停止位
#1500000 $stop;
end
always #(T/2) clk = ~clk;
uart i1 (
.rs232_rx(rs232_rx),
.rs232_tx(rs232_tx),
.clk(clk),
.rst_n(rst_n)
);
endmodule
7.RTL图:
仿真图:
工程文件上传至qq群:868412045
最后
以上就是内向荷花为你收集整理的FPGA学习之UART串口通信(当UART控制器从上位机接收到数据后,立即将数据输出,并发送回上位机,完成“回环测试”)的全部内容,希望文章能够帮你解决FPGA学习之UART串口通信(当UART控制器从上位机接收到数据后,立即将数据输出,并发送回上位机,完成“回环测试”)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复