SPI通信简介SPI是由Motorola公司开发的一种通用数据总线四根通信线SCK、MOSI、MISO、SS同步全双工支持总线挂载多设备一主多从SPI时序SPI总工有四种工作模式SCK默认低电平当SS第一个下降沿的时候MOSI和MISO移出数据在SCK的上升沿移入数据如此循环八次完成已发个字节的交换所以SPI的通信其实就是交换数据。如图可知主机想要给从机发送0xAA交换一个字节后主机接收到的是0x55从机则接收到0xAA我们如果只想接收数据就随便发送一个数据给从机只想发送数据实现某种功能那么从机交换过来的数据我们可以不读。SPI读写W25Q64SPI硬件外设配置SPI基本结构初始化配置模板void MySPI_Init(void) { //配置成SPI模式0 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);//SPI1是APB2的外设 //PA4配置为推挽输出 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_ModeGPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_PinGPIO_Pin_4; GPIO_InitStructure.GPIO_SpeedGPIO_Speed_50MHz; GPIO_Init(GPIOA,GPIO_InitStructure); //配置SCK和MOSI为复用推挽输出 GPIO_InitStructure.GPIO_ModeGPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_PinGPIO_Pin_5|GPIO_Pin_7; GPIO_InitStructure.GPIO_SpeedGPIO_Speed_50MHz; GPIO_Init(GPIOA,GPIO_InitStructure); //配置MISO为上拉输入模式 GPIO_InitStructure.GPIO_ModeGPIO_Mode_IPU; GPIO_InitStructure.GPIO_PinGPIO_Pin_6; GPIO_InitStructure.GPIO_SpeedGPIO_Speed_50MHz; GPIO_Init(GPIOA,GPIO_InitStructure); //初始化SPI SPI_InitTypeDef SPI_InitStructure; SPI_InitStructure.SPI_ModeSPI_Mode_Master;//SPI模式 SPI_InitStructure.SPI_DirectionSPI_Direction_2Lines_FullDuplex;//选择双线全双工 SPI_InitStructure.SPI_DataSizeSPI_DataSize_8b;//8位数据帧 SPI_InitStructure.SPI_FirstBitSPI_FirstBit_MSB;//高位先行 SPI_InitStructure.SPI_BaudRatePrescalerSPI_BaudRatePrescaler_128;//波特率分频器 SPI_InitStructure.SPI_CPOLSPI_CPOL_Low;//SCK默认低电平 SPI_InitStructure.SPI_CPHASPI_CPHA_1Edge;//相当于选择CPHA为0第一个边缘采样移入 SPI_InitStructure.SPI_NSSSPI_NSS_Soft;//选择软件模拟NSS引脚 SPI_InitStructure.SPI_CRCPolynomial7;//CRC校验默认为7 SPI_Init(SPI1,SPI_InitStructure); SPI_Cmd(SPI1,ENABLE); }SPI1的SS对应PA4SCK对应PA5MISO对应PA6MOSI对应PA7片选引脚SS用软件模拟所以配置为通用推挽输出SCK和MOSI要交由片上外设控制所以配置为复用推挽输出MISO是输入端所以配置为普通的上拉输入配置SPI结构体一般都是按照上面的参数来配置SPI不同的模式差别也就在于时钟的相位是第几个边沿采样的区别本质上没有区别这里配置成模式0最后要严谨一点的话在初始化的最后还要将SS默认设置为高电平。最后封装常用的起始位停止位交换数据函数即可void MySPI_W_SS(uint8_t BitValue)//置SS电平高低 { GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)BitValue); } void MySPI_Start(void)//起始函数 { MySPI_W_SS(0); } void MySPI_Stop(void)//终止函数 { MySPI_W_SS(1); } uint8_t MySPI_SwapByte(uint8_t ByteSend)//交换数据 { //TXE为1TDR寄存器为空表示可以发送数据 while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)!SET); SPI_I2S_SendData(SPI1,ByteSend);//写DR会清除标志位 //等待RENE为1RDR寄存器非空表示可以读取接收到的数据 while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)!SET); return SPI_I2S_ReceiveData(SPI1);//读DR同理 }封装交换数据的函数时