当前位置:   article > 正文

『FPGA通信接口』串行通信接口-UART_fpga 模拟串口需要多少单元、

fpga 模拟串口需要多少单元、

在这里插入图片描述

一、基本概念

1、并行通信:指数据的多个位用多条数据线同时传输,同时具备传输速度快的优点和占用引脚资源多的缺点。
2、串行通信:将数据分成一位一位的形式在一条传输线上逐个传输,优缺点与并行通信相反。
3、同步通信:带时钟同步信号的数据传输,发送方和接收方在同一时钟的控制下,同步传输数据。
4、异步通信:不带时钟同步信号的数据传输。发送方与接收方使用各自的时钟控制数据的发送和接收过程。
5、串行通信的传输方向
单工:数据只能沿一个方向传输
半双工:数据传输可以沿两个方向,但同一时刻只能传输一个方向
双工:数据可以同时进行双向传输
6、常见的串行通信接口

通信标准引脚说明通信方式通信方向
UARTTXD:发送 RXD:接收 GND:公共地异步双工
单总线DQ: 发送/接收端异步半双工
SPISCK:同步时钟 MISO:主机输入,从机输出 MOSI:主机输出,从机输入同步双工
IICSCL:同步时钟 SDA:数据输入\输入端同步半双工

二、串口特性

1、串口基本概念:通用异步收发传输器(Universal Asynchronous Receiver/Transmitter,通常称作UART) 是一种串行异步收发协议。
2、通信速率:串口通信的速率用波特率表示,波特率是指从一设备发到另一设备的波特率,即每秒钟可以通信的数据比特个数。UART波特率受发送和接收线对距离(线长度)的影响。常用的波特率有9600、19200、38400、57600、115200等。
3、适用场景:一般适用于 传输速度要求不高,距离短的场合,实践中用于芯片调试信息的输出。
4、电平标准:TTL、RS-232、RS-485是适用串口的三种电平标准(电信号),常见接口形式如4针杜邦线(TTL),DB9(RS232)。PL2303、CP2102芯片是USB 转 TTL串口 的芯片,用USB来扩展串口(TTL电平)。MAX232芯片是TTL电平与RS232电平的专用双向转换芯片,可以TTL转RS-232,也可以RS-232转TTL。uart是一种异步通信协议。而rs232只是物理层的电气接口要求。uart可以使用rs232物理层来进行通信。而rs232作为物理层也可以用其余不同于uart的协议来做通信。
为什么串口有这么多不同的电气标准?有了UART这个东西之后是不是就可以直接在设备之间用了呢?如果是在一块板子上这里没有太大问题。现在大多数芯片的输出电压都是TTL的,所以电平兼容,而且一块板子上干扰又不会太高。但要是用在设备与设备之间通信,那可能就会有问题了。比如说主控电脑和电机驱动器之间。这种情况下TTL的抗噪能力就比较单薄了。比较简单的做法就是增加逻辑1和逻辑0之间的电压差,比如像RS232这种接口中定义的电平格式。还有用差分信号的方式,比如RS485。没错,RS232只是一种接口,定义了接口的外观、各个引脚的功能、信号0和1对应的电压等。

三、串口通信协议

UART串口通信需要两根信号线来实现,一根用于串口发送,另外一根负责串口接收,要求通信双方配置相同的通信参数。UART在发送或接收过程中的一帧数据由 4 部分组成,起始位、数据位、奇偶校验位和停止位。
●空闲位:UART协议规定,当总线处于空闲状态时信号线的状态为‘1’即高电平.
●起始位:开始进行数据传输时发送方要先发出一个低电平’0’来表示传输字符的开始。因为空闲位一直是高电平所以开始第一次通讯时先发送一个明显区别于空闲状态的信号即为低电平。
●数据位:起始位之后就是要传输的数据,数据可以是5,6,7,8,9位,构成一个字符,一般都是8位。先发送最低位最后发送最高位。
●奇偶校验位:数据位传送完成后,要进行奇偶校验,校验位其实是调整个数,串口校验分几种方式:
1.无校验
2.奇校验:如果数据位中’1’的数目是偶数,则校验位为’1’,如果’1’的数目是奇数,校验位为’0’。
3.偶校验:如果数据为中’1’的数目是偶数,则校验位为’0’,如果为奇数,校验位为’1’。
●停止位:数据结束标志,可以是1位,1.5位,2位的高电平。

