目录
(附源码)FPGA驱动将串口接收到的数据发送出去!!!,看完你就懂了。
1.前言
之前我们介绍了串口通信的发送部分,即每秒自动发送一帧数据,那么如果我们不想让他一直发送数据该怎么办呢?下面我们就来介绍一下不自动发送数据,当接收到数据时,将接收到的数据发送出来,没有接收到数据时不发送。最后贴出代码供大家参考一下,希望可以起到抛砖引玉的作用。
2.串口通信简介
本次还是同样介绍一下串口通信,防止没看过之前博客的同学不了解。
日常通信方式中主要分为串行通信和并行通信,并行通信通常情况下是由多个发送或接收数据线组成的,每根线传输一位或多位,传输速率较快,但成本较高,不适合用于长距离通信。而串行通信通常是数据发送或接收在一条数据线上,数据的每一位按特定的通信协议顺序传输,这种方法会减少使用成本,但传输速率较并行传输来说较慢。而串口通信协议数据串行通信,所以我们本次主要来讲解下串行通信。
串行通信主要分为两种,同步串行通信和异步串行通信,同步串行通信需要通信双方在同一时钟控制下同步传输数据,如SPI、IIC等。异步串行通信是指不规则数据段传输特性的串行数据传输,如串口通信等。UART采用异步串行通信方式,在发送数据时,将并行数据转化成串行数据发送,在接收端将接收到的串行数据转化成并行数据来处理。
串口通信数据线包括TX和RX,TX用来发送,RX用来接收,连接为TX接RX,RX接TX。串口通信数据帧格式如下图所示。串口通信数据帧由起始位,数据位,奇偶校验位和停止位组成,起始位低电平有效,8位数据位用来传输一个8位的数据(还可以是5、6、7位,大多数情况下是8位),奇偶校验位用来平衡数据位中“1”的个数+奇偶校验位“1”的个数为奇数或者偶数(如果不使用奇偶检验位数据位可以是9位),最后停止位高电平有效。串口通信速率由波特率来表示,常用的有9600、19200、38400、57600、115200等等,比如115200表示1s之内可以传输115200个位,即一位的传输时间为1/115200s。
3.程序设计
本次使用verilog实现串口通信,系统主要分为三个模块,分别是串口发送、串口接收和主函数。下面将分别实现。串口发送部分即上篇博客实现的功能,只不过是将原来的每隔一秒自动发送的控制信号去掉,同时添加发送使能信号以及发送完成信号,例化为该模块的输入输出,供主模块调用。对此我们就简单说到这里。串口接收部分主要流程也是同样的,判断每一位接收的数据,起始位、数据位、停止位等,从而将串行数据整合成并行数据,我这里在一帧数据的每一位判断一次,如果希望抗干扰能力强,可以在一帧的每一位判断多次来确定当前位的数值,同时在接收中存在一个亚稳态的问题,即在系统时钟边沿接收到下降沿信号,这是就无法接收到下降沿信号,可能造成接收不到起始位从而把数据位的某一位判断成了起始位,造成数据接收紊乱,解决这一办法的问题也很简单,就是添加一个锁存器,将接收到的数据锁存起来,在下一个时钟到来的时候判断。最后主函数模块用来例化一个串口接收和发送的模块,并实现相关逻辑,将接收到的数据送给发送模块发送。之后编写testbench文件进行波形仿真,最后进行下板验证。
仿真波形如图所示,可以看到tx将rx接收到的数据发送了出来。
最后下板验证如下图所示,通过串口助手发送并接收数据,可以正常发送和接收数据。
基于quartus II的工程地址如下:
FPGA纯verliog实现串口通信,串口回环资源-优快云文库
部分程序展示:
uart_byte_tx.v
/*
功能:串口发送一个字节(8位)模块设计
作者:王志川
时间:2024.6.29
*/
module uart_byte_tx(
input CLK,//50MHZ时钟信号
input RST,//复位信号
input send_en,//发送使能信号
input [7:0]data,//发送的数据
output reg send_done,//发送完成信号
output reg uart_tx//串口发送引脚
);
/*
波特率115200即每秒发送115200个数据位
本次开发板时钟频率为50MHZ
本次支持的波特率包括4800,9600,19200,38400,115200,128000,230400,256000;
其中发送间隔最长的为4800,间隔为208333ns
50MHZ的时钟相邻上升沿的时间间隔为20ns,大约需要10417个时钟上升沿,对应二进制数位宽为14位
其余波特率对应的上升沿计数次数为5208,2604,1302,434,391,217,195
*/
/*
baud_set=0,baud=4800;
baud_set=1,baud=9600;
baud_set=2,baud=19200;
baud_set=3,baud=38400;
baud_set=4,baud=115200;
baud_set=5,baud=128000;
baud_set=6,baud=230400;
baud_set=7,baud=256000;
*/
//波特率选择,0-7对应上述波特率
reg [13:0]baud_cnt;//计数值
reg [2:0]baud_set = 3'd4; //波特率选择
always@(*)
begin
case(baud_set)
0:baud_cnt <= 10417;
1:baud_cnt <= 5208;
2:baud_cnt <= 2604;
3:baud_cnt <= 1302;
4:baud_cnt <= 434;
5:baud_cnt <= 391;
6:baud_cnt <= 217;
7:baud_cnt <= 195;
default:baud_cnt <= 434;
endcase
end
reg [13:0]baud_cnts;//波特率计数值
reg [3:0]cnt;//发送位计数
//实现波特率计数器
always@(posedge CLK or negedge RST)
begin
if(!RST)
baud_cnts <= 14'd0;//复位清零
else if(send_en)begin
if(baud_cnts == baud_cnt-1)//计数到最大值清零
baud_cnts <= 14'd0;
else
baud_cnts <= baud_cnts + 1'd1;//否则计数器自加
end
else
baud_cnts <= 14'd0;
end
//实现位计数器,即发送位的计数
always@(posedge CLK or negedge RST)
begin
if(!RST)
cnt <= 4'd0;//复位清零
else if(baud_cnts == baud_cnt-1)begin//计数到最大值才开始发送位
if(cnt == 4'd11)
cnt <= 4'd0;
else
cnt <= cnt +1'd1;
end
else if(send_done == 1'd1)
cnt <= 4'd0;
end
//位发送实现逻辑
always@(posedge CLK or negedge RST)
begin
if(!RST)
uart_tx <= 1'd1;//复位
else
case(cnt)
0:begin
send_done <= 1'd0;//起始位
uart_tx <= 1'd1;
end
1:uart_tx <= 1'd0;//开始发送
2:uart_tx <= data[0];
3:uart_tx <= data[1];
4:uart_tx <= data[2];
5:uart_tx <= data[3];
6:uart_tx <= data[4];
7:uart_tx <= data[5];
8:uart_tx <= data[6];
9:uart_tx <= data[7];
10:uart_tx <= 1'd1;//停止位
11:send_done <= 1'd1;//发送完成
default:uart_tx<= 1'd1;
endcase
end
endmodule
4.总结
本次串口通信到此结束,希望各位学有所成,有什么问题可以私信我。