您的位置:首页 > 汽车 > 新车 > 北京app搭建公司_网站策划的内容包含了什么?_外贸网站建设报价_淘宝培训

北京app搭建公司_网站策划的内容包含了什么?_外贸网站建设报价_淘宝培训

2025/4/15 11:20:08 来源:https://blog.csdn.net/qq_62637071/article/details/144694576  浏览:    关键词:北京app搭建公司_网站策划的内容包含了什么?_外贸网站建设报价_淘宝培训
北京app搭建公司_网站策划的内容包含了什么?_外贸网站建设报价_淘宝培训

一、SPI通信协议的介绍:

                                 

        SPI(Serial Peripheral Inertface):串行外设接口,是一种高速的、全双工、同步的通信总线。采用一主多从的模式,其中一个设备作为主机,其他设备作为从机,从机可以是一个也可以是多个。涉及到通信有四条线:

       MISO 主机输入从机输出:主机通过MOSI输入,从机通过MOSI输出。

       MOSI 主机输出从机输入:主机通过MOSI输出,从机通过MOSI输入。

       SCLK时钟线:完全由主机掌握,即由主设备产生,主要是产生时钟信号,其时钟信号的频率决定了传输的速度。对于主机来说时钟线为输出,对于从机来说,所有时钟线为输入。

       CS(SS)从设备片选信号:由主设备控制,低电平有效。主机选择从机时,将连接对应的ss线置0就可以选择此从机,主要确保只有选定的设备与主设备进行通信。相较于IIC,这种方法更简单但会浪费更多引脚,但无需IIC一样先进行寻址(SS线置0相当于寻址了)。

二、SPI的四种工作模式介绍

        SPI有四种工作模式,看起来是很复杂繁琐的,其实不然,SPI的工作模式主要是由时钟信号的相位和极性决定的,即由CPOL时钟极性和CPHA时钟相位决定。

        CPOL:时钟极性,决定空闲时SCK的状态,当CPOL=1时,SCK时钟线空闲状态下为高电平,反之CPOL=0时SCK空闲状态下为低电平。

        CPHA:时钟相位,决定第几个下降沿采样,当CPHA=1时,在SCK时钟线第二个跳变沿采样,反之CPHA=0时在SCK时钟线第一个跳变沿采样。

        由于CPOL和CPHA都有两种情况,如果将这两种结合起来也就是四种情况,也就构成了SPI的四种工作模式。

2.1 SPI模式0(CPOL = 0,CPHA = 0)

        CPOL = 0:空闲时是低电平,第1个跳变沿是上升沿,第2个跳变沿是下降沿

        CPHA = 0:数据在第1个跳变沿(上升沿)采样

2.2  SPI模式1(CPOL = 0,CPHA = 1)

        CPOL = 0:空闲时是低电平,第1个跳变沿是上升沿,第2个跳变沿是下降沿

        CPHA = 1:数据在第2个跳变沿(下降沿)采样

2.3  SPI模式2(CPOL = 1,CPHA = 0)

        CPOL = 1:空闲时是高电平,第1个跳变沿是下降沿,第2个跳变沿是上升沿

        CPHA = 0:数据在第1个跳变沿(下降沿)采样

2.4  SPI模式3(CPOL = 1,CPHA = 1)

        CPOL = 1:空闲时是高电平,第1个跳变沿是下降沿,第2个跳变沿是上升沿

        CPHA = 1:数据在第2个跳变沿(上升沿)采样

三、软件SPI程序的编写

3.1 引脚的初始化:

        对于其引脚的初始化,我们将输出引脚配置为推挽输出,输入引脚配置为浮空或者上拉输入。即:将SS、MOSI、SCK这三个由主机控制的配置为推挽输出,MISO配置为上拉输入。这里使用标准库SPI模式0来举例:

void my_spi_init(void)
{GPIO_InitTypeDef GPIO_InitStructure;//定义一个GPIO_InitTypeDef类型的结构体RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//开启GPIOB端口时钟GPIO_InitStructure.GPIO_Pin=MOSI_PIN | SCK_PIN | NSS_PIN; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//通用推挽输出GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//引脚速率50MHZGPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin=MISO_PIN; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;//上拉输入GPIO_Init(GPIOB, &GPIO_InitStructure);SPI_SCK(0);//根据自己的SPI模式来选择。
}

宏定义部分:不知道为什么BitAction强制转换成一位在我编译器中用不了导致一直通信失败,我们可以去掉也不会影响通信。

