zoukankan      html  css  js  c++  java
  • Verilog -- SPI协议

    Verilog -- SPI协议

    简介

    SPI是一种全双工通信,并且是一种同步传输方式(slave的接收clk需要master给出)

    SPI总线是一种4线总线,因其硬件功能很强,所以与SPI有关的软件就相当简单,使中央处理器(Central Processing Unit,CPU)有更多的时间处理其他事务。正是因为这种简单易用的特性,越来越多的芯片集成了这种通信协议,比如AT91RM9200。SPI是一种高速、高效率的串行接口技术。通常由一个主模块和一个或多个从模块组成,主模块选择一个从模块进行同步通信,从而完成数据的交换。SPI是一个环形结构,通信时需要至少4根线(事实上在单向传输时3根线也可以)。
    SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,事实上3根也可以(单向传输时)。也是所有基于SPI的设备共有的,它们是MISO(主设备数据输入)、MOSI(主设备数据输出)、SCLK(时钟)、CS(片选)。
    (1)MISO– Master Input Slave Output,主设备数据输入,从设备数据输出;
    (2)MOSI– Master Output Slave Input,主设备数据输出,从设备数据输入;
    (3)SCLK – Serial Clock,时钟信号,由主设备产生;
    (4)CS – Chip Select,从设备使能信号,由主设备控制。
    其中,CS是从芯片是否被主芯片选中的控制信号,也就是说只有片选信号为预先规定的使能信号时(高电位或低电位),主芯片对此从芯片的操作才有效。这就使在同一条总线上连接多个SPI设备成为可能。
    (以上来自百度百科)

    SPI最大传输速率

    SPI是一种事实标准,由Motorola开发,并没有一个官方标准。已知的有的器件SPI已达到50Mbps。具体到产品中SPI的速率主要看主从器件SPI控制器的性能限制。
    受以下几个条件影响:
    SPI的最大时钟频率
    CPU处理SPI数据的能力
    输出端驱动能力(PCB所允许的最大信号传输速率)

    SPI的最大时钟频率

    一般情况下,SPI模块的最大时钟频率为系统时钟频率的1/2。虽然SPI的传输速率主要受限于CPU处理SPI数据的能力,但在同另一个非常高速率的SPI设备通讯时,SPI的最大时钟频率将有可能制约其传输速率。

    CPU处理SPI数据的能力

    通常情况下,考虑到系统中CPU有可能需要处理其他任务,以及对所接收SPI数据的具体运算处理方法,CPU处理SPI数据的能力将影响到整体的传输速率。

    以上内容参考 https://www.cnblogs.com/liujinggang/p/9609739.html

    下面是verilog代码:

    
    module spi_master
    (
        input               I_clk       , // 全局时钟50MHz
        input               I_rst_n     , // 复位信号,低电平有效
        input               I_en        , //  en信号
        input        [7:0]  I_data_in   , // 要发送的数据
        output  reg  [7:0]  O_data_out  , // 接收到的数据
        output  reg         O_tx_done   , // 发送一个字节完毕标志位
        output  reg         O_rx_done   , // 接收一个字节完毕标志位
        
        // 四线标准SPI信号定义
        input               I_spi_miso  , // SPI串行输入,用来接收从机的数据
        output  reg         O_spi_sck   , // SPI时钟
        output  reg         O_spi_cs    , // SPI片选信号
        output  reg         O_spi_mosi    // SPI输出,用来给从机发送数据          
    );
    
    reg [3:0]   R_state      ; 
    
    always @(posedge I_clk or negedge I_rst_n)
    begin
        if(!I_rst_n)
            begin
                R_state     <=  4'd0    ;
                O_spi_cs    <=  1'b1    ;
                O_spi_sck   <=  1'b0    ;
                O_spi_mosi  <=  1'b0    ;
                O_tx_done   <=  1'b0    ;
                O_rx_done   <=  1'b0    ;
                O_data_out  <=  8'd0    ;
            end 
        else if(I_en) // en信号打开的情况下
            begin
                O_spi_cs    <=  1'b0    ; // 把片选CS拉低
                R_state     <=  R_state + 1'b1   ;
                case(R_state)
                    // 发送
                    4'd0:    // 发送第7位
                        begin
                            O_spi_mosi  <=  I_data_in[7]        ;
                            O_spi_sck   <=  1'b0                ;
                            O_tx_done   <=  1'b0                ;
                          end
                    4'd2:    // 发送第6位
                        begin
                            O_spi_mosi  <=  I_data_in[6]        ;
                            O_spi_sck   <=  1'b0                ;
                            O_tx_done   <=  1'b0                ;
                          end
                    4'd4:    // 发送第5位
                        begin
                            O_spi_mosi  <=  I_data_in[5]        ;
                            O_spi_sck   <=  1'b0                ;
                            O_tx_done   <=  1'b0                ;
                        end 
                    4'd6:    // 发送第4位
                        begin
                            O_spi_mosi  <=  I_data_in[4]        ;
                            O_spi_sck   <=  1'b0                ;
                            O_tx_done   <=  1'b0                ;
                        end 
                    4'd8:    // 发送第3位
                        begin
                            O_spi_mosi  <=  I_data_in[3]        ;
                            O_spi_sck   <=  1'b0                ;
                            O_tx_done   <=  1'b0                ;
                        end                            
                    4'd10:    // 发送第2位
                        begin
                            O_spi_mosi  <=  I_data_in[2]        ;
                            O_spi_sck   <=  1'b0                ;
                            O_tx_done   <=  1'b0                ;
                        end 
                    4'd12:    // 发送第1位
                        begin
                            O_spi_mosi  <=  I_data_in[1]        ;
                            O_spi_sck   <=  1'b0                ;
                            O_tx_done   <=  1'b0                ;
                        end 
                    4'd14:    // 发送第0位
                        begin
                            O_spi_mosi  <=  I_data_in[0]        ;
                            O_spi_sck   <=  1'b0                ;
                            O_tx_done   <=  1'b1                ;
                          end 
    
                    // 接收
                    4'd1:    // 接收第7位
                        begin
                            O_spi_sck       <=  1'b1                ; 
                            O_rx_done       <=  1'b0                ;
                            O_data_out[7]   <=  I_spi_miso          ;   
                          end
                    4'd3:    // 接收第6位
                        begin
                            O_spi_sck       <=  1'b1                ;
                            O_rx_done       <=  1'b0                ;
                            O_data_out[6]   <=  I_spi_miso          ; 
                        end
                    4'd5:    // 接收第5位
                        begin
                            O_spi_sck       <=  1'b1                ;
                            O_rx_done       <=  1'b0                ;
                            O_data_out[5]   <=  I_spi_miso          ; 
                        end 
                    4'd7:    // 接收第4位
                        begin
                            O_spi_sck       <=  1'b1                ;
                            O_rx_done       <=  1'b0                ;
                            O_data_out[4]   <=  I_spi_miso          ; 
                        end 
                    4'd9:    // 接收第3位
                        begin
                            O_spi_sck       <=  1'b1                ;
                            O_rx_done       <=  1'b0                ;
                            O_data_out[3]   <=  I_spi_miso          ; 
                        end                            
                    4'd11:    // 接收第2位
                        begin
                            O_spi_sck       <=  1'b1                ;
                            O_rx_done       <=  1'b0                ;
                            O_data_out[2]   <=  I_spi_miso          ; 
                        end 
                    4'd13:    // 接收第1位
                        begin
                            O_spi_sck       <=  1'b1                ;
                            O_rx_done       <=  1'b0                ;
                            O_data_out[1]   <=  I_spi_miso          ; 
                        end 
                    4'd15:    // 接收第0位
                        begin
                            O_spi_sck       <=  1'b1                ;
                            O_rx_done       <=  1'b1                ;
                            O_data_out[0]   <=  I_spi_miso          ; 
                          end
                    default:R_state  <=  4'd0                ;   
                endcase 
              end 
               
        else
            begin
                R_state     <=  4'd0    ;
                O_tx_done   <=  1'b0    ;
                O_rx_done   <=  1'b0    ;
                O_spi_cs    <=  1'b1    ;
                O_spi_sck   <=  1'b0    ;
                O_spi_mosi  <=  1'b0    ;
                O_data_out  <=  8'd0    ;
            end      
    end
    
    
    endmodule
    
    

    testbench:

    `timescale 1ns/1ps
    
    module spi_master_tb();
    
    reg I_clk;
    reg I_rst_n;
    reg I_en;
    reg  [7:0]I_data_in; 
    wire [7:0]O_data_out;
    wire O_tx_done;  
    wire O_rx_done;
    
    wire I_spi_miso;
    wire O_spi_sck;  
    wire O_spi_cs;  
    wire O_spi_mosi;
    
    always #1 I_clk = ~ I_clk;
    
    assign I_spi_miso = O_spi_mosi;
    
    integer i;
    initial begin
      I_clk = 1;
      I_en = 0;
      I_data_in = 0;
      I_rst_n = 1; #2 I_rst_n = 0; #2 I_rst_n = 1;
    
      #20;
      @(posedge I_clk) begin
          I_en <= 1;
          I_data_in <= 0;
        end
      for(i=1;i<20;i=i+1) begin
        @(negedge O_tx_done) begin
          I_data_in <= i;
        end
      end
      I_en <= 0;
    end
    
    
    spi_master U_SPI_MASTER_0(
        .I_clk                          ( I_clk                         ),
        .I_rst_n                        ( I_rst_n                       ),
        .I_en                           ( I_en                          ),
        .I_data_in                      ( I_data_in                     ),
        .O_data_out                     ( O_data_out                    ),
        .O_tx_done                      ( O_tx_done                     ),
        .O_rx_done                      ( O_rx_done                     ),
        .I_spi_miso                     ( I_spi_miso                    ),
        .O_spi_sck                      ( O_spi_sck                     ),
        .O_spi_cs                       ( O_spi_cs                      ),
        .O_spi_mosi                     ( O_spi_mosi                    )
    );
    
    initial begin
        $fsdbDumpvars();
        $fsdbDumpMDA();
        $dumpvars();
        #1000 $finish;
     end
    
    endmodule
    
    

    仿真波形:

    测试程序中将MOSI和MISO短接了,所以spi发送的数据被自己接收,从波形图中可以看到,发送和接收的逻辑正确。

  • 相关阅读:
    Excel导入SQL数据库完整代码
    获取google地图经纬度
    设为首页、加入收藏 兼容代码
    破解电脑开机密码
    微软的面试题
    ashx文件中使用session
    MetaEdit、修改IIS链接人数限制
    男人戴戒指的意义
    .Net/C#中Cache的用法
    SharedPreferences用于存储数据
  • 原文地址:https://www.cnblogs.com/lyc-seu/p/12629382.html
Copyright © 2011-2022 走看看