Xilinx FPGA:vivado对串口的接收端和发送端增加校验位

一、实验原理:

奇校验原理:

前8位是数据位,第九位是校验位,数据位中的1是奇数个,则校验位是0,保证数据位和校验位异或的结果是1;数据位中的1是偶数个,则校验位为1,保证数据位和校验位异或的结果是1。

1bit2bit3bit4bit5bit6bit7bit8bit9bit
110011100
111011101

偶校验原理:

前8位是数据位,第九位是校验位,数据位中1的个数是奇数个,则校验位是1,保证数据位和校验位异或的结果是0;数据位中1的个数是偶数个,则校验位为0,保证数据位和校验位异或的结果是0。

1bit2bit3bit4bit5bit6bit7bit8bit9bit
110011101
111011100

二、程序设计

TOP层:

`timescale 1ns / 1ps
module TOP(
   input              sys_clk  ,
   input              rst_n    ,
   input              rx_data  ,
   output             tx_data  
    );
    
    wire[7:0]        uart_data ;
    wire             rx_done   ;
    uart_rx uart_rx_u1(
                   .   sys_clk   ( sys_clk  ) ,
                   .   rst_n     ( rst_n    ) ,
                   .   rx_data   ( rx_data  ) ,  //输入串行数据
                   .   uart_data ( uart_data) , // 输出并行数据
                   .   rx_done   ( rx_done  )  //数据传输完成结束信号      
    );
    

    wire        tx_done     ;
    send send_u1(
                   .   sys_clk    (sys_clk  )   ,
                   .   rst_n      (rst_n    )   ,
                   .   uart_data  (uart_data)   ,
                   .   rx_done    (rx_done  )   ,
                   .   tx_data    (tx_data  )   , 
                   .   tx_done    (tx_done  )
    );
endmodule

接收端:一般由串口工具增加校验位,串口工具根据发送的数据和校验方式选择“0”或者“1”,在仿真中,我们只能选择固定的数字“0”或者“1”进行校验,所以串口中无论发送什么数字,都有返回值,但是在仿真中,只能返回部分符合筛选机制的值。

`timescale 1ns / 1ps
/串口接收端    串行转并行
module uart_rx(
    input                  sys_clk ,
    input                  rst_n    ,
    input                  rx_data   ,  //输入串行数据
    output    reg[7:0]     uart_data , // 输出并行数据
    output    reg          rx_done     //数据传输完成结束信号  
         
    );
    
    parameter         SYSCLK =   50_000_000  ;
    parameter         Baud   =   115200      ;
    parameter         COUNT  =   SYSCLK/Baud ;//434   传输1比特所需要的时钟周期
    parameter         MID    =   COUNT/2     ;
    
    parameter         MODE_CHECK = 0 ;
    
    ///产生开始信号(检测下降沿-----二级寄存)
    reg           rx_reg1     ;
    reg           rx_reg2     ;
    
    wire          start_flag    ;  开始信号
    reg           rx_flag       ;
    reg    [4:0]   cnt_bit      ;///0~10
    reg    [9:0]   cnt          ;///434
    reg    [8:0]   data_reg     ;
    reg            check        ;

    
    always@(posedge sys_clk)
          if(!rst_n)begin
          rx_reg1 <= 1 ;  //处于空闲位  数据线上无数据
          rx_reg2 <= 1 ;  //处于空闲位  数据线上无数据
          end
          else
              begin
                  rx_reg1 <= rx_data  ;
                  rx_reg2 <= rx_reg1  ;
              end
     assign start_flag = ~rx_reg1 & rx_reg2  ;  
     //rx_flag
     always@(posedge sys_clk )
           if(!rst_n)
              rx_flag <= 0 ;
           else if (start_flag)
              rx_flag <= 1 ;
           else if ( cnt_bit == 10 && cnt == MID -1 )
              rx_flag <= 0 ;
           else
              rx_flag <= rx_flag ;
   cnt 434  
     always@(posedge sys_clk )
           if(!rst_n)
              cnt <= 0;
           else if ( rx_flag == 1 )begin
                if ( cnt == COUNT -1) ///一定要减一,如果不减一,实际会计到435次,反算回去波特率就不是115200了
                    cnt <= 0;
                else
                    cnt <= cnt +1 ;
           end
           else
               cnt <= 0 ;
   
   /计数器
      always@(posedge sys_clk )
           if(!rst_n)
              cnt_bit <= 0 ;
           else if ( rx_flag )begin
                if ( cnt == COUNT -1)begin
                    if(cnt_bit == 10)
                    cnt_bit <= 0 ;
                    else
                    cnt_bit <= cnt_bit +1 ;
                    end
                else
                cnt_bit <= cnt_bit     ;
                end
           else
           cnt_bit <= 0 ;
   
  
           
    [7:0]uart_data
    /*
    cnt_bit = 0 起始位
    cnt_bit = 1-8 数据位
    cnt_bit = 9 停止位
    但是  [7:0]uart_data   没有uart[8]
    所以
    if ( cnt == MID -1 && cnt_bit > 0 && cnt_bit < 9 )
         uart_data[cnt_bit -1] <= rx_data ;
    */