四、逻辑开发

4.1verilog代码

Top模块:

module uart_top(
  input   clk,//50M
  input   reset_n,
  input   uart_rx,
  output  uart_tx,
  output  led
  );

//确定baud周期
parameter Baud = 3'd0;
reg [15:0]bclk_cnt;       //一个串口位占据的时钟个数
always@(posedge clk or negedge reset_n)
if(~reset_n)
  bclk_cnt <= 16'd5207;//1s/9600/20ns=5208   9600Bau
else begin 
  case(Baud)
      0:bclk_cnt <= 16'd5207;  //9600Bau
      1:bclk_cnt <= 16'd2603; //19200Bau
      2:bclk_cnt <= 16'd1301;  //38400Bau
      3:bclk_cnt <= 16'd867;   //57600Bau
      4:bclk_cnt <= 16'd433;   //115200Bau
      default:bclk_cnt <= 16'd5207;
  endcase
end
//获取数据接收起始位
reg uart_rx_reg1;    
reg uart_rx_reg2;    
reg uart_rx_reg3;    
wire uart_rx_nedge;
always@(posedge clk or negedge reset_n)
  if(~reset_n)begin
      uart_rx_reg1 <= 1'b0;
      uart_rx_reg2 <= 1'b0;
      uart_rx_reg3 <= 1'b0;   
  end
  else begin
      uart_rx_reg1 <= uart_rx;
      uart_rx_reg2 <= uart_rx_reg1;
      uart_rx_reg3 <= uart_rx_reg2;   
  end
