SPI通讯协议

📅 2026/7/12 18:07:48
SPI通讯协议
SPI通讯SCK串行时钟MOSI(主机输出从机输入)MISO(主机输入从机输出)SS从机选择这里的SCK就相当于I2C的SCL,MOSI和MISO合在一起就相当于I2C的SDA,不过不同的是这里发送数据和接收数据各用一条线互不影响SS就是给每个从机一条通信线这样就不用像I2C一样要涉及分配地址和寻址了。SPI硬件电路这里主机需要和哪个从机通信时就将从机的SS置低电平为了防止数据冲突主机只能选择一个从机通信其它从机SS保持高电平。这里输出引脚配置与I2C不同是推挽输出所以SPI的下降沿和上升沿非常迅速与I2C的上升缓慢下降迅速不同。移位示意图从这个图可以看出这里发送和接收数据都是高位先行而且与I2C不同的是这里发送和接收数据的方式是主机和从机互换数据所以当主机向从机发送一个0xAA数据时主机就会接收从机的0x55数据所以当我们只想向从机发送数据可以选择不读从机发送的数据而只能接收数据时可以发送0xFF的垃圾数据。SPI时序本节使用的是模式0,注意模式0的第一个上升沿就是移入数据。本节演示SPI读写W25Q64存储器分为易失性存储器和非易失性存储器易失性存储器SRAM,DRAM等非易失性存储器E2PROM,Flash等SPI硬件外设SPI基本结构TDR整体数据转入移位寄存器会置TXE标志位移位寄存器将整体数据转入RDR会置RXNE标志位。//包装SPI通讯引脚voidMySPI_W_SS(uint8_tBitValue){GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)BitValue);}voidMySPI_Init(void){RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_ModeGPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_SpeedGPIO_Speed_50MHz;GPIO_InitStructure.GPIO_PinGPIO_Pin_4;GPIO_Init(GPIOA,GPIO_InitStructure);GPIO_InitStructure.GPIO_ModeGPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_SpeedGPIO_Speed_50MHz;GPIO_InitStructure.GPIO_PinGPIO_Pin_5|GPIO_Pin_7;GPIO_Init(GPIOA,GPIO_InitStructure);GPIO_InitStructure.GPIO_ModeGPIO_Mode_IPU;GPIO_InitStructure.GPIO_SpeedGPIO_Speed_50MHz;GPIO_InitStructure.GPIO_PinGPIO_Pin_6;GPIO_Init(GPIOA,GPIO_InitStructure);SPI_InitTypeDef SPI_InitStructure;SPI_InitStructure.SPI_FirstBitSPI_FirstBit_MSB;//选择高位先行SPI_InitStructure.SPI_ModeSPI_Mode_Master;//指定当前设备为主机SPI_InitStructure.SPI_DataSizeSPI_DataSize_8b;//选择8位数据帧SPI_InitStructure.SPI_DirectionSPI_Direction_2Lines_FullDuplex;//选择全双工双线模式SPI_InitStructure.SPI_BaudRatePrescalerSPI_BaudRatePrescaler_128;SPI_InitStructure.SPI_CPHASPI_CPHA_1Edge;//选择第一个边沿开始采样SPI_InitStructure.SPI_CPOLSPI_CPOL_Low;//选择0模式SPI_InitStructure.SPI_NSSSPI_NSS_Soft;//选择软件NSSSPI_InitStructure.SPI_CRCPolynomial7;//CRC校验默认为7SPI_Init(SPI1,SPI_InitStructure);MySPI_W_SS(1);SPI_Cmd(SPI1,ENABLE);}voidSPI_Start(void)//配置起始条件{MySPI_W_SS(0);}voidSPI_Stop(void)//配置停止条件{MySPI_W_SS(1);}uint8_tSPI_SwapByte(uint8_tSendByte)//配置交换数据{while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)!SET);//等待发送标志位1SPI_I2S_SendData(SPI1,SendByte);//发送数据到DR寄存器while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)!SET);//等待接受标志位1returnSPI_I2S_ReceiveData(SPI1);//读取DR寄存器}SPI1的SS对应PA4SCK对应PA5MISO对应PA6MOSI对应PA7片选引脚SS用软件模拟所以配置为通用推挽输出SCK和MOSI要交由片上外设控制所以配置为复用推挽输出MISO是输入端所以配置为普通的上拉输入配置SPI结构体一般都是按照上面的参数来配置SPI不同的模式差别也就在于时钟的相位是第几个边沿采样的区别本质上没有区别这里配置成模式0最后要严谨一点的话在初始化的最后还要将SS默认设置为高电平。本节使用非连续传输。与全双工连续输出不同的是这里TDR寄存器将整体数据传到移位寄存器并置TXE标志位后不会立马将下一个数据放到TDR中而是等待移位寄存器将整体数据传入到RDR寄存器并将数据从RDR中读出后才将下一个数据传到TDR中。所以可以看到这里时钟和数据的时序中字节与字节间会产生一个间隙SCK的频率越大这个间隙影响越大。下面三个波形可以很明显的观察到