• SPI介绍+软件模拟SPI


    1.什么是SPI

     SPI通常有一个主设备和一个或多个从设备,通常采用的是4根线,它们是MISO(数据输入,针对主机来说)、MOSI(数据输出,针对主机来说)、SCLK(时钟,主机产生)、CS/SS(片选,一般由主机发送或者直接使能,通常为低电平有效)。全双工。

    2.SPI物理层

    图1 SPI物理层连接图

     一个主机可以连接多个从机,其中SCK,MOSI,MISO所有从机共用。SSx单独连接主机。当主机需要和从机通信时,主机把对应的从机的SSx线拉低。从机发现自己的SSx线被拉低,则表示主机要和它通信了。对比IIC,SPI不需要外接上拉电阻,也不需要广播地址来寻机。

    3.SPI协议层

    图2 SPI 协议层信号

     SPI只有开始信号,停止信号,和数据有效性信号,读写信号(同步的)。相比IIC,少了应答机制。

    (1)开始信号:NSS被拉低的下降沿(SSx、CS。都叫做使能线,片选线。叫法很多,作用一样 都是让从机使能通信)

    (2)停止信号:NSS被拉高的上升沿。

    (3)触发,转移,一样的意思。表示主机读一位从机的数据,从机读一位主机的数据。

    (4)采样,采集,读取,一样的意思,表示总线的上数据已就绪,可以读了。采样后,MISO、MOSI线上的数据被锁存直到被转移。

    4.SPI的4种模式

    4.1.极性和相位组合成4种模式

     要说SPI的4种模式,就要先说一下两个概念:时钟极性CPOL和时钟相位CPHA。CPOL和CPHA均有0 1两种状态,组合成4种状态。

    时钟极性:时钟极性 CPOL 是指 SPI 通讯设备处于空闲状态时,SCK 信号线的电平信号(SPI 通讯开始前、 NSS 线为高电平时 SCK 的状态)

           CPOL=0 时, SCK 在空闲状态时为低电平①,在SCK有效期间为高电平 ②

           CPOL=1 时,SCK 在空闲状态时为高电平③,在SCK有效期间为低电平 ④
                          

    图3 时钟极性规定的空闲状态的电平

     时钟相位:时钟相位 CPHA 是指数据的采样的时刻,

           当 CPHA=0 时, MOSI MISO 数据线上的信号将会在 SCK 时钟线的“奇数边沿” 被采样。

                       当 CPHA=1 时,数据线在 SCK 的“偶数边沿” 采样。

                        

    图4 时钟相位CPHA = 0时

      当 CPHA=0 时, MOSI 或 MISO 数据线上的信号将会在 SCK 时钟线的“奇数边沿” 被采样。根据时钟极性的不同,奇数边沿可能是上升沿也可能是下降沿。

      可以看到,采集数据是边沿触发的,且没有明确要求是上升沿还是下降沿。CPHA=0时,采集数据是在奇数边沿,则转换数据是在偶数边沿。

                      

       图5 时钟相位 CPHA = 1时

    当 CPHA=1时, MOSI 或 MISO 数据线上的信号将会在 SCK 时钟线的“偶数边沿” 被采样。根据时钟极性的不同,奇数边沿可能是上升沿也可能是下降沿。

    可以看到 CPHA=1时,采集数据是在偶数边沿,则转换数据是在奇数边沿。

    关于几个关键词的描述:

    (1)采集、采样:当采样时,主机要发给从机的数据已经在MOSI上了,从机要发送给主机的数据已经在MISO上了。可以去读取了。(什么时候准备数据的,下面说)

    (2)转换:因为SPI是全双工的,可以同时收发数据。主机在发送出一位数据,就会接收到一位数据。在同一个时钟周期同步转换。

             为什么叫转换?从下面图6中看到,两个设备都有一个shift寄存器。比如当CPHA=0时,在第一个边沿采集到了数据,在第二个边沿转换,

            主机的shift寄存器的最高位移到了从机的shift寄存器的最低位。两个寄存器都左移,循环8次。就能完成8位数据的交换,主机shift寄存器的数据都移到从记得shift寄存器里面了。从机的也都移到主机里了。(很巧妙 用互相转移完成读写同步)

                   

    图6 shift register

    4.2.为什么需要4种模式 以及什么时候采集数据,什么时候发送数据

    模式0:CPOL=0,CPHA =0  SCK空闲为低电平,数据在SCK的上升沿被采样(提取数据)
    
    模式1:CPOL=0,CPHA =1  SCK空闲为低电平,数据在SCK的下降沿被采样(提取数据)
    
    模式2:CPOL=1,CPHA =0  SCK空闲为高电平,数据在SCK的下降沿被采样(提取数据)
    
    模式3:CPOL=1,CPHA =1  SCK空闲为高电平,数据在SCK的上升沿被采样(提取数据)

    简单;来说就是有些外设在被选中后(CS被拉低)就会把数据放到总线上,让主机区读。这个时候用CPHA=0模式。而有些在设备发送数据前需要一个SCK时钟来激活外设,外设在接收到第一个始终后才会把数据放到总线上。这个时候用CPHA=1模式。

    CPHA=0时:主机从机给总线上准备数据,读写,转移的过程(从摩托罗拉SPI协议4.4.2 翻译过来的,大概翻译,不完全)

               

    (1)时钟的第一个边沿发生时,MOSI、MISO上的数据将被采集并被锁存,在第一个时钟边沿发生之前,必须有个CS被拉低的信号(设备必须使能),从CS被拉低。到第一个时钟边沿发生,需要有个延时。为什么需要延时?因为在CS被拉低到出现第一个边沿这个过程中,被使能的从机设备将会把自己的数据放在MISO上让主机去读。主机把自己的数据放在MOSI上让从机去读。

    (2)当时钟的第二个边沿发生时,MISO、MOSI上被采集并锁存的数据将会转移。(交换一位,参考图6)

    (3)第二个时钟沿发生后,从机的下一位数据将会被发送到MISO上,主机上,我们需要主动把数据放在MOSI上。这个也就是说的在奇数边沿采集,在偶数边沿转移。我们可以看到发送8位数据,需要8个时钟周期,16个时钟边沿。

    (4)在第16个时钟边沿发生后,主机的shift寄存器和从机的shift寄存器的数据就完全交换了。

    CPHA=1时:主机从机给总线上准备数据,读写,转移的过程(从摩托罗拉SPI协议4.4.3 翻译过来的,大概翻译,不完全)

            

     (1)在奇数数边沿转移,在偶数边沿采集。按照权威的文档中说,有些设备需要得到一个时钟后才会把数据放到MISO线上。不像上面介绍的设备一旦CS选中就发数据。

     (2)这种设备在第一个边沿先转移一下,用来激活设备,设备才会把数据放到MISO线上。在第二个边沿采集,又回到第一个边沿转移,设备准备数据到MISO上,第二个边沿采集,循环。

    这里还有点疑惑。先记录一下吧。是不是只第一次读取从设备的数据时需要一个时钟激活,激活后,一旦发生转移,从设备的下一位数据就自动就绪了。可能是第一个时钟转移(激活),转移后自动准备下一位数据。我觉得可能是第二种,只要一个时钟激活就可以了。第一个时钟激活,第一个时钟转移,那转移一次数据,从设备就会被激活。

        

    4.3 什么时候用哪种模式?

    时钟相位CPHA = 0。这种模式适合那种从设备一旦被片选后就输出数据到MISO线上。

    时钟相位CPHA = 0。这种模式适合那种从设备被片选后还需要一个时钟才能 输出数据到MISO线上。

    4.4软件模拟

    转自内陆的咸水鱼

      1 /* CPOL = 0, CPHA = 0, MSB first */
      2 uint8_t SOFT_SPI_RW_MODE0( uint8_t write_dat )
      3 {
      4     uint8_t i, read_dat;
      5     for( i = 0; i < 8; i++ )
      6     {
      7         if( write_dat & 0x80 )
      8             MOSI_H;  
      9         else                    
     10             MOSI_L;  
     11         write_dat <<= 1;
     12         delay_us(1);    
     13         SCK_H; 
     14         read_dat <<= 1;  
     15         if( MISO ) 
     16             read_dat++; 
     17         delay_us(1);
     18         SCK_L; 
     19         __nop();
     20     }
     21     
     22     return read_dat;
     23 }
     24  
     25  
     26 /* CPOL=0,CPHA=1, MSB first */
     27 uint8_t SOFT_SPI_RW_MODE1(uint8_t byte) 
     28 {
     29     uint8_t i,Temp=0;
     30  
     31     for(i=0;i<8;i++)     // 循环8次
     32     {
     33         SCK_H;     //拉高时钟
     34         if(byte&0x80)
     35         {
     36             MOSI_H;  //若最到位为高,则输出高
     37         }
     38         else      
     39         {
     40             MOSI_L;   //若最到位为低,则输出低
     41         }
     42         byte <<= 1;     // 低一位移位到最高位
     43         delay_us(1);
     44         SCK_L;     //拉低时钟
     45         Temp <<= 1;     //数据左移
     46  
     47         if(MISO)
     48             Temp++;     //若从从机接收到高电平,数据自加一
     49         delay_us(1);
     50  
     51     }
     52     return (Temp);     //返回数据
     53 }
     54  
     55 /* CPOL=1,CPHA=0, MSB first */
     56 uint8_t SOFT_SPI_RW_MODE2(uint8_t byte) 
     57 {
     58     uint8_t i,Temp=0;
     59  
     60     for(i=0;i<8;i++)     // 循环8次
     61     {
     62         if(byte&0x80)
     63         {
     64             MOSI_H;  //若最到位为高,则输出高
     65         }
     66         else      
     67         {
     68             MOSI_L;   //若最到位为低,则输出低
     69         }
     70         byte <<= 1;     // 低一位移位到最高位
     71         delay_us(1);
     72         SCK_L;     //拉低时钟
     73         Temp <<= 1;     //数据左移
     74  
     75         if(MISO)
     76             Temp++;     //若从从机接收到高电平,数据自加一
     77         delay_us(1);
     78         SCK_H;     //拉高时钟
     79         
     80     }
     81     return (Temp);     //返回数据
     82 }
     83  
     84  
     85 /* CPOL = 1, CPHA = 1, MSB first */
     86 uint8_t SOFT_SPI_RW_MODE3( uint8_t write_dat )
     87 {
     88     uint8_t i, read_dat;
     89     for( i = 0; i < 8; i++ )
     90     {
     91         SCK_L; 
     92         if( write_dat & 0x80 )
     93             MOSI_H;  
     94         else                    
     95             MOSI_L;  
     96         write_dat <<= 1;
     97         delay_us(1);    
     98         SCK_H; 
     99         read_dat <<= 1;  
    100         if( MISO ) 
    101             read_dat++; 
    102         delay_us(1);
    103         __nop();
    104     }
    105     return read_dat;
    106 }
    107  

    参考资料

    文中图片来自野火资料

    软件模拟SPI接口程序代码(4种模式) https://blog.csdn.net/zwj695535100/article/details/107303648/

    摩托罗拉SPI总线协议规范  https://wenku.baidu.com/view/eb052d1289eb172dec63b700.html(4.4章节)

    https://www.cnblogs.com/fedorayang/p/8564792.html

    https://www.sohu.com/a/270796067_288206

  • 相关阅读:
    mouseover、mouseout,mouseenter、mouseleave区别
    第一篇博客,就真的是随笔,写写最近的状况。
    MySQL日期时间函数大全 转
    解决Eclipse中SVN版本信息不显示的问题
    android 环境变量配置,以及sdcard配置
    服务器Out of Memory
    Android SDK Manager 更新时的“https://dl-ssl.google.com refused”错误
    不可变的原始值和可变的对象引用
    null和undefined
    HTML 表单
  • 原文地址:https://www.cnblogs.com/1024E/p/13322892.html
Copyright © 2020-2023  润新知