SPI試驗---verilog(實用單通模式)


SPI通信的讀寫操作

一、     SPI簡介:

   SPI的通信原理很簡單,它以主從方式工作,這種模式通常有一個主設備和一個或多個從設備,需要至少4根線,事實上3根也可以(單向傳輸時)。也是所有基於SPI的設備共有的,它們是SDI(數據輸入)、SDO(數據輸出)、SCLK(時鍾)、CS(片選)

(1)SDO – 主設備數據輸出,從設備數據輸入;

(2)SDI – 主設備數據輸入,從設備數據輸出;

(3)SCLK – 時鍾信號,由主設備產生;

(4)CS – 從設備使能信號,由主設備控制。

其中,CS是控制芯片是否被選中的,也就是說只有片選信號為預先規定的使能信號時(高電位或低電位),對此芯片的操作才有效。這就允許在同一總線上連接多個SPI設備成為可能。

由SCLK提供時鍾脈沖,SDI,SDO則基於此脈沖完成數據傳輸。數據輸出通過 SDO線,數據在時鍾上升沿或下降沿時改變,在緊接着的下降沿或上升沿被讀取。完成一位數據傳輸,輸入也使用同樣原理。

要注意的是,SCLK信號線只由主設備控制,從設備不能控制信號線。同樣,在一個基於SPI的設備中,至少有一個主控設備。這樣傳輸的特點:這樣的傳輸方式有一個優點,與普通的串行通訊不同,普通的串行通訊一次連續傳送至少8位數據,而SPI允許數據一位一位的傳送,甚至允許暫停