//    always@(posedge sys_clk )
//          if(!rst_n)
//          uart_data <= 0 ;
//          else if ( rx_flag )begin
//                if ( cnt == MID -1 && cnt_bit > 0 && cnt_bit < 9 )
//                uart_data[cnt_bit -1] <= rx_data ; //这里uart_data是不断随着cnt_bit变化的,只有在第九位的时候才有正确的最终值
//                else
//                uart_data <= uart_data ;
//          end
//          else
//          uart_data <= 0 ;
    因为这里要让uart_data只在rx_done的时候有值,所以定义一个中间寄存器data_reg
     always@(posedge sys_clk )
          if(!rst_n)
          data_reg <= 0 ;
          else if ( rx_flag )begin
                if ( cnt == MID -1 && cnt_bit > 0 && cnt_bit < 10 )
                data_reg[cnt_bit -1] <= rx_data ; //这里uart_data是不断随着cnt_bit变化的,只有在第九位的时候才有正确的最终值
                else
                data_reg <= data_reg ;
          end
          else
          data_reg <= 0 ;
     
//  其他赋值方法1
//      always@(posedge sys_clk )
//          if(!rst_n)
//             uart_data <= 0;
//          else if (cnt == MID -1)begin
//             case(cnt_bit)
//             0:  uart_data  <= 0  ;
//             1:  uart_data [0] <= rx_data  ;
//             2:  uart_data [1] <= rx_data  ;
//             3:  uart_data [2] <= rx_data  ;
//             4:  uart_data [3] <= rx_data  ;
//             5:  uart_data [4] <= rx_data  ;
//             6:  uart_data [5] <= rx_data  ;
//             7:  uart_data [6] <= rx_data  ;
//             8:  uart_data [7] <= rx_data  ;
//             9:  uart_data  <= uart_data  ;  ///停止位的时候数据传完,保持
//             endcase
//             end
//           else
//             uart_data  <= uart_data  ;
   
//   其他赋值方法2
//    always@(posedge sys_clk )
//          if(!rst_n)
//          uart_data <= 0 ;
//          else if (cnt == MID -1 && cnt_bit > 0 && cnt_bit < 9)
//               uart_data <= {rx_data,uart_data[7:1]} ;
//          else
//               uart_data <= uart_data ;

    check
    always@(posedge sys_clk)
          if(!rst_n)
              check <= 0 ;
          else if (rx_flag )begin
               if ( cnt_bit == 10 )
                   check <= ^data_reg  ;
               else
                   check <= check ;
          end
          else
               check <= 0 ;

          
   给uart_data赋值
     always@(posedge sys_clk )
          if(!rst_n)     
             uart_data <= 0 ;   
          else if (rx_flag)begin
               if (cnt_bit == 10 && cnt == 3 && check == MODE_CHECK)
               uart_data <= data_reg ;
               else
               uart_data <= uart_data ;
          end
          else
          uart_data <= uart_data ; 可以保持到下一个数据到来