//下降沿检测
assign uart_rx_nedge = !uart_rx_reg2 & uart_rx_reg3;
//#####################################3.state machine
reg    [2:0] state;
parameter IDLE = 3'd0;
parameter START = 3'd1;
parameter DATA = 3'd2;
parameter STOP = 3'd3;
always@(posedge clk or negedge reset_n)
  if(!reset_n)
      begin
          state<=IDLE;
      end
  else case(state)
  IDLE: begin
      state<=uart_rx_nedge? START:IDLE;
  end
  START:begin
      if(start_cnt==bclk_cnt)begin
          if(begin_flag<2)
              state<=DATA;
          else
              state<=IDLE;
      end else
           state<=START;
  end
  DATA:begin
      state <=(data_cnt==4'd8&&start_cnt==bclk_cnt)? STOP :DATA;
  end
  STOP:begin
      state<=IDLE;
  end
endcase
//baud周期计数,计满为一个波特周期(时钟分频)
reg [15:0]start_cnt;
always @(posedge clk or negedge reset_n) begin
  if(!reset_n)
  start_cnt<=0;
  else if(start_cnt==bclk_cnt)begin
      start_cnt<=0;
  end else if(state==START||state==DATA)begin
      start_cnt<=start_cnt+1;
  end
end
//起始信号干扰排除
reg [2:0]begin_flag;
always @(posedge clk or negedge reset_n) begin
  if(!reset_n)
      begin_flag<=3'd0;
  else if(state==START)begin
      if(start_cnt==16'd1950||start_cnt==16'd2275||start_cnt==16'd2600||start_cnt==16'd2925||
      start_cnt==16'd3250||start_cnt==16'd3575)begin
          begin_flag<=begin_flag+uart_rx_reg2;
      end else
          begin_flag<=begin_flag;
  end else
      begin_flag<=0;
end
//接收数据排序
reg [3:0]data_cnt;
always @(posedge clk or negedge reset_n)begin
  if(!reset_n)
      data_cnt<=0;
  else if(state==DATA)begin
      if(start_cnt==16'd1)begin
          data_cnt<=data_cnt+1;
      end else 
          data_cnt<=data_cnt;
  end 
  else 
      data_cnt<=0; 
end
//寄存接收到的数据
reg [7:0]cur_data;
always @(posedge clk or negedge reset_n) begin
  if(!reset_n)begin
  cur_data[0]<=3'd0;
  cur_data[1]<=3'd0;
  cur_data[2]<=3'd0;
  cur_data[3]<=3'd0;
  cur_data[4]<=3'd0;
  cur_data[5]<=3'd0;
  cur_data[6]<=3'd0;
  cur_data[7]<=3'd0;
  end
  else if(state==DATA)begin 
      if(start_cnt==bclk_cnt/2)begin
          case(data_cnt)
              4'd1 : cur_data[0] <= uart_rx_reg2;   //寄存数据位最低位
              4'd2 : cur_data[1] <= uart_rx_reg2;
              4'd3 : cur_data[2] <= uart_rx_reg2;
              4'd4 : cur_data[3] <= uart_rx_reg2;
              4'd5 : cur_data[4] <= uart_rx_reg2;
              4'd6 : cur_data[5] <= uart_rx_reg2;
              4'd7 : cur_data[6] <= uart_rx_reg2;
              4'd8 : cur_data[7] <= uart_rx_reg2;
              default:;  
          endcase
      end
  end
end
//接收结束,发出发送指令(数据同步信号,50M一个周期)
wire rx_bit_done;
assign rx_bit_done=(state==STOP)? 1'b1:1'b0;
uart_my_tx u1_uart_tx(
      .clk(clk),
      .reset_n(reset_n),
      .data_byte(cur_data),
      .send_en(rx_bit_done),
      .baud_set(Baud),
      .uart_tx(uart_tx),
      .tx_flag(led)
  );  
endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154

子模块:

module uart_my_tx(
  input clk,
  input reset_n,
  input [7:0]data_byte,
  input send_en,
  input baud_set,
  output reg uart_tx,
  output  tx_flag
  );
assign tx_flag=start_flag;//busy信号相当于
//寄存发送数据
reg [7:0]data_byte_reg;//data_byte寄存后数据
always@(posedge clk or negedge reset_n)
  if(!reset_n)
      data_byte_reg <= 8'd0;
  else if(send_en)
      data_byte_reg <= data_byte;
  else
      data_byte_reg <= data_byte_reg;
//确定baud周期
reg [15:0]bclk_cnt;       //一个串口位占据的时钟个数
always@(posedge clk or negedge reset_n)
if(~reset_n)
  bclk_cnt <= 16'd5207;//1s/9600/20ns=5208   9600Bau
else begin 
  case(baud_set)
      0:bclk_cnt <= 16'd5207;  //9600Bau
      1:bclk_cnt <= 16'd2603; //19200Bau
      2:bclk_cnt <= 16'd1301;  //38400Bau
      3:bclk_cnt <= 16'd867;   //57600Bau
      4:bclk_cnt <= 16'd433;   //115200Bau
      default:bclk_cnt <= 16'd5207;
  endcase
end
//do transfer
reg start_flag;//指示写过程正在进行,可以据此产生busy信号
reg tx_done;//指示写结束
always@(posedge clk or negedge reset_n)
  if(~reset_n)
      start_flag<=0;
    
  else if(send_en)
      start_flag<=1;
  else if(pluse_cnt == 4'd10)
      start_flag<=0;
  else
      start_flag<=start_flag;
  
//波特计数  计满为一个波特周期
reg [15:0]baud_cnt;
always @(posedge clk or negedge reset_n) begin
  if(!reset_n)
      baud_cnt<=0;
  else if(start_flag)begin
      if(baud_cnt==bclk_cnt)
          baud_cnt<=0;
      else
          baud_cnt<=baud_cnt+1;
  end 
  else
      baud_cnt<=0;
end
//产生发送数据脉冲
reg tran_pluse;
always @(posedge clk or negedge reset_n) begin
  if(!reset_n)
      tran_pluse<=0;
  else if(baud_cnt==16'd1)begin
      tran_pluse<=1;
  end 
  else
      tran_pluse<=0;
end
//脉冲计数使数据顺序发送
reg [3:0]pluse_cnt;
always @(posedge clk or negedge reset_n) begin
  if(!reset_n)
      pluse_cnt<=0;
  else if(pluse_cnt==4'd10)begin
      pluse_cnt<=0;
  end else if(tran_pluse)
      pluse_cnt<=pluse_cnt+1;
  else   
      pluse_cnt<=pluse_cnt;
end
//产生tx_done数据发送完毕指示
  always@(posedge clk or negedge reset_n)
  if(!reset_n)    
      tx_done <= 1'b0;
  else if(pluse_cnt == 4'd10)
      tx_done <= 1'b1;
  else
      tx_done <= 1'b0;
localparam START_BIT = 1'b0;
localparam STOP_BIT = 1'b1; 
always@(posedge clk or negedge reset_n)
  if(!reset_n)
      uart_tx <= 1'b1;
  else begin
      case(pluse_cnt)
          0:uart_tx <= 1'b1;
          1:uart_tx <= START_BIT;
          2:uart_tx <= data_byte_reg[0];
          3:uart_tx <= data_byte_reg[1];
          4:uart_tx <= data_byte_reg[2];
          5:uart_tx <= data_byte_reg[3];
          6:uart_tx <= data_byte_reg[4];
          7:uart_tx <= data_byte_reg[5];
          8:uart_tx <= data_byte_reg[6];
          9:uart_tx <= data_byte_reg[7];
          // 10:uart_tx <= STOP_BIT;
          default:uart_tx <= 1'b1;
      endcase
  end 
reg [3:0]tr_cnt;
always @(posedge clk or negedge reset_n) begin
  if(!reset_n)
      tr_cnt <= 0;
  else if(tx_done)
      tr_cnt<=tr_cnt+1;
  else
      tr_cnt<=tr_cnt;
end
ila_0 your_instance_name (
  .clk(clk), // input wire clk
  .probe0({tran_pluse,tr_cnt,pluse_cnt,start_flag}), // input wire [0:0]  probe0  
  .probe1(data_byte_reg), // input wire [7:0]  probe1 
  .probe2(tx_done), // input wire [0:0]  probe2 
  .probe3(uart_tx) // input wire [0:0]  probe3
);
endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131

4.2代码解读

1.描述
●该串口驱动的基本配置为:9600Baud、1bit停止位,8Bit数据位,无奇偶校验位;
●该驱动运行的效果为:接受一个8bit数据,便产生一个send_en指示脉冲,指示发送模块将这8bit数据发送出去。在接收完成之后继续接收,在发送完成之后继续等待发送。
2.步骤解读(从Top开始)
①结合系统时钟以及驱动配置,使用信号bclk_cnt计算出9600Baud情况下,一个“Baud”的时钟个数,并给出易于修改波特率的方法。
②通过经典代码,捕获uart_rx管脚的下降沿(uart_rx_nedge)便于捕获起始信号。
③通过begin_flag信号对起始信号的干扰做一个判断和甄别,在状态机中,将据此信号决定状态机的走向。
④通过data_cnt信号与接收数据对齐,从而便于在start_cnt==bclk_cnt/2(某位数据传输的正中间最稳定的时候)寄存串口数据。
⑤start_cnt信号计满为一个波特周期,便于数据寄存。
⑥最后在top中生成rx_bit_done脉冲信号,用于指示将接收到的数据传送回去。
⑦步骤1-5在top中完成对数据的接收,并产生的指示发送信号,进入到子模块中,收到send_en后,将接收到的8bit数据寄存到data_byte_reg信号。
⑧根据top指示,选择设置的Baud(bclk_cnt信号)用于发送数据。
⑨根据send_en和发送过程未结束产生tx_flag,相当于busy信号,该信号通过top连接至led。
⑩根据子模块中计满波特周期信号baud_cnt结合tran_pluse脉冲信号,生成数据同步信号pluse_cnt,便于数据的发送。
通过给uart_tx赋值完成数据发送,并产生tx_done信号表示一次8bit发送结束。
3.注意事项
①波特率指每秒传输的数据位数,系统使用不同的用户时钟,采用不同的波特率可能会造成,传输每一位数据所需的时钟个数成为小数,但较小偏差的波特率在串口通信是被允许的。
②当数据寄存完成后,可以提前一点结束接收过程,也就提前开始了下一个数据的监听,为下一帧数据的起始位预留时间。
③采样时刻选取在每一位数据(每一个波特周期)的中点,确保数据稳定。
⑤为了防止在传输的过程中数据出现变化,需要将数据做寄存处理。
⑥通信双方的波特率、停止位、数据位、校验位的设置必须一致才能正确通信。
⑦若时钟频率为50Mhz,得到一位数据需要的时钟数,基本的分频计数单元(每一位数据或者起始位停止位)1000000000(1s)/115200=8680ns 。每一位的时间是8680ns 用计数器计数8680/20=434,则计数434-1次即可。

五、调试效果

在这里插入图片描述
如图所示,是与Qt写的上位机进行通信实验的结果,可以看到配置一致,串口工作,将发送的数据全部返回。软件源代码链接见文末。

六 传送门

END

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/492724
推荐阅读
相关标签