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

仿真图片:

 
原文地址:https://www.cnblogs.com/fhyfhy/p/4430969.html