zoukankan      html  css  js  c++  java
  • SPI的通信试验 --verilog (从机-全双工)

        SPI的 有关知识参考FPGA作为主机的通信实验。

        本实验中FPGA作为从机通过SPI与MCU等通信的试验,可以在时钟上升沿接收数据并且在时钟下降沿发送数据,模仿全双工模式。接收的

    数据作为地址,通过读取ROM中地址的数据然后发送出去。注意 发送完成以及接收完成之后的数据处理的关系。

       程序:

        顶层文件:       

    /********************************Copyright**************************************                           
    **----------------------------File information--------------------------
    ** File name  :spi_slave_send.v  
    ** CreateDate :2015.04
    ** Funtions   :FPGA作为从机在接收主机发来的地址信号时,输出数据信号,同时还接收主机发来的地址数据(全双工),一直到CS线拉高,没有信号再从主机中发出。
                   数据地址都为8bit,spi的时钟在发送完8位之后最好能够间隔一下再发生另外8个时钟   
    ** Operate on :M5C06N3L114C7
    ** Copyright  :All rights reserved. 
    ** Version    :V1.0
    **---------------------------Modify the file information----------------
    ** Modified by   :
    ** Modified data :        
    ** Modify Content:
    *******************************************************************************/
     
     module  spi_slave (
               clk,
               rst_n,
               
                         spi_cs,
                         spi_sck,
                         spi_miso,
                         spi_mosi,
                         
                         spi_over
                 );
     input          clk;
     input          rst_n;
     
     input           spi_cs;
     input           spi_sck;
     output          spi_miso;
     input           spi_mosi;
     
     output          spi_over;
    
    //-----------------------------
     wire   [7:0]    rdata;
     wire            rover;
     wire            txd_en;
     wire   [7:0]    txd_data;
     wire            txd_over;
      spi_slave_rxd  spi_slave_rxd_1(
                                     .clk(clk),
                                     .rst_n(rst_n),
                           
                                     .spi_cs(spi_cs),
                                     .spi_sck(spi_sck),
                                     .spi_miso(),
                                     .spi_mosi(spi_mosi),
                                     
                                     .rdata_out(rdata),
                                     .rover(rover)
                                         );
    
      data_read  data_read_1(
                       .clk(clk),
                                     .rst_n(rst_n),
                                            
                                     .rover(rover),
                                     .rdata(rdata),
                                             
                                      .txd_en(txd_en),
                                     .txd_data(txd_data)
                                            
                                            );
      spi_slave_txd  spi_slave_txd_1(
                   .clk(clk),
                   .rst_n(rst_n),
                   
                             .txd_en(txd_en),
                             .txd_data(txd_data),
                             
                             .spi_cs(spi_cs),
                             .spi_sck(spi_sck),
                             .spi_mosi(spi_mosi),
                             .spi_miso(spi_miso),
                             
                             .spi_over(spi_over),
                             .txd_over(txd_over)
                     );
    
    
    
    endmodule
    View Code

        接收程序:

    /********************************Copyright**************************************                           
    **----------------------------File information--------------------------
    ** File name  :SPI_slave_rd.v  
    ** CreateDate :2015.04
    ** Funtions   :SPI的通信实验,FPGA作为从机,接收主机数据以及向主机发送数据
    ** Operate on :M5C06N3L114C7
    ** Copyright  :All rights reserved. 
    ** Version    :V1.0
    **---------------------------Modify the file information----------------
    ** Modified by   :
    ** Modified data :        
    ** Modify Content:
    *******************************************************************************/
     
    
      module  spi_slave_rxd  (
                                     clk,
                                     rst_n,
                           
                                     spi_cs,
                                     spi_sck,
                                     spi_miso,
                                     spi_mosi,
                                     
                                     rdata_out,
                                     rover
                                         );
         input          clk;
         input          rst_n;
         
         input         spi_cs;
         input         spi_sck;
         input         spi_mosi;
         output        spi_miso;
         
         output reg [7:0] rdata_out;
         output reg       rover;
     //---------------------------------------
       reg           spi_cs_0,spi_cs_1;  /* 延时两个时钟,配合检测时钟边沿 */
         reg           spi_sck_0,spi_sck_1;
         reg           spi_mosi_0,spi_mosi_1;
         wire          spi_sck_pos;
    //     wire          spi_sck_neg;
         wire          spi_cs_pos;
         wire          spi_cs_neg;
         wire          spi_cs_flag;
         wire          spi_miso_flag;
         always @(posedge clk or negedge rst_n)
         begin
          if(!rst_n)
           begin
              {spi_cs_1,spi_cs_0} <= 2'b11;
                    {spi_sck_1,spi_sck_0} <= 2'b00;
                    {spi_mosi_1,spi_mosi_0} <= 2'b00;
            end
          else 
            begin
              {spi_cs_1,spi_cs_0} <= {spi_cs_0,spi_cs}; 
                    {spi_sck_1,spi_sck_0} <= {spi_sck_0,spi_sck};
                    {spi_mosi_1,spi_mosi_0} <= {spi_mosi_0,spi_mosi};
            end
          end
            assign  spi_sck_pos = ~spi_sck_1 &spi_sck_0;  /* 取上升沿 */
    //        assign  spi_sck_neg = ~spi_sck_0 &spi_sck_1;  /* 取下降沿 */
          assign  spi_cs_pos  = ~spi_cs_1&spi_cs_0;      /* 取spi_cs上升沿,作为结束信号 */
            assign  spi_cs_neg  = ~spi_cs_0&spi_cs_1;    /* 取spi_cs下降沿,作为开始信号 */
          assign  spi_cs_flag = spi_cs_1;
            assign  spi_miso_flag = spi_mosi_1;
        //----------------------------------------------------------
        localparam  idel = 3'd0;
        localparam  rxd_sta = 3'd1;
        localparam  rxd_over = 3'd2;    
          reg  [3:0]   cnt;
          reg  [7:0]   rdata;
            reg  [2:0]   state;
            always @(posedge clk or negedge rst_n)
             begin
              if(!rst_n)
               begin
                 cnt <= 0;
                     rdata <= 0;
                     state <= idel;
                     rover <= 0;
                     rdata_out <= 0;
                end
                else 
                    begin
                        case(state)    
                         idel:
                           begin
                                cnt <= 0;
                          rdata <= 0;
                                rover <= 0;
                                rdata_out <= 0;
                                if(spi_cs_neg) 
                                   state <= rxd_sta;
                                 else 
                                     state <= idel;
                                end
                       rxd_sta:
                           begin
                                    if(spi_cs_flag) 
                                      state <= idel;
                                     else 
                                         begin
    //                                         if((cnt == 8)&&(spi_sck_neg))     /* 更严谨 */
                                             if((cnt == 8))      
                                               begin
                                                        cnt <= 0;
                                                        state <= rxd_over;
                                                        rdata_out <= rdata;
                                                        rover <= 1;
                                                    end
                                              else 
                                                  begin
                                                     if(spi_sck_pos)
                                                            begin
                                                             cnt <= cnt + 1;  /* 最后cnt=8 */    
                                                             rdata[4'd7 - cnt] <= spi_miso_flag;  /* 从最高位到最低位逐渐接收数据 */
                                                            end
                                                     else
                                                            begin
                                                                cnt <= cnt ;  
                                                                rdata <= rdata;                      
                                                            end     
                                                end                                         
                                            end
                                end
                         rxd_over:
                            begin
                                    rover<= 0;    
                                    state <= rxd_sta;    
                                 end
                    default :    state <= idel;         
                 endcase
          end    
      end
        
    //    assign rdata_out = rdata;
    //          else if(!psi_cs_flag)
    //            begin
    //             if(spi_sck_pos)
    //                  begin
    //                         cnt <= cnt + 1;  /* 最后cnt=8 */    
    //                         rdata[4'd7 - cnt] <= spi_miso_flag;  /* 从最高位到最低位逐渐接收数据 */
    //                        end
    //                 else
    //                        begin
    //                            cnt <= cnt ;  
    //                          rdata <= rdata;                      
    //                        end
    //            end
    //             else 
    //                 begin
    //                            cnt <= 0 ;  
    //                          rdata <= rdata;          /* 注意:保持,不清除 */              
    //                 end    
    //          end
    //            
    //      //------------------------
    //     always @(posedge clk or negedge rst_n)
    //     begin
    //      if(!rst_n)
    //       begin
    //          rdata_out <= 8'd0;
    //                rover <= 0;
    //        end
    //      else if(spi_cs_pos)
    //        begin
    //              rdata_out <= rdata; /* 赋值 */
    //                rover <= 1;         /* 置位 */
    //        end
    //        else 
    //            begin
    //              rdata_out <= rdata_out;  /* 注意:保持,不清除 */   
    //                rover <= 0;        /* 清除 */
    //        end
    //      end
     
        endmodule
        
        
    View Code

       数据转换程序:

    /********************************Copyright**************************************                           
    **----------------------------File information--------------------------
    ** File name  :data_read.v  
    ** CreateDate :2015.04
    ** Funtions   : 因为接收完成标志只有一个时钟周期,所以需要马上寄存地址数据,并且地址读取待发送的数据,然后置位发送是能信号。
    ** Operate on :M5C06N3L114C7
    ** Copyright  :All rights reserved. 
    ** Version    :V1.0
    **---------------------------Modify the file information----------------
    ** Modified by   :
    ** Modified data :        
    ** Modify Content:
    *******************************************************************************/
     
       module data_read(
                          clk,
                                            rst_n,
                                            
                                            rover,
                                            rdata,
                                            
                                            txd_en,
                                            txd_data
                                            
                                            ); 
    
      input         clk;
        input         rst_n;
        
        input         rover;
        input  [7:0]  rdata;
        
        output        txd_en;
        output  [7:0] txd_data;
        
     //------------------------------//
     reg            r_over_1;
     reg   [7:0]    r_addr;
     always @(posedge clk or negedge rst_n)
     begin
      if(!rst_n)
       begin
         r_addr <= 0;
             r_over_1 <= 0;
        end
      else if(rover)
        begin
         r_addr <= rdata; 
             r_over_1 <= 1;
        end
        else if(txd_en)
           r_over_1 <= 0;
      end
        
     reg      r_over_1_1;
     reg    r_over_1_2;
     reg    r_over_1_3;
     always @(posedge clk or negedge rst_n)
     begin
      if(!rst_n)
       begin
          {r_over_1_3,r_over_1_2,r_over_1_1} <= 3'b000;
        end
      else 
        begin
          {r_over_1_3,r_over_1_2,r_over_1_1} <= {r_over_1_2,r_over_1_1,r_over_1}; 
        end
      end
     assign  txd_en =     ~r_over_1_3&r_over_1_2;
         
        
        data_rom data_rom_1(
                             .addr(r_addr),
                                                 .data(txd_data)
                                                 );
    endmodule
    View Code

       rom程序: 

    /********************************Copyright**************************************                           
    **----------------------------File information--------------------------
    ** File name  :data_rom.v  
    ** CreateDate :2015.04
    ** Funtions   : 简单 rom 文件
    ** Operate on :M5C06N3L114C7
    ** Copyright  :All rights reserved. 
    ** Version    :V1.0
    **---------------------------Modify the file information----------------
    ** Modified by   :
    ** Modified data :        
    ** Modify Content:
    *******************************************************************************/
     
    
     module  data_rom  (
                  addr,
                                
                                data
    
                 );
     input   [7:0]       addr;
     output  [7:0]       data;
     
    // always @(*)
    //    begin
    //         case()
    //         end
     assign data = addr + 1;
    
    endmodule
    
    
     
    View Code

    发送程序:

        /********************************Copyright**************************************                           
        **----------------------------File information--------------------------
        ** File name  :spi_slave_txd.v  
        ** CreateDate :2015.04
        ** Funtions   :FPGA作为从机的发送数据程序
        ** Operate on :M5C06N3L114C7
        ** Copyright  :All rights reserved. 
        ** Version    :V1.0
        **---------------------------Modify the file information----------------
        ** Modified by   :
        ** Modified data :        
        ** Modify Content:
        *******************************************************************************/
     
      module  spi_slave_txd  (
                   clk,
                   rst_n,
                   
                             txd_en,
                             txd_data,
                             
                             spi_cs,
                             spi_sck,
                             spi_mosi,
                             spi_miso,
                             
                             spi_over,
                             txd_over
                     );
         input          clk;
         input          rst_n;
         
         input          txd_en;
         input  [7:0]   txd_data;
         input          spi_cs;
         input          spi_sck;
         input          spi_mosi;
         output   reg   spi_miso;
         
         output         spi_over;
         output   reg   txd_over;
         
         //----------------------------------
          reg        spi_cs_0,spi_cs_1;
            reg        spi_sck_0,spi_sck_1;
            wire       spi_sck_neg;
            wire       spi_over;
            wire       spi_cs_flag;
            always @(posedge clk or negedge rst_n)
             begin
              if(!rst_n)
               begin
                 {spi_cs_1,spi_cs_0} <= 2'b11;
                     {spi_sck_1,spi_sck_0} <= 2'b00;
                end
              else 
                begin
                 {spi_cs_1,spi_cs_0} <=  {spi_cs_0,spi_cs};
                     {spi_sck_1,spi_sck_0} <= {spi_sck_0,spi_sck};
                end
              end
            
            assign spi_cs_flag = spi_cs_1;
            assign spi_sck_neg = (spi_sck_1&(~spi_sck_0));
            assign spi_over  =     ~spi_cs_1&spi_cs_0;
         //---------------------------------------------//
         localparam   idel = 2'd0;
         localparam   txd_sta= 2'd2;
         localparam   txd_data_sta = 2'd1;
         reg     [1:0]  state;
         reg     [3:0]  cnt;
         reg     [7:0]  txdata;
         
        always @(posedge clk or negedge rst_n)
         begin
          if(!rst_n)
           begin
             state <= idel;
                 cnt <= 0;
             txdata <= 0;
                 spi_miso <= 1;   /* 拉高 */
          end
          else 
            begin
              case(state)
                     idel:
                       begin
                             cnt <= 0;
                   txdata <= 0;
                             spi_miso <= 1;   /* 拉高 */
                             if(txd_en)     
                              begin
                                     state <= txd_data_sta;
                                     
                                    end
                                else
                                    begin
                                        state <= idel;    
                                    end                                    
                          end
                        txd_data_sta:
                          begin
                                    txdata <= txd_data;
                                    state <= txd_sta;    
                                    txd_over <=0;
                                end
                     txd_sta:
                         begin
                            if(spi_cs_flag )     
                                 state <= idel;
                            else if(cnt == 8)
                              begin
                                    cnt <= 0;    
                                    txd_over <= 1;
                                    state <= txd_data_sta;
                                    end
                             else
                                    begin
                                         if(spi_sck_neg)
                                             begin
                                                 spi_miso <= txdata[7-cnt[2:0]] ; /* 先高位再低位传输 */
                                                 cnt <= cnt +1;    /* 范围:0-8 */
                                                end
                                            else 
                                                begin
                                                    spi_miso <= spi_miso; /* 保持 */
                                                  cnt <= cnt;
                                                end
                                        end                                                     
                              end
                    default:state <= idel;
                 endcase    
            end
          end        
        
        endmodule
        
    View Code

      仿真程序:

     
        /********************************Copyright**************************************                           
        **----------------------------File information--------------------------
        ** File name  :spi_slave_tb.v  
        ** CreateDate :2015.04
        ** Funtions   :测试文件
        ** Operate on :M5C06N3L114C7
        ** Copyright  :All rights reserved. 
        ** Version    :V1.0
        **---------------------------Modify the file information----------------
        ** Modified by   :
        ** Modified data :        
        ** Modify Content:
        *******************************************************************************/
      
        `timescale 1 ns/1 ns
        
        module  spi_slave_tb ;
           reg          clk;
             reg          rst_n;
             
             reg           spi_cs;
             reg           spi_sck;
             wire          spi_miso;
             reg           spi_mosi;
             
             wire          spi_over;
        
        spi_slave spi_slave_1(
                                     .clk,
                                     .rst_n,
                                     
                                     .spi_cs,
                                     .spi_sck,
                                     .spi_miso,
                                     .spi_mosi,
                                     
                                     .spi_over
                                         );
    
         parameter tck = 24;
         parameter t = 1000/tck;
         
         always 
           #(t/2) clk = ~clk;
        
         
         //-------------------------------
      /* 模仿spi主机的发送程序,这个task很好,仿顺序操作,可以直观的显示过程 */
        task  spi_sd;
        input [7:0]  data_in;
        begin
        #(5*t);
            
            spi_sck = 0; spi_mosi= data_in[7]; #(5*t);    spi_sck = 1; #(5*t);    //send bit[7]
            spi_sck = 0; spi_mosi= data_in[6]; #(5*t);    spi_sck = 1; #(5*t);    //send bit[6]
            spi_sck = 0; spi_mosi= data_in[5]; #(5*t);    spi_sck = 1; #(5*t);    //send bit[5]
            spi_sck = 0; spi_mosi= data_in[4]; #(5*t);    spi_sck = 1; #(5*t);    //send bit[4]
            spi_sck = 0; spi_mosi= data_in[3]; #(5*t);    spi_sck = 1; #(5*t);    //send bit[3]
            spi_sck = 0; spi_mosi= data_in[2]; #(5*t);    spi_sck = 1; #(5*t);    //send bit[2]
            spi_sck = 0; spi_mosi= data_in[1]; #(5*t);    spi_sck = 1; #(5*t);    //send bit[1]
            spi_sck = 0; spi_mosi= data_in[0]; #(5*t);    spi_sck = 1; #(5*t);    //send bit[0]
          spi_sck = 0;
                 
            end
        endtask
        
        
        initial 
          begin
           clk = 0;
             rst_n = 0;
             spi_cs = 1;
             spi_sck = 0;
           spi_mosi = 0;
             
             #(20*t) rst_n = 1; 
             #(10*t);
              spi_cs = 0;
              spi_sd(8'h01);
              #(50*t);
              spi_sd(8'h04);
                #(50*t);
              spi_sd(8'h00);
              #(50*t);
              spi_cs = 1;
          end
        
        
        
        
        endmodule
        
        
      
    View Code

      仿真图:

        

      

  • 相关阅读:
    AxWindowsMediaPlayer的详细用法
    C# 播放器控件的常用方法
    在 C# 中使用文件名启动应用程序
    备份和恢复Android手机的NAND分区
    刷Recovery的方法
    WPF RadioButton的探究,为啥选中一个其他都自动不选中了呢?
    WPF大牛Josh Smith 转投 iOS 的怀抱
    终端服务的剪贴板的缺陷,导致WPF调用Clipboard.SetText() 失败
    用df命令查看分区情况
    WPF与输入法冲突研究之二:TextInput事件的BUG?
  • 原文地址:https://www.cnblogs.com/fhyfhy/p/4428362.html
Copyright © 2011-2022 走看看