二、     SPI的時序電路圖:

    SPI時鍾極性CPOL = 0表示在沒有數據傳輸時為低電平,= 1表示沒有數據傳輸時為高電平。

    SPI時鍾相位CPHA,= 0表示時鍾的第一個沿更新數據、第二個沿鎖存數據,= 1表示時鍾的第一個沿鎖存數據、第二個沿更新數據。

  

 程序代碼:

    /********************************Copyright**************************************                           
    **----------------------------File information--------------------------
    ** File name  :spi_writeread.v  
    ** CreateDate :2015.04
    ** Funtions   : SPI作為主機向從機讀寫,讀的時候要注意,總共為15個時鍾,在最后一個寫地址時鍾的下降沿就開始讀取數據,
                   若要在下一個時鍾的下降沿讀取數據則要根據需要修改程序。注:本程序先發送最高位,先接收最高位
    ** Operate on :M5C06N3L114C7
    ** Copyright  :All rights reserved. 
    ** Version    :V1.0
    **---------------------------Modify the file information----------------
    ** Modified by   :
    ** Modified data :        
    ** Modify Content:
    *******************************************************************************/
     

    module  spi_writeread (
                                             clk,
                                             rst_n,                 
                                             spi_re_en,
                                             spi_wr_en,
                                             spi_addr,
                                             spi_send_data,
                                             spi_read_data,
                                             
                                             spi_cs,
                                             spi_clk,
                                             spi_mi,
                                             spi_mo,
                                             spi_busy,
                                             spi_over                                         
                                                 );
     input          clk;
     input          rst_n;
     input          spi_re_en;    //接收使能
     input          spi_wr_en;    //發送使能
     input  [7:0]   spi_addr;     //待發送的地址
     input  [7:0]   spi_send_data;  //待發送的數據 
     
     output         spi_cs;    //片選信號
     output         spi_clk;   //時鍾信號
     input          spi_mi;    //主機從芯片讀取的數據
     output         spi_mo;    //主機向芯片發送的數據
     output   reg        spi_over;   //spi操作完成
     output   reg [7:0]  spi_read_data;  //spi接收的數據,即讀取的數據
     output   reg        spi_busy;      //spi忙信號
     
     reg        temp_cs;         
     reg        temp_scl;
     reg        temp_mo;
     
     assign  spi_cs = temp_cs;
     assign  spi_clk = temp_scl;
     assign  spi_mo = temp_mo;


    reg             sendbit_over;   //字節發送完成標志
    reg             resbit_over;    //接收字節完成標志
    reg    [7:0]    res_data;       //接收的數據

    //*******************狀態機***************************
     parameter   cnt_delay = 4;   //CS的延時時鍾的計數(根據芯片決定)
     reg    [3:0]    state;              //狀態機
     reg    [7:0]    send_data;         //待發送的移位數據寄存器
     reg    [7:0]    read_data;          //接收數據寄存器
     reg    [2:0]    delay;             //發送完成,延時到可以再次發送,然后待命
     reg             wr_flag;           //寫操作標志 
     reg             re_flag;           //讀操作標志
     reg             send_en;
     reg             resive_en;
     
     always @(posedge clk or negedge rst_n)
     begin
        if(!rst_n)
         begin
                 temp_cs <= 1;                 
         
             state <= 4'd0;
             send_data <= 8'd0;    
             read_data <= 8'd0;
             delay <= 0;
             wr_flag <= 0;
             re_flag <= 0;
             spi_busy <= 0;
                 spi_over <= 0;
             resive_en <= 0;
             send_en <=0;
            end
        else 
            begin
                case(state)
                 4'd0:
                     begin
                             delay <= 0;
                             temp_cs <= 1;
                             send_data <= 8'd0;
                             read_data <= 8'd0;
                             wr_flag <= 0;
                             re_flag <= 0;
                             spi_busy <= 0;
                             spi_over <= 0;
                             resive_en <= 0;
                             send_en <=0;
                             
                             if(spi_wr_en)       //寫使能
                                    begin
                                        spi_busy <=1;    
                                        state <= 4'd1;  
                                        wr_flag <= 1;   //寫操作標志置位高
                                    end
                                else if(spi_re_en)
                                 begin
                                        spi_busy <=1; 
                                        state <= 4'd1;
                                        re_flag <= 1;  //讀操作標志置位高
                                    end
                            end
                    4'd1:                     
                        begin
                             temp_cs <= 0;       //拉低cs信號
                             state <= 4'd2;    
                            end
                 4'd2:                  //拉低時鍾和數據輸出線
                     begin        
                         if(sendbit_over)
                             begin
                                    send_en <=0; 
                                    if(wr_flag) 
                                     begin
                                        state <= 4'd3;     
                                     end
                                    else if(re_flag)
                                     begin
                                        state <= 4'd4;     
                                        resive_en <=1;      /* 接收使能置高 */                                    
                                     end    
                                    else 
                                     begin
                                        state <= 4'd0;                                        
                                     end
                                end
                            else
                                begin
                                    send_data <= spi_addr;   //將地址寄存,然后發送地址                     
                                    state <= 4'd2;    
                                    send_en <=1;      
                                end        
                     end
                4'd3:
                     begin
                             if(sendbit_over)
                             begin
                                    state <= 4'd5;     
                                    send_en <=0;  
                                end
                            else
                                begin
                                    send_data <= spi_send_data;   //將地址寄存,然后發送地址                     
                                    state <= 4'd3;    
                                    send_en <=1;      
                                end         
                        end
                    4'd4:
                        begin
                            if(resbit_over)
                             begin
                                    state <= 4'd5;     
                                    resive_en <=0;
                                    read_data <= res_data; 
                                end
                            else
                                begin                                                 
                                    state <= 4'd4;    
                                    resive_en <=1;      
                                end         
                        end
                        4'd5:
                             begin
                                    temp_cs <= 1;
                                 if(delay == cnt_delay)
                                    begin
                                        state <= 4'd6;
                                        delay <= 0;
                                        spi_over <= 1; 
                                     end
                                    else 
                                        delay <= delay + 1;
                                    end
                            4'd6:
                                begin
                                    spi_over <= 0; 
                                    spi_busy <= 0;
                                    state <= 4'd0;
                                    end
                 default :  state <= 4'd0;                     
             endcase
         end
    end

    always @(posedge clk or negedge rst_n)
     begin
        if(!rst_n)
         begin
             spi_read_data <= 8'd0;
            end
        else 
            begin
                if(spi_over)
                    spi_read_data <= read_data;
                else 
                    spi_read_data <= spi_read_data;
            end
        end                 
     

    //****************發送****************
    reg    [3:0]   send_state;
    reg    [7:0]   shift_data;
    reg    [3:0]   send_num;

    reg    [3:0]    resive_state;
    reg    [2:0]    res_num;


    always @(posedge clk or negedge rst_n)
     begin
        if(!rst_n)
         begin
                temp_scl <= 0;             //模式0狀態,數據、時鍾都為低電平
                temp_mo  <= 0;
                
                shift_data <= 0;
                send_num <= 0;

                send_state<= 0;
                sendbit_over <= 0;
                
                resive_state <= 0;
                res_data <= 8'd0;
                res_num <= 0;
                resbit_over <= 0;
                
            end
        else if(send_en)
            begin
             case(send_state)
                4'd0:
                     begin    
                             temp_scl <= 0;
                             temp_mo <= 0;
                             send_num <= 4'd0;
                             shift_data <= send_data; 
                             send_state <= 4'd1;
                             sendbit_over <= 0;
                            end
                4'd1:
                     begin
                        temp_mo <=  shift_data[7] ;    /* 先發最高位,再發最低位 */          
                        send_state <= 4'd2;                              
                    end
                4'd2:
                    begin
                        temp_scl <= 1;                                 
                        send_state <= 4'd3;    
                    end
                4'd3:
                     begin
                            if(send_num == 4'd7)
                                begin
                                    send_state <= 4'd5;
                                    sendbit_over <= 1; 
                                    send_num <= 4'd0;
                                end
                            else
                                begin
                                    send_state <= 4'd4;  
                                end                         
                        end
                4'd4:
                    begin
                        temp_scl <= 0;
                        shift_data <= shift_data << 1;
                        send_num <= send_num + 1;    
                        send_state <= 4'd1; 
                     end

                4'd5:
                         begin
                             send_state <= 4'd5;  
                             sendbit_over <= 0;
                            end
                default: send_state <= 4'd0;
             endcase           
            end
        else if(resive_en)
                begin
                    case(resive_state)
                        'd0:
                            begin                           
                                 resive_state <= 4'd1;
                                 res_num <= 0;                        
                                 resbit_over <= 0;
                                end
                        'd1: 
                             begin
                                 temp_scl <= 0;        
                                 res_data[0] <= spi_mi;      /* 接收最低位,然后左移,故實際是先接收最高位 */
                                 resive_state <= 4'd2;                                                                       
                                end
                        'd2: 
                             begin
                                if(res_num == 4'd7)    
                                    begin
                                        resive_state <= 4'd5;    
                                        res_num <= 4'd0;
                                    end
                                else
                                 begin
                                    res_data <= res_data << 1;
                                    resive_state <= 4'd3;    
                                 end                                                                       
                                end        
                        'd3:
                            begin
                                    temp_scl <= 1;
                                    resive_state <= 4'd4;                                 
                            end
                        'd4:
                            begin
                                res_num <= res_num + 1;
                                resive_state <= 4'd1;    
                                end
                        'd5:
                            begin
                                 resbit_over <= 1;    
                                 resive_state <= 4'd6;    
                                end
                        'd6:
                            begin
                                resbit_over <= 0;    
                                resive_state <= 4'd6;        
                            end
                    default: resive_state <= 4'd0;    
                endcase
            end    
        else 
                begin                    
                    temp_scl <= 0;
                    temp_mo <= 0;
                    
                    shift_data <= 0;
                    send_num <= 0;
                    sendbit_over <= 0;    
                    send_state<= 0;
                    
                    resive_state <= 4'd0;
                    res_data <= 8'd0;
                    res_num <= 0;
                    resbit_over <= 0;        
             end
     end

    endmodule
View Code

測試程序:

 
    /********************************Copyright**************************************                           
    **----------------------------File information--------------------------
    ** File name  :spi_writeread_tb.v  
    ** CreateDate :2015.04
    ** Funtions   : SP的測試文件
    ** Operate on :M5C06N3L114C7
    ** Copyright  :All rights reserved. 
    ** Version    :V1.0
    **---------------------------Modify the file information----------------
    ** Modified by   :
    ** Modified data :        
    ** Modify Content:
    *******************************************************************************/
     
  module  spi_writeread_tb;  
    
     reg          clk;
     reg          rst_n;
     reg          spi_re_en;    //接收使能
     reg          spi_wr_en;    //發送使能
     reg  [7:0]   spi_addr;     //待發送的地址
     reg  [7:0]   spi_send_data;  //待發送的數據 
     
     wire          spi_cs;    //片選信號
     wire          spi_clk;   //時鍾信號
     reg           spi_mi;    //主機從芯片讀取的數據
     wire          spi_mo;    //主機向芯片發送的數據
     wire          spi_over;   //spi操作完成
     wire   [7:0]  spi_read_data;  //spi接收的數據,即讀取的數據
     wire          spi_busy;      //spi忙信號
     
     spi_writeread  spi_writeread_1(
                                             .clk,
                                             .rst_n,                 
                                             .spi_re_en,
                                             .spi_wr_en,
                                             .spi_addr,
                                             .spi_send_data,
                                             .spi_read_data,
                                             
                                             .spi_cs,
                                             .spi_clk,
                                             .spi_mi,
                                             .spi_mo,
                                             .spi_busy,
                                             .spi_over                                         
                                                 );
                                                 
     parameter tck = 24;
     parameter t = 1000/tck;
     
     always 
       #(t/2) clk = ~clk;
    
     
     always 
       #(5*t) spi_mi = ~spi_mi;
         
   initial 
      begin
        clk = 0;
        rst_n = 0;
            spi_re_en = 0;
            spi_wr_en = 0;
            spi_addr = 0;
            spi_send_data = 0;
            spi_mi = 0;
            
            #(10*t)  rst_n = 1;
            
             #(5*t)  spi_addr = 8'h55;
                    spi_send_data = 8'haa;
             #(2*t) spi_wr_en = 1;
             #(2*t) spi_wr_en = 0;
             
            #(100*t) ;
            #(5*t)  spi_addr = 8'h0f;
              #(2*t) spi_re_en = 1;
              #(2*t) spi_re_en = 0;
             
             
      end
        
                                                 
    
    endmodule
    
View Code

 

仿真圖片:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM