概述
文章目录
- 前言
- 一、Uart串口通信
- 二、串口异步通信实现
- 1.程序框图
- 2.波特率设置模块
- 3.串口发送控制模块
- 4.串口发送控制模块
- 三、结果
- 1、仿真结果
- 2、板级调试结果
- 设计文件与仿真文件
前言
(完整代码在文末,包括仿真文件与设计文件,通过仿真与板级验证)本文利用verilog语言实现uart串口异步通信,FPGA接收串口发来的数据,并将接收到的数据通过tx端发送到PC端,在PC端串口打印显示数据
开发板:SF-AT7
软件平台:Vivado 2016.2
一、Uart串口通信
- uart串口通信是一种异步串行全双工通信方式,tx端用于数据发送,rx端用于数据接收。信号线空闲时为高电平。
- 由于是异步通信方式,数据发送会包装成数据帧的形式发送,帧格式为:1个起始位(0)、8个数据位(用户数据)、1个奇偶校验位(用于简单的纠错以保证传输的可靠性)、1和2个停止位(1),其中奇偶校验位不是必须的。下图为帧格式结构。
- 那么该如何检测到数据发送呢?可以注意到数据帧格式中第一个bit是低电平,当FPGA的rx端检测到信号线上有下降沿产生时,表示有数据传送过来,根据预先设置好的波特率对数据接收接收,由于数据是串行从低位到高位传输,接收到的数据暂时存储在寄存器中,待接收完1字节的数据,通过串并转换保存接收到的数据。
- 发送时通过tx信号线按照设置好的波特率将数据发送出去,数据发送仍然要按照数据帧格式发送,即先发送1bit的低电平,再从低位到高位发送数据。
二、串口异步通信实现
1.程序框图
FPGA实时检测uart_rx信号是否有数据,若接收到数据,你把接收到的数据通过uart_tx发送给PC端。
我们知道串口数据传输在设置的波特率下进行,因此需要有专门的波特率产生单元,且发送与接收分别对应一个波特率产生单元,将波特率设置包装成一个模块,分别在发送与接收端例化该模块即可,这是两个独立的硬件资源,属于逻辑复制,而并非资源共享。
整个程序具体实现过程就是有一个顶层模块,在顶层模块中例化波特率设置模块、串口发送与接收处理模块。
speed_setting u2_speed_rx(
.clk(clk_25m), //波特率选择模块
.rst_n(sys_rst_n),
.bps_start(bps_start1),
.clk_bps(clk_bps1)
);
//UART接收数据处理
my_uart_rx_q u3_my_uart_rx(
.clk(clk_25m), //接收数据模块
.rst_n(sys_rst_n),
.uart_rx(uart_rx),
.rx_data(rx_data),
.rx_int(rx_int),
.clk_bps(clk_bps1),
.bps_start(bps_start1)
);
//-------------------------------------
//UART发送信号波特率设置
speed_setting u4_speed_tx(
.clk(clk_25m), //波特率选择模块
.rst_n(sys_rst_n),
.bps_start(bps_start2),
.clk_bps(clk_bps2)
);
//UART发送数据处理
my_uart_tx_q u5_my_uart_tx(
.clk(clk_25m), //发送数据模块
.rst_n(sys_rst_n),
.rx_data(rx_data),
.rx_int(rx_int),
.uart_tx(uart_tx),
.clk_bps(clk_bps2),
.bps_start(bps_start2)
);
2.波特率设置模块
- 前面说过波特率设置本质是一个计数器,常用的波特率如9600、115200等是指1s內传输数据的个数,1/9600就是一个周期所对应的时长。以25MHz为例,一个周期是40ns ,那么计数周期就是:1/9600/40ns*1000000000(注意单位统一),设置一个计数器,当计数次数达到9600bps时,标志位有一个周期的高脉冲信号,该标志位信号用来控制tx端传送数据位的切换或者rx端接收数据位的切换。
- 根据上面的分析结果,利用verilog语言编程实现,可以用一个always块来实现计数器,另一个always块实现标志位置位。为了保证数据采集更加准备,我们选择在数据传送中间采样,即计数器记到9600一半的时候进行数据采样。
- 针对接收端,uart端口rx端接收到数据总线的信号。总线上数据都是以字节的形式传输,而uart通信协议中规定数据是串行接收的,因此接收时要进行串并转换,串并转换的速率由波特率决定。接收到信号后波特率设置模块开始计数,定时产生维持一个周期高电平的采样信号。
代码如下:
module speed_setting(
input clk,
input rst_n,
input bps_start,
output clk_bps
);
`define BPS_9600
`define CLK_PERIORD 40 //定义时钟周期为40ns(25MHz)
`define BPS_SET 9600 //定义通信波特率为9600bps(将需要的波特率省去两个零后定义即可)
`define BPS_PARA (1_000_000_000/`CLK_PERIORD/`BPS_SET)//10_000_000/`CLK_PERIORD/96; //波特率为9600时的分频计数值
`define BPS_PARA_2 (`BPS_PARA/2)//BPS_PARA/2; //波特率为9600时的分频计数值的一半,用于数据采样
reg[12:0] cnt; //分频计数
reg clk_bps_r; //波特率时钟寄存器
//----------------------------------------------------------
reg[2:0] uart_ctrl; // uart波特率选择寄存器
//计数器计数
always@(posedge clk or negedge rst_n)begin
if(!rst_n) cnt <= 13'd0 ;
else if(bps_start)begin
if(cnt < BPS_PARA) cnt <= cnt + 13'd1 ;
else cnt <= 13'd0 ;
end
else cnt <= 13'd0 ;
end
//采样信号标志位
always@(posedge clk or negedge rst_n)begin
if(!rst_n) clk_bps_r <= 1'b0 ;
else if(cnt == BPS_PARA_2) clk_bps_r <= 1'b1 ;
else clk_bps_r <= 1'b0 ;
end
assign clk_bps = clk_bps_r ;
endmodule
3.串口发送控制模块
该模块实现对UART接收信号uart_rx进行解码,并实现串并转换,并将数据保存在【rx_data[7:0]】中。具体程序内部实现框图如下:
- 下降沿检测
通过检测信号【uart_rx】信号下降沿。来判断是否有信号发送过来。边沿检测一般会利用多个锁存器,所存uart_rx的状态,根据前后状态的逻辑运算结果进行判断,如果前一个状态是高电平,最新的状态为低电平,则表示出现了下降沿。
module my_uart_rx(
clk,rst_n,
uart_rx,rx_data,rx_int,
clk_bps,bps_start
);
input clk; // 25MHz主时钟
input rst_n; //低电平复位信号
input uart_rx; // RS232接收数据信号
input clk_bps; // clk_bps的高电平为接收或者发送数据位的中间采样点
output bps_start; //接收到数据后,波特率时钟启动信号置位
output[7:0] rx_data; //接收数据寄存器,保存直至下一个数据来到
output rx_int; //接收数据中断信号,接收到数据期间始终为高电平
//----------------------------------------------------------------
reg uart_rx0,uart_rx1,uart_rx2,uart_rx3; //接收数据寄存器,滤波用
wire neg_uart_rx; //表示数据线接收到下降沿
always @ (posedge clk or negedge rst_n)
if(!rst_n) begin
uart_rx0 <= 1'b0;
uart_rx1 <= 1'b0;
uart_rx2 <= 1'b0;
uart_rx3 <= 1'b0;
end
else begin
uart_rx0 <= uart_rx;
uart_rx1 <= uart_rx0;
uart_rx2 <= uart_rx1;
uart_rx3 <= uart_rx2;
end
assign neg_uart_rx = uart_rx3 & uart_rx2 & ~uart_rx1 & ~uart_rx0;
-
波特率控制模块
该模块因为信号下降沿的到来,使得【bps_start】信号置为1,启动波特率设置模块。 -
计数采样模块
由于数据从低位到高位串行传输,所以要先用一个寄存器暂存数据,且第一位是标识位,不应该将其存储在寄存器中,采用【num】从0开始计数,计数到1时在每个波特率高电平有效的一个周期内从低位到高位保存数据。
always @ (posedge clk or negedge rst_n)
if(!rst_n) begin
rx_temp_data <= 8'd0;
num <= 4'd0;
rx_data_r <= 8'd0;
end
else if(rx_int) begin //接收数据处理
if(clk_bps) begin //读取并保存数据,接收数据为一个起始位,8bit数据,1或2个结束位
num <= num+1'b1;
case (num)
4'd1: rx_temp_data[0] <= uart_rx; //锁存第0bit
4'd2: rx_temp_data[1] <= uart_rx; //锁存第1bit
4'd3: rx_temp_data[2] <= uart_rx; //锁存第2bit
4'd4: rx_temp_data[3] <= uart_rx; //锁存第3bit
4'd5: rx_temp_data[4] <= uart_rx; //锁存第4bit
4'd6: rx_temp_data[5] <= uart_rx; //锁存第5bit
4'd7: rx_temp_data[6] <= uart_rx; //锁存第6bit
4'd8: rx_temp_data[7] <= uart_rx; //锁存第7bit
default: ;
endcase
end
else if(num == 4'd9) begin //我们的标准接收模式下只有1+8+1(2)=11bit的有效数据
num <= 4'd0; //接收到STOP位后结束,num清零
rx_data_r <= rx_temp_data; //把数据锁存到数据寄存器rx_data中
end
end
assign rx_data = rx_data_r;
4.串口发送控制模块
uart接收端接收数据完成后,通过【uart_tx】端将接收到的数据【rx_data】f发送出去。程序流程框图如下:
注意:因为要完全按照帧格式发送数据,所以要先发送一个波特率周期的低电平,在从低到高传输数据位。
reg uart_tx_r ;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
num <= 4'd0 ;
uart_tx_r <= 1'b1;
end
else if(tx_en)begin
if(clk_bps)begin
num <= num + 4'd1 ;
case(num)
4'd0 : uart_tx_r <= 1'b0 ;
4'd1 : uart_tx_r <= tx_data[0] ;
4'd2 : uart_tx_r <= tx_data[1] ;
4'd3 : uart_tx_r <= tx_data[2] ;
4'd4 : uart_tx_r <= tx_data[3] ;
4'd5 : uart_tx_r <= tx_data[4] ;
4'd6 : uart_tx_r <= tx_data[5] ;
4'd7 : uart_tx_r <= tx_data[6] ;
4'd8 : uart_tx_r <= tx_data[7] ;
4'd9 : uart_tx_r <= 1'b1 ;
default: uart_tx_r <= 1'b1;
endcase
end
else if(num == 4'd10)begin
num <= 4'd0 ;
end
end
end
assign uart_tx = uart_tx_r;
三、结果
1、仿真结果
- 仿真波形可以看到,uart_rx端将信号【8‘haa】接收过来,且是在每个bps周期的中间接收,然后再通过【uart_tx】端发送出去,在每个【clk_bps】有效的一个时钟周期内从低位到高位传输接收到的数据
2、板级调试结果
- 利用串口调试工具,发送8bit数据【aa】,可以看到接收端成功接收到该数据。
设计文件与仿真文件
https://pan.baidu.com/s/1dnoyo84pEtMlk5fSQdaqSA 提取码:p4q1
最后
以上就是动人摩托为你收集整理的FPGA-Verilog实现uart串口异步通信前言一、Uart串口通信二、串口异步通信实现三、结果设计文件与仿真文件的全部内容,希望文章能够帮你解决FPGA-Verilog实现uart串口异步通信前言一、Uart串口通信二、串口异步通信实现三、结果设计文件与仿真文件所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复