//              uart_data <= 0 ;  只保持在rx_done处于高电平的时候
  /rx_done
     always@(posedge sys_clk )
          if(!rst_n)           
            rx_done <= 0 ;
          else if (rx_flag)begin
               if ( cnt_bit == 10 && cnt == MID/2 -1)
                   rx_done <= 1 ;
               else
                   rx_done <= 0 ;       
          end
          else
            rx_done <= 0 ;  



      
endmodule

发送端:

`timescale 1ns / 1ps
/*
校验位:
奇校验:去保证[数据位+校验位]中1的个数为奇数    
偶校验:去保证[数据位+校验位]中1的个数为偶数
eg:偶校验
1100_1010     0
0  0_ 1 1
  0    0
     0        0
         0              
1010_1110     1
1  1_0 1
  0   1
    1         1
          0

偶校验:^[数据位+校验位] = 0
奇校验:^[数据位+校验位] = 1
-------------------------------
接收端加校验位:^[数据位+校验位] = 0,则输出并行数据
发送端加校验位:把校验位放到数据线上发送,保证[数据位+校验位]中1的个数为偶数
*/

module send(
     input               sys_clk   ,
     input               rst_n     ,
     input    [7:0]      uart_data ,
     input               rx_done   ,
     output   reg   tx_data   , 
     output   reg        tx_done
    );
    parameter         SYSCLK =   50_000_000  ;
    parameter         Baud   =   115200      ;
    parameter         COUNT  =   SYSCLK/Baud ;//434   传输1比特所需要的时钟周期
    parameter         MID    =   COUNT/2     ;

    wire                 tx_start ;
    reg                  tx_flag   ;
 
    reg                  tx_reg1   ;
    reg                  tx_reg2   ;
    
    reg[4:0]             cnt_bit   ;
    reg[10:0]            cnt       ;



tx_start
    always@(posedge sys_clk)
          if(!rst_n)begin
             tx_reg1 <= 0 ;
             tx_reg2 <= 0 ;
           end
           else begin
              tx_reg1 <= rx_done  ;
              tx_reg2 <= tx_reg1  ;
           end
 
   assign  tx_start = tx_reg1 & ~tx_reg2 ;
 
 ///tx_flag
   always@(posedge sys_clk)
         if(!rst_n)
            tx_flag <= 0 ;
         else if ( tx_start)
            tx_flag <= 1 ;
         else if ( cnt == MID -1 && cnt_bit == 10)
            tx_flag <= 0 ;
         else
            tx_flag <= tx_flag ;
 ///计时器
      cnt 434  
     always@(posedge sys_clk )
           if(!rst_n)
              cnt <= 0;
           else if ( tx_flag == 1 )begin
                if ( cnt == COUNT -1) ///一定要减一,如果不减一,实际会计到435次,反算回去波特率就不是115200了
                    cnt <= 0;
                else
                    cnt <= cnt +1 ;
           end
           else
               cnt <= 0 ;
  
     /计数器
      always@(posedge sys_clk )
           if(!rst_n)
              cnt_bit <= 0 ;
           else if ( tx_flag )begin
                if ( cnt == COUNT -1)begin
                    if(cnt_bit == 10)///0123456789 10
                    cnt_bit <= 0 ;
                    else
                    cnt_bit <= cnt_bit +1 ;
                    end
                else
                cnt_bit <= cnt_bit     ;
                end
           else
           cnt_bit <= 0 ;
           
  parameter             MODE_CHECK = 0 ;
 
赋值
     always@(posedge sys_clk )
          if(!rst_n)
          tx_data <= 1 ;   //表示没有数据
          else if ( tx_flag )begin
                if (   cnt_bit > 0 && cnt_bit < 9 )
                ///cnt_bit 0123456789
                ///tx_data 0123456789
                ///uart_data 12345678
                tx_data <= uart_data [cnt_bit-1]; //这里uart_data是不断随着cnt_bit变化的,只有在第九位的时候才有正确的最终值
                else if(cnt_bit == 0)
                tx_data <= 0 ;
                else if(cnt_bit == 9)
                tx_data <= (MODE_CHECK == 0)? ^uart_data: ~^uart_data;
              /*
              MODE_CHECK == 0是偶校验,假如uart_data是1110_0000,其异或的结果
              是1,将异或的结果作为校验位,让数据位和校验位异或的结果为0,满足偶校验。
              假如uart_data是1110_1000,其异或的结果是0,将异或的结果作为校验位,
              让数据位和校验位异或的结果为0,满足偶校验。奇校验则相反。
              */
                else if (cnt_bit == 10)///停止位
                tx_data <= 1 ;
                else
                tx_data <= tx_data ;
          end
          else
          tx_data <= 1 ;
          
          
       always@(posedge sys_clk )
          if(!rst_n)           
            tx_done <= 0 ;
          else if (tx_flag)begin
               if ( cnt_bit == 10 && cnt == MID/2 -1)
                   tx_done <= 1 ;
               else
                   tx_done <= 0 ;       
          end
          else
            tx_done <= 0 ;  
endmodule

三、仿真设计

`timescale 1ns / 1ps
module test_bench(  );
 
    reg                      sys_clk    ;
    reg                      rst_n      ;
    reg                      rx_data    ;  //输入串行数据
    wire                      tx_data    ;
 
    parameter         SYSCLK =   50_000_000  ;
    parameter         Baud   =   115200      ;
    parameter         COUNT  =   SYSCLK/Baud ;//434   传输1比特所需要的时钟周期
    parameter         MID    =   COUNT/2     ;
    
