FPGA-UART(串口通信)

一、串口通信介绍

通用异步收发传输器,通常称为UART,是一种采用异步串行通信方式的收发传输器。在串行通信时,要求通信双方都采用一种标准接口,使不同的设备可以方便地连接起来进行通信。RS-232-C接口(又称EIARS-232-C)是常用的一种串行通信接口。
串口的主要功能为:在发送数据时将并行数据转换成串行数据进行传输,在接收数据时将接收到的串行数据转换成并行数据。
为了实现串口通信,这里使用FPGA-EGO1开发板做串口回环实验,要求PC端通过串口助手发送数据给FPGA,FPGA接收到数据后再发回PC端,并在串口助手处显示数值。即串口助手发送什么就能接收回什么。
在这里插入图片描述
在串行通信中,数据是按位传送的,因此数据传输速率用每秒钟传送二进制代码的位数表示,称为波特率。每秒传送一个格式位就是1波特,即1波特=1bit/s(位/秒)。常见的波特率有9600bit/s、115200bit/s等,其他标准的波特率有1200bit/s、2400bit/s、4800bit/s、19200bit/s、38400bit/s、57600bit/s。

二、源代码

1、顶层模块

module uart_top(clk,rst_n,uart_rx,uart_tx);
input clk;       //时钟,100MHZ
input rst_n;     //复位,低电平有效
input uart_rx;   //FPGA通过串口接收的数据
output uart_tx;  //FPGA通过串口发送的数据

wire [7:0]data;
wire data_vld;

//接收模块
uart_rx u1(.clk(clk),    
           .rst_n(rst_n),
           .din(uart_rx),
           .dout(data),
           .dout_vld(data_vld)
           );

//发送模块
uart_tx u2(.clk(clk),
           .rst_n(rst_n),
           .din_vld(data_vld),
           .din(data),
           .dout(uart_tx)
           );

endmodule

2、发送

module uart_tx
#(parameter CLK = 100_000_000,  //100MHZ时钟
  parameter BPS = 9600,         //9600波特率
  parameter BPS_CNT = CLK/BPS   //波特率计数
  )
(clk,rst_n,din,din_vld,dout);
input wire clk;
input wire rst_n;
input wire [7:0]din;   //输入数据
input wire din_vld;    //输入数据的有效指示
output reg dout;       //输出数据

reg flag;
reg [7:0]din_tmp;
reg [15:0]cnt0;      //波特率计数
wire add_cnt0;
wire end_cnt0;
reg [3:0]cnt1;       //数据位计数
wire add_cnt1;
wire end_cnt1;
wire [9:0]data;      //发送数据

//数据暂存(din可能会消失)
always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      din_tmp <= 8'd0;
    else if(din_vld)
      din_tmp <= din;
  end

//发送状态指示
always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      flag <= 0;
    else if(din_vld)
      flag <= 1;
    else if(end_cnt1)
      flag <= 0;
  end

//波特率计数
always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      cnt0 <= 0;
    else if(add_cnt0)
      begin
        if(end_cnt0)
          cnt0 <= 0;
        else
          cnt0 <= cnt0 + 1'b1;
      end
  end

assign add_cnt0 = flag;
assign end_cnt0 = (cnt0 == (BPS_CNT-1)) || end_cnt1;
//开始1位+数据8位+停止1位,共10位
always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      cnt1 <= 0;
    else if(add_cnt1)
      begin
        if(end_cnt1) 
          cnt1 <= 0;
        else
          cnt1 <= cnt1 + 1'b1;
      end
  end

assign add_cnt1 = end_cnt0;
assign end_cnt1 = (cnt1 == (10-1))&&(cnt0==(BPS_CNT/2-1));
//数据输出
assign data = {1'b1,din_tmp,1'b0};

always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      dout <= 1'b1;
    else if(flag)
      dout <= data[cnt1];
  end

endmodule

3、接收

module uart_rx
#(parameter CLK = 100_000_000,  //100MHZ时钟
  parameter BPS = 9600,         //波特率
  parameter BPS_CNT = CLK/BPS   //波特率计数
  )
(clk,rst_n,din,dout,dout_vld);
input clk,rst_n;
input din;                  //输入数据
output reg [7:0] dout;      //输出数据
output reg dout_vld;        //输出数据的有效指示

reg rx0;
reg rx1;
reg rx2;
wire rx_en;
reg flag;
reg [15:0]cnt0;  //波特率计数
wire add_cnt0;
wire end_cnt0;
reg [3:0]cnt1;   //数据位计数
wire add_cnt1;
wire end_cnt1;
reg [7:0]data;

//消除亚稳态+下降沿检测
always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      begin
        rx0 <= 1; rx1 <= 1; rx2 <= 1;
      end
    else
      begin
        rx0 <= din; rx1 <= rx0; rx2 <= rx1;
      end
  end
  
assign rx_en = rx2 && ~rx1;       //rx2=1且rx1=0,检测下降沿

//接收状态机指示
always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      flag <= 0;
    else if(rx_en)
      flag <= 1;
    else if(end_cnt1)
      flag <= 0;
  end

//波特率计数
always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      cnt0 <= 0;
    else if(add_cnt0)
      begin
        if(end_cnt0)
          cnt0 <= 0;
        else
          cnt0 <= cnt0 + 1'b1;
      end
  end

assign add_cnt0 = flag;
assign end_cnt0 = (cnt0 == BPS_CNT - 1) || end_cnt1;

//开始1位(不接收)+数据8位+停止1位(不接收),共10位
always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      cnt1 <= 0;
    else if(add_cnt1)
      begin
        if(end_cnt1)
          cnt1 <= 0;
        else 
          cnt1 <= cnt1 + 1'b1;
      end
  end
 
assign add_cnt1 = end_cnt0;
assign end_cnt1 = (cnt1 == (10-1))&&(cnt0 == (BPS_CNT/2-1));   

//缓存数据 
always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      data <= 8'd0;
    else if((cnt1>=1)&&(cnt1<=8)&&(cnt0==BPS_CNT/2-1))
      data[cnt1-1] <= rx2;
  end   
 
 //输出数据
always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      dout <= 0;
    else if(end_cnt1)
      dout <= data;
  end
    
always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      dout_vld <= 0;
    else if(end_cnt1)
      dout_vld <= 1;
    else
      dout_vld <= 0;
  end  

endmodule

4、端口配置说明

在这里插入图片描述

三、上板验证

fpga-uart

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值