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

目录
相关文章
|
1月前
|
数据格式 异构计算
|
1月前
|
人工智能 物联网 5G
|
9月前
|
算法 芯片 异构计算
通过FPGA实现基于RS232串口的指令发送并控制显示器中目标位置
通过FPGA实现基于RS232串口的指令发送并控制显示器中目标位置
|
传感器 5G 数据处理
ZYNQ(FPGA)与DSP之间GPIO通信
基于 TI KeyStone 架构 C6000 系列 TMS320C6657双核C66x 定点/浮点 DSP以及 Xilinx Zynq-7000 系列 XC7Z035/045 SoC 处理器设计的高端异构多核评估板,由核心板与评估底板组成。 DSP采用 TMS320C6657 双核C66x 定点/浮点,每核主频1GHz/1.25GHz。 Xilinx Zynq SoC处理器采用的XC7Z035/045集成PL端Kintex-7架构+PS 端双核ARM Cortex-A9 ,28nm可编程逻辑资源。
ZYNQ(FPGA)与DSP之间GPIO通信
|
11月前
|
算法 异构计算
m基于FPGA的256QAM调制信号产生模块verilog实现,包含testbench
m基于FPGA的256QAM调制信号产生模块verilog实现,包含testbench
270 0
|
11月前
|
算法 异构计算
m基于FPGA的1024QAM调制信号产生模块verilog实现,包含testbench
m基于FPGA的1024QAM调制信号产生模块verilog实现,包含testbench
343 0
|
算法 数据安全/隐私保护 芯片
m基于FPGA的通信数据帧加扰解扰verilog实现,包含testbench
m基于FPGA的通信数据帧加扰解扰verilog实现,包含testbench
274 0
|
算法 异构计算
基于FPGA的直接序列扩频通信verilog设计,包括汉明编译码,扩频解扩,同步模块以及testbench
基于FPGA的直接序列扩频通信verilog设计,包括汉明编译码,扩频解扩,同步模块以及testbench
280 0
基于FPGA的直接序列扩频通信verilog设计,包括汉明编译码,扩频解扩,同步模块以及testbench
|
1月前
|
机器学习/深度学习 算法 异构计算
m基于FPGA的多通道FIR滤波器verilog实现,包含testbench测试文件
本文介绍了使用VIVADO 2019.2仿真的多通道FIR滤波器设计。展示了系统RTL结构图,并简述了FIR滤波器的基本理论,包括单通道和多通道的概念、常见结构及设计方法,如窗函数法、频率采样法、优化算法和机器学习方法。此外,还提供了Verilog核心程序代码,用于实现4通道滤波器模块,包含时钟、复位信号及输入输出接口的定义。
72 7
|
1月前
|
算法 异构计算
m基于FPGA的电子钟verilog实现,可设置闹钟,包含testbench测试文件
该文介绍了基于FPGA的电子钟设计,利用Vivado2019.2平台进行开发并展示测试结果。电子钟设计采用Verilog硬件描述语言,核心包括振荡器、分频器和计数器。时间显示为2个十进制格式,闹钟功能通过存储器和比较器实现,当当前时间等于设定时间时触发。文中给出了Verilog核心程序示例,展示了时钟信号、设置信号及输出的交互。
108 2