initial
      begin
           sys_clk = 0 ;
           rst_n   = 0 ;
           #10
           rst_n   = 1 ;
      end
  always #1 sys_clk = ~sys_clk ; //2ns
  
  initial
        begin
            uart_out (8'h31);
            uart_out (8'hef);
            uart_out (8'h10);
            uart_out (8'h78);
            uart_out (8'h66);
        end
  
  
  //任务函数
  task     uart_out   ;
     input      [7:0]    DATA   ;
         begin
              rx_data = 1 ;///空闲位初始
              #20 
              rx_data = 0 ;///起始位
               ///传输1bit的计时次数*1周期时间=总时间
               #(COUNT*2)  rx_data = DATA[0] ;///数据位第一位
               #(COUNT*2)  rx_data = DATA[1] ;///数据位第二位
               #(COUNT*2)  rx_data = DATA[2] ;
               #(COUNT*2)  rx_data = DATA[3] ;
               #(COUNT*2)  rx_data = DATA[4] ;
               #(COUNT*2)  rx_data = DATA[5] ;
               #(COUNT*2)  rx_data = DATA[6] ;
               #(COUNT*2)  rx_data = DATA[7] ;
               #(COUNT*2)  rx_data = 0       ;
/*这一步相当于代替了串口调试工具,串口调试工具根据选择的“偶校验”
自动在数据位后面生成“0”或者“1”,我们这边只能赋值一个数“0”或者“1”。
我选择赋值为0,所以注定了“8'h31”“8'hef”“8'h10”是通不过的。
*/
               #(COUNT*2)  rx_data = 1       ;
               #(COUNT*2)                    ;//停止位也需要时间
         end
  
  
  
  endtask
 
 TOP  TOP_U1(
                . sys_clk (sys_clk) ,
                . rst_n   (rst_n  ) ,
                . rx_data (rx_data) ,
                . tx_data (tx_data) 
    );
 

endmodule

仿真结果:

实验结果:

管脚绑定:

set_property PACKAGE_PIN T17 [get_ports rst_n]
set_property PACKAGE_PIN K17 [get_ports sys_clk]
set_property PACKAGE_PIN W18 [get_ports rx_data]
set_property PACKAGE_PIN V18 [get_ports tx_data]
set_property IOSTANDARD LVCMOS33 [get_ports rst_n]
set_property IOSTANDARD LVCMOS33 [get_ports rx_data]
set_property IOSTANDARD LVCMOS33 [get_ports sys_clk]
set_property IOSTANDARD LVCMOS33 [get_ports tx_data]

实验结果:

回环测试成功

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值