#define MISO_PORT       GPIOB
#define MISO_PIN        GPIO_Pin_14
#define MOSI_PORT       GPIOB
#define MOSI_PIN        GPIO_Pin_15
#define SCK_PORT        GPIOB
#define SCK_PIN         GPIO_Pin_13
#define NSS_PORT        GPIOB
#define NSS_PIN         GPIO_Pin_12#define SPI_MOSI(x)  MOSI_PORT->BSRR = MOSI_PIN<<(16*(!x))
#define SPI_SCK(x)   SCK_PORT->BSRR = SCK_PIN<<(16*(!x))
#define SPI_NSS(x)   NSS_PORT->BSRR = NSS_PIN<<(16*(!x))
//#define READ_MISO    (BitAction)(MISO_PORT->IDR & MISO_PIN)
#define READ_MISO    (MISO_PORT->IDR & MISO_PIN)

3.2 SPI交换一个字节的数据

        这里举例的为模式0,即SCK空闲时为低电平、第一个边沿采样,即上升沿采样。故在初始化中我们就将SCK置低电平。在交换字节函数里,由于是第一个边沿采样,所以我们的在SCK上升沿之前就将数据改变好放在数据线上方便数据交换(高位先行)。

uint8_t MySPI_SW_Byte(uint8_t Byte)
{uint8_t receive_byte = 0x00;for(uint8_t i=0;i<8;i++){SPI_MOSI((BitAction)(Byte & (0x80>>i)));
//如果通信失败可以换成下面注释掉的代码
//        if(Byte & (0x80>>i))
//        {    SPI_MOSI(1);        }
//        else
//        {   SPI_MOSI(0);        }SPI_SCK(1);//将时钟线拉高,采样数据。if(READ_MISO)receive_byte |= (0x80>>i);SPI_SCK(0);//拉低时钟线,方便数据改变}return receive_byte;
}

3.3 向指定寄存器写入数据

/***** SPI写一个字节的数据 *****/
uint8_t MySPI_WriteByte(uint8_t reg,uint8_t data)
{uint8_t status;SPI_NSS(0);                     //拉低片选,使能传输status = MySPI_SW_Byte(reg);    //发送寄存器值(位置),并读取状态值MySPI_SW_Byte(data);SPI_NSS(1);                     //拉高片选,失能传输return status;                  //返回读到的状态值
}

3.4 读取指定寄存器的数据

/***** SPI读一个字节的数据 *****/
uint8_t MySPI_ReadByte(void)
{uint8_t data;                   //用于存储读到的数据;SPI_NSS(0);                     //拉低片选,使能传输status = MySPI_SW_Byte(reg);    //发送寄存器值(位置)data = MySPI_SW_Byte(0xff);     //随便发一组无用数据来读出寄存器的数据SPI_NSS(1);                     //拉高片选,失能传输return data;                   //返回读到的数据值
}

3.5 写入指定长度数据到指定寄存器

/***********************************************************
函数名称:void MySPI_WriteLen(u8 reg,u8 *pBuf,u8 len)
函数功能:在指定位置写出指定长度的数据
入口参数:reg:指定寄存器地址  *pBuf:数据指针  len:数据长度
返回参数:status  此次读到的状态寄存器值
************************************************************/
uint8_t MySPI_WriteLen(u8 reg,u8 *pBuf,u8 len)
{uint8_t status;					//用于存放读到的寄存器状态SPI_NSS(0);                     //拉低片选,使能传输status = MySPI_SW_Byte(reg);    //发送寄存器值(位置)for(uint8_t i=0;i<len;i++){MySPI_SW_Byte(*pBuf++);		//发送要写入的数据}SPI_NSS(1);                     //拉高片选,失能传输return status;                  //返回读到的状态值
}

3.6  读取指定寄存器指定长度数据

/***********************************************************
函数名称:void MySPI_ReadLen(u8 reg,u8 *pBuf,u8 len)
函数功能:在指定位置读出指定长度的数据
入口参数:reg:指定寄存器地址  *pBuf:数据指针  len:数据长度
返回参数:status  此次读到的状态寄存器值
************************************************************/
uint8_t MySPI_ReadLen(u8 reg,u8 *pBuf,u8 len)
{uint8_t status;							//用于存放读到的寄存器状态SPI_NSS(0);                     		//拉低片选,使能传输status = MySPI_SW_Byte(reg);    		//发送寄存器值(位置),并读取状态值for(uint8_t i=0;i<len;i++){pBuf[i] = MySPI_SW_Byte(0xff);		//随便发一组无用数据来读出寄存器的数据}SPI_NSS(1);                     		//拉高片选,失能传输return status;                  		//返回读到的状态值
}

到这里就关于SPI部分代码写完了。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com