FPGA-串口通信的原理和发送模块

简介: FPGA-串口通信的原理和发送模块

了解串口通信首先要了解串口通信的原理:


串口通信的概念非常简单,串口按位(bit)发送和接收字节。尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。它很简单并且能够实现远距离通信。比如IEEE488定义并行通行状态时,规定设备线总长不得超过20米,并且任意两个设备间的长度不得超过2米;而对于串口而言,长度可达1200米。典型地,串口用于ASCII码字符的传输。通信使用3根线完成:(1)地线,(2)发送,(3)接收。由于串口通信是异步的,端口能够在一根线上发送数据同时在另一根线上接收数据。其他线用于握手,但是不是必须的。串口通信最重要的参数是波特率、数据位、停止位和奇偶校验。对于两个进行通信的端口,这些参数必须匹配:  

a,波特率:这是一个衡量通信速度的参数。它表示每秒钟传送的bit的个数。例如300波特表示每秒钟发送300个bit。当我们提到时钟周期时,我们就是指波特率例如如果协议需要4800波特率,那么时钟是4800Hz。这意味着串口通信在数据线上的采样率为4800Hz。通常电话线的波特率为14400,28800和36600。波特率可以远远大于这些值,但是波特率和距离成反比。高波特率常常用于放置的很近的仪器间的通信,典型的例子就是GPIB设备的通信。    

 b,数据位:这是衡量通信中实际数据位的参数。当计算机发送一个信息包,实际的数据不会是8位的,标准的值是5、7和8位。如何设置取决于你想传送的信息。比如,标准的ASCII码是0~127(7位)。扩展的ASCII码是0~255(8位)。如果数据使用简单的文本(标准 ASCII码),那么每个数据包使用7位数据。每个包是指一个字节,包括开始/停止位,数据位和奇偶校验位。由于实际数据位取决于通信协议的选取,术语“包”指任何通信的情况。  

 c,停止位:用于表示单个包的最后一位。典型的值为1,1.5和2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。   

 d,奇偶校验位:在串口通信中一种简单的检错方式。有四种检错方式:偶、奇、高和低。当然没有校验位也是可以的。对于偶和奇校验的情况,串口会设置校验位(数据位后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位。例如,如果数据是011,那么对于偶校验,校验位为0,保证逻辑高的位数是偶数个。如果是奇校验,校验位为1,这样就有3个逻辑高位。高位和低位不真正的检查数据,简单置位逻辑高或者逻辑低校验。这样使得接收设备能够知道一个位的状态,有机会判断是否有噪声干扰了通信或者是否传输和接收数据是否不同步。

发送模块的设计:

串口发送,通过按键控制指令类型。可在PC端显示相应指令。

指令一:I like FPGA.

指令二:I like Verilog.

指令三:I like 107.

后来加了指令切换功能通过按键切换(这里没有画出来)

image.png

模块设计:


div.v

moduleuart_div(clk,rst_n,bps_sel,bps_clk    );
inputclk      ;//输入系统时钟inputrst_n    ;//复位信号input     [1:0] bps_sel  ;//波特率选择outputbps_clk  ;//输出波特率时钟信号regbps_clk  ;
parameterBPS_4800=13'd5208    ,BPS_9600=13'd2604    ,BPS_19200=13'd1302    ,BPS_115200=13'd217     ;reg      [12:0] div_cnt  ;
reg      [12:0] time_div ;
//波特率选择模块always@(*)beginif(rst_n==1'b0)begintime_div=BPS_9600;
endelsebegincase(bps_sel)
2'b00: time_div = BPS_4800;2'b01: time_div = BPS_9600;2'b10: time_div = BPS_19200;2'b11: time_div = BPS_115200;default:time_div=BPS_9600;
endcaseendend//波特率时钟计数模块always@(posedgeclkornegedgerst_n)beginif(rst_n==1'b0)begindiv_cnt<=1'b0;endelseif(div_cnt==time_div-1'b1)begindiv_cnt<=0;
endelsebegindiv_cnt<=div_cnt+1'b1;endend//波特率时钟输出模块always@(posedgeclkornegedgerst_n)beginif(rst_n==1'b0)beginbps_clk<=1'b0;endelseif(div_cnt<(time_div-1'b1))beginbps_clk<=1'b1;endelsebeginbps_clk<=1'b0;endendendmodule

串口通信发送模块:


moduleuart_txd(clk,rst_n,Date_byte,send_en,txd,txd_finish,uart_state    );
inputclk        ;//输入时钟inputrst_n      ;//复位信号input    [7:0] Date_byte  ;//发送数据inputsend_en    ;//发送使能outputtxd        ;//串口发送outputtxd_finish ;//发送完成位outputuart_state ;//串口状态位regtxd        ;
regtxd_finish ;
reguart_state ;
reg      [7:0] date_buf   ;
//数据寄存模块always@(posedgeclkornegedgerst_n)beginif(rst_n==1'b0)begindate_buf<=8'd0;endelseif(send_en==1'b1)begindate_buf<=Date_byte;
endelsebegindate_buf<=date_buf;
endend//串口状态模块always@(posedgeclkornegedgerst_n)beginif(rst_n==1'b0)beginuart_state<=1'b0;endelseif(send_en==1'b1)beginuart_state<=1'b1;endelseif(txd_finish==1'b1)beginuart_state<=1'b0;endelsebeginuart_state<=uart_state;
endendreg      [3:0] dat_cnt    ;
//数据计数模块always@(posedgeclkornegedgerst_n)beginif(rst_n==1'b0)begindat_cnt<=4'd0;endelseif(txd_finish==1'b1)begindat_cnt<=4'd0;endelseif(send_en==1'b1)begindat_cnt<=dat_cnt+1'b1;endelsebegindat_cnt<=4'd0;endend//串口发送完成标志模块always@(posedgeclkornegedgerst_n)beginif(rst_n==1'b0)begintxd_finish<=1'b0;endelseif(dat_cnt==4'd10)begintxd_finish<=1'b1;endelsebegintxd_finish<=1'b0;endend//数据发送模块always@(posedgeclkornegedgerst_n)beginif(rst_n==1'b0)begintxd<=1'b1;endelseif(send_en==1'b0)begintxd<=1'b1;endelsebegincase(dat_cnt)
4'd0 :txd<=1'b1;
4'd1 :txd<=1'b0;//发送位4'd2 :txd<=date_buf[0];4'd3 :txd<=date_buf[1];4'd4 :txd<=date_buf[2];4'd5 :txd<=date_buf[3];4'd6 :txd<=date_buf[4];4'd7 :txd<=date_buf[5];4'd8 :txd<=date_buf[6];4'd9 :txd<=date_buf[7];4'd10:txd<=1'b1;//停止位default:txd<=1'b1;endcaseendendendmodule

数据缓冲模块:


module txd_data_buf(clk,rst_n,txd_finish,sel_dat,dat_buf_en,data_out
    );
  input           clk       ;//输入时钟
  input           rst_n     ;//复位信号
  input           txd_finish;//一位数据发送完成标志
  input     [1:0] sel_dat   ;
  output    [7:0] data_out  ;
  reg       [7:0] data_out  ;
  output          dat_buf_en;//数据发送使能位
  reg             dat_buf_en;
  reg       [4:0] dat_cnt   ;
  parameter       DAT_FULL_1 =    4'd12    ,
                    DAT_FULL_2 =    4'd15    ,
          DAT_FULL_3 =    4'd11    ;
  //数据发送使能模块
  always@(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
      dat_buf_en<=1'b0;
    end
    else if(sel_dat==2'b01)begin
      if(dat_cnt==dat_ful)begin
        dat_buf_en<=1'b0;
      end
      else begin
        dat_buf_en<=1'b1;
      end
    end
    else if(sel_dat==2'b10)begin
      if(dat_cnt==dat_ful)begin
        dat_buf_en<=1'b0;
      end
      else begin
        dat_buf_en<=1'b1;
      end
    end
    else if(sel_dat==2'b11)begin
      if(dat_cnt==dat_ful)begin
        dat_buf_en<=1'b0;
      end
      else begin
        dat_buf_en<=1'b1;
      end
    end
    else begin
      dat_buf_en<=dat_buf_en;
    end
  end
  //数据计数模块
  always@(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
      dat_cnt<=1'b0;
    end
    else if(dat_cnt==dat_ful)begin
      dat_cnt<=dat_cnt;
    end
    else if(dat_buf_en==1'b0)begin
      dat_cnt<=1'b0;
    end
    else if(txd_finish==1'b1)begin
      dat_cnt<=dat_cnt+1'b1;
    end
    else begin
      dat_cnt<=dat_cnt;
    end
  end
  //数据选择模块
  always@(*)begin
    if(rst_n==1'b0)begin
      data_out=0;
    end
    else if(dat_buf_en==1'b0)begin
      data_out=0;
    end
    else if(sel_dat==2'b01)begin
      case(dat_cnt)
        4'd0 :data_out="I";
        4'd1 :data_out=" ";
        4'd2 :data_out="L";
        4'd3 :data_out="i";
        4'd4 :data_out="k";
        4'd5 :data_out="e";
        4'd6 :data_out=" ";
        4'd7 :data_out="F";
        4'd8 :data_out="P";
        4'd9 :data_out="G";
        4'd10:data_out="A";
        4'd11:data_out=".";
        default:data_out=0;
      endcase
    end
    else if(sel_dat==2'b10)begin
      case(dat_cnt)
        4'd0 :data_out="I";
        4'd1 :data_out=" ";
        4'd2 :data_out="L";
        4'd3 :data_out="i";
        4'd4 :data_out="k";
        4'd5 :data_out="e";
        4'd6 :data_out=" ";
        4'd7 :data_out="V";
        4'd8 :data_out="e";
        4'd9 :data_out="r";
        4'd10:data_out="i";
        4'd11:data_out="l";
        4'd12:data_out="o";
        4'd13:data_out="g";
        4'd14:data_out=".";
        default:data_out=0;
      endcase
    end
    else if(sel_dat==2'b11)begin
      case(dat_cnt)
        4'd0 :data_out="I";
        4'd1 :data_out=" ";
        4'd2 :data_out="L";
        4'd3 :data_out="i";
        4'd4 :data_out="k";
        4'd5 :data_out="e";
        4'd6 :data_out=" ";
        4'd7 :data_out="1";
        4'd8 :data_out="0";
        4'd9 :data_out="7";
        4'd10:data_out=".";
        default:data_out=0;
      endcase
    end
  end
  reg [3:0] dat_ful;
  always@(*)begin
    if(rst_n==1'b0)begin
      dat_ful=4'd15;
    end
    else if(sel_dat==2'b01)begin
      dat_ful=DAT_FULL_1;
    end
    else if(sel_dat==2'b10)begin
      dat_ful=DAT_FULL_2;
    end
    else if(sel_dat==2'b11)begin
      dat_ful=DAT_FULL_3;
    end
    else begin
      dat_ful=dat_ful;
    end
  end
endmodule

控制电路,控制指令发送:


module control(clk,rst_n,key_entr,key_down,sel_dat,bps_sel
    );
  input clk;
  input rst_n;
  input key_entr,key_down;
  output reg [1:0]sel_dat;
  output reg [1:0]bps_sel;
  //按键抖动判断逻辑
  wire key;  //所有的按键相与的结果,用于按键触发判断
  assign key =key_entr;
  reg[3:0]keyr ; //按键值key的缓冲寄存器
  always@(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      keyr <=4'b1111;
    else
      keyr <={keyr[2:0],key};
  end
  wire key_neg =~keyr[2] &keyr [3];//有按键被按下
  wire key_pos =keyr[2] &~keyr [3];// 有按键被释放
  //定时器计数逻辑,用于对按键的消抖的判断
  reg [19:0] cnt;
  always@(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      cnt <=20'd0;
    else if(key_pos||key_neg)
      cnt <=20'd0;
    else if(cnt <20'd999_999)
      cnt <= cnt +1'b1;
    else
      cnt <=20'd0;
  end
  reg [1:0] key_value_0;
  reg [1:0] key_value_1;
  //定时采取按键值
  always@(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
    begin
      key_value_0 <=2'b11;
      key_value_1 <=2'b11;
    end
    else begin
      if(cnt ==20'd999_999)
        key_value_0 <={key_entr,key_down};
      else 
        key_value_1 <=key_value_0;
    end 
  end
  wire [1:0] key_press=key_value_1 & ~key_value_0;
  //切换指令控制
  always@(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      sel_dat<=1'b0;
    else if(key_press[1]==1'b1)begin
      if(sel_dat==2'b11)begin
        sel_dat<=2'b01;
      end
      else begin
        sel_dat<=sel_dat+1'b1;
      end
    end
    else begin
      sel_dat<=sel_dat;
    end
  end
  //切换波特率控制
  always@(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      bps_sel<=2'b01;
    else if(key_press[0]==1'b1)begin
      if(bps_sel==2'b11)begin
        bps_sel<=2'b00;
      end
      else begin
        bps_sel<=bps_sel+1'b1;
      end
    end
    else begin
      bps_sel<=bps_sel;
    end
  end
endmodule

顶层文件


module top(ext_clk_25m,ext_rst_n,uart_tx,key_down,key_entr,led
    );
  input       ext_clk_25m ;//系统时钟
  input       ext_rst_n   ;//复位信号
  input       key_down    ;//波特率选择
  input       key_entr    ;//指令选择
  output[1:0] led         ;//指示bps
  output      uart_tx     ;//串口发送
  assign led[1:0]=bps_sel[1:0];
  wire        bps_clk     ;
  wire        txd_finish  ;
  wire        uart_state  ;
  wire  [7:0] data_out    ;
  wire        dat_buf_en  ;
  wire  [1:0] bps_sel     ;
  wire  [1:0] sel_dat     ;
  //数据缓冲区
  txd_data_buf u_buf(
  .clk(bps_clk),
  .rst_n(ext_rst_n),
  .txd_finish(txd_finish),
  .sel_dat(sel_dat),
  .dat_buf_en(dat_buf_en),
  .data_out(data_out)
    );
  //波特率选择模块
  uart_div u_div(
   .clk(ext_clk_25m),
   .rst_n(ext_rst_n),
   .bps_sel(bps_sel),
   .bps_clk(bps_clk)
    );
  //串口发送模块
  uart_txd u_txd(
  .clk(bps_clk),
  .rst_n(ext_rst_n),
  .Date_byte(data_out),
  .send_en(dat_buf_en),
  .txd(uart_tx),
  .txd_finish(txd_finish),
  .uart_state(uart_state)
    );
  //按键控制模块
  control u_con(
  .clk(ext_clk_25m),
  .rst_n(ext_rst_n),
  .key_entr(key_entr),
  .key_down(key_down),
  .sel_dat(sel_dat),
  .bps_sel(bps_sel)
    );
endmodule

串口助手验证:


image.png

目录
相关文章
|
8天前
|
算法 数据安全/隐私保护 异构计算
基于FPGA的16QAM调制+软解调系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR
本项目基于FPGA实现了16QAM基带通信系统,包括调制、信道仿真、解调及误码率统计模块。通过Vivado2019.2仿真,设置不同SNR(如8dB、12dB),验证了软解调相较于传统16QAM系统的优越性,误码率显著降低。系统采用Verilog语言编写,详细介绍了16QAM软解调的原理及实现步骤,适用于高性能数据传输场景。
107 69
|
12天前
|
移动开发 算法 数据安全/隐私保护
基于FPGA的QPSK调制+软解调系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR
本文介绍了基于FPGA的QPSK调制解调系统,通过Vivado 2019.2进行仿真,展示了在不同信噪比(SNR=1dB, 5dB, 10dB)下的仿真效果。与普通QPSK系统相比,该系统的软解调技术显著降低了误码率。文章还详细阐述了QPSK调制的基本原理、信号采样、判决、解调及软解调的实现过程,并提供了Verilog核心程序代码。
49 26
|
18天前
|
算法 异构计算
基于FPGA的4ASK调制解调系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR
本文介绍了基于FPGA的4-ASK调制解调系统的算法仿真效果、理论基础及Verilog核心程序。仿真在Vivado2019.2环境下进行,分别测试了SNR为20dB、15dB、10dB时的性能。理论部分概述了4-ASK的工作原理,包括调制、解调过程及其数学模型。Verilog代码实现了4-ASK调制器、加性高斯白噪声(AWGN)信道模拟、解调器及误码率计算模块。
41 8
|
25天前
|
算法 物联网 异构计算
基于FPGA的4FSK调制解调系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR
本文介绍了基于FPGA的4FSK调制解调系统的Verilog实现,包括高斯信道模块和误码率统计模块,支持不同SNR设置。系统在Vivado 2019.2上开发,展示了在不同SNR条件下的仿真结果。4FSK调制通过将输入数据转换为四个不同频率的信号来提高频带利用率和抗干扰能力,适用于无线通信和数据传输领域。文中还提供了核心Verilog代码,详细描述了调制、加噪声、解调及误码率计算的过程。
47 11
|
1月前
|
算法 数据安全/隐私保护 异构计算
基于FPGA的1024QAM基带通信系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR
本文介绍了基于FPGA的1024QAM调制解调系统的仿真与实现。通过Vivado 2019.2进行仿真,分别在SNR=40dB和35dB下验证了算法效果,并将数据导入Matlab生成星座图。1024QAM调制将10比特映射到复数平面上的1024个星座点之一,适用于高数据传输速率的应用。系统包含数据接口、串并转换、星座映射、调制器、解调器等模块。Verilog核心程序实现了调制、加噪声信道和解调过程,并统计误码率。
44 1
|
2月前
|
算法 数据安全/隐私保护 异构计算
基于FPGA的64QAM基带通信系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR
本文介绍了基于FPGA的64QAM调制解调通信系统的设计与实现,包括信号生成、调制、解调和误码率测试。系统在Vivado 2019.2中进行了仿真,通过设置不同SNR值(15、20、25)验证了系统的性能,并展示了相应的星座图。核心程序使用Verilog语言编写,加入了信道噪声模块和误码率统计功能,提升了仿真效率。
52 4
|
2月前
|
监控 算法 数据安全/隐私保护
基于三帧差算法的运动目标检测系统FPGA实现,包含testbench和MATLAB辅助验证程序
本项目展示了基于FPGA与MATLAB实现的三帧差算法运动目标检测。使用Vivado 2019.2和MATLAB 2022a开发环境,通过对比连续三帧图像的像素值变化,有效识别运动区域。项目包括完整无水印的运行效果预览、详细中文注释的代码及操作步骤视频,适合学习和研究。
|
2月前
|
存储 算法 数据处理
基于FPGA的8PSK调制解调系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR
本系统在原有的8PSK调制解调基础上,新增了高斯信道与误码率统计模块,验证了不同SNR条件下的8PSK性能。VIVADO2019.2仿真结果显示,在SNR分别为30dB、15dB和10dB时,系统表现出不同的误码率和星座图分布。8PSK作为一种高效的相位调制技术,广泛应用于无线通信中。FPGA凭借其高度灵活性和并行处理能力,成为实现此类复杂算法的理想平台。系统RTL结构展示了各模块间的连接与协同工作。
58 16
|
7月前
|
编解码 算法 异构计算
基于FPGA的NC图像质量评估verilog实现,包含testbench和MATLAB辅助验证程序
在Vivado 2019.2和Matlab 2022a中测试的图像质量评估算法展示了效果。该算法基于NC指标,衡量图像与原始图像的相似度,关注分辨率、色彩深度和失真。提供的Verilog代码段用于读取并比较两个BMP文件,计算NC值。
|
2月前
|
算法 数据安全/隐私保护 异构计算
基于FPGA的16QAM基带通信系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR
本项目基于FPGA实现16QAM调制解调通信系统,使用Verilog语言编写,包括信道模块、误码率统计模块。通过设置不同SNR值(如8dB、12dB、16dB),仿真测试系统的误码性能。项目提供了完整的RTL结构图及操作视频,便于理解和操作。核心程序实现了信号的生成、调制、信道传输、解调及误码统计等功能。
50 3

热门文章

最新文章