当前位置:首页 > 公众号精选 > 漫长当下

首先SPI通信有四种模式,由时钟相位(CPHA)和时钟极性(CPOL)的不同配置组合而成,当CPOL=0时,时钟空闲状态为低电平;当CPOL=1时,时钟空闲状态为高电平。当CPHA=0时,在时钟的第一个跳变沿(上升或下降)数据被采样;当CPHA=1时,在时钟的第二个跳变沿(上升或下降)数据被采样。主机和从机的配置应该是一致的。

下面两张图是从STM32F103的参考手册截取的,结合图便可以理解前面的内容。


图1


图2


以模式0(即CPOL=0,CPHA=0)为例,对应图2,看下主机端的代码该怎么写,此处忽略引脚初始化代码,仅讲解发送和接收的代码,先来看发送,代码如下:

/*software spi write byte*/
void SpiWriteByte_Software(uint8_t c)
{
    //片选
    SPI_NSS_L;

    //MSB First
    for(uint32_t i = 0;i < 8; i++)
    {
        /*主机准备数据*/
        if(c & (uint8_t)0x80)
        {
            SPI_MOSI_H;
        }
        else
        {
            SPI_MOSI_L;
        }
        //获取下一个传输的bit
        c <<= 0x1UL;

        //拉高时钟,从机在上升沿读取主机数据
        SPI_CLK_H;
        //主机拉低时钟
        SPI_CLK_L;
    }

    //去片选
    SPI_NSS_H;
}

发送的时候我们只需要关心时钟信号、MOSI、NSS这三根线,在发送数据前先拉低片选(低有效),告诉从机准备好接收数据,主机将要发送的数据以高位先行的方式从MOSI线逐位移出,准备好第一个bit位后,拉高时钟线,从机在时钟的上升沿读取MOSI线的状态,读取后即得到了第一个bit位,拉低时钟线,主机准备第二个bit位,以此循环直到发送完所有数据。

理解了发送的过程,那么接收也就简单了,代码如下:

/*software spi read byte*/
uint8_t SpiReadByte_Software(void)
{
    uint8_t retData = 0;

    //片选
    SPI_NSS_L;

    for(uint32_t i = 0;i < 8; i++)
    {
        //拉高时钟,
        SPI_CLK_H;

        if(SPI_MISO)
        {
            retData |= ((uint8_t)0x80 >> i);
        }
        else
        {
            retData &= (~((uint8_t)0x80 >> i));
        }

        SPI_CLK_L;
    }

    //去片选
    SPI_NSS_H;

    return retData;
}

接收过程和发送过程不一样的就是把关心的MOSI换成了MISO而已,在接收数据前先拉低片选,告诉从机可以发送数据了,从机就会将bit数据准备好,主机拉高时钟,读取MISO的状态即可得到从机发送的第一个bit位,然后拉低时钟,告诉从机准备下一个传输的bit位,从机准备好后,主机再次拉高时钟,读取MISO状态,以此循环接收完所有数据。

上面的过程是发送和接收分离的,SPI协议是可以工作在全双工模式的,发送数据的同时也可以接收数据,只需要将发送和接收合并一下就行了。

将程序结合时序图,更加容易理解,当你理解了SPI协议,那么I2C和UART的通信模拟也是非常简单的,主要就是能看懂时序。

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
关闭