
一、简介1、SPI(Serial Peripheral Interface串行外设接口由Motorola公司开发的一种高速同步串行数据总线近距离板内芯片通信首选(MCU↔Flash、LCD、传感器、ADC。同步拥有独立时钟线收发频率不受晶振偏差影响。全双工同一时刻可以同时发、同时收数据I2C半双工、UART异步。支持总线挂载多设备一主多从不支持多主机缺点无内置应答机制、无硬件地址需要片选区分设备。SPI支持SPI1(APB2)、SPI2(APB1)2、硬件--标准4线SPI1SCLK(SCK)串行时钟主机输出驱动数据移位2MOSI主机输出→从机输入Master Out Slave In3MISO从机输出→主机输入Master In Slave Out4SPI支持SPI1(APB2)、SPI2(APB12)。5NSS/CS片选信号低电平有效居多6STM32F103系列引脚分配外设SCK时钟MOSI主机发/从机收MISO主机收/从机发NSS片选总线时钟SPI1PA5PA7PA6PA4APB2最大36MSPI2PB13PB15PB14PB12APB2最大18M3、主从架构1主机Master产生SCLK时钟控制通信发起MCU一般作为主机2从机Slave):跟随SCLK收到时钟移位数据3一条SPI总线可以挂载多个从设备依靠CS片选区分总线不能自动寻址SPI没有设备地址想要切换设备必须先拉低对应CS拉高其他CS。二、SPI四大模式CPOLCPHA1、CPOL(Clock Polarity时钟极性空闲状态无通信时SCLK电平CPOL0空闲SCLK低电平CPOL1空闲SCLK高电平2、CPHAClock Phase时钟相位数据采样时刻CPHA0第一个时钟边沿采样数据CPHA1第二个时钟边沿采样数据3、4种常用模式模式CPOLCPHA说明应用场景Mode100最常用WQ25 Flash、LCDMode201较少部分ADCMode310较少部分射频芯片Mode411较常用OLED、部分屏幕三、SPI通信时序原理1、移位寄存器交换机制主机和从机内部各有一个8bit移位寄存器每来一个SCLK脉冲主机移出1bit→MOSI发给从机从机移出1bit→MISO发给主机SPI本质数据交换只要发数据必然同时收数据。全双工移位寄存器通信写DR寄存器时就开始收发发1字节同时收1字节。SPI没有单独只读函数读读数据也要发送哑字节0xFF;即主机写DR→移位寄存器移出数据到MOSI同时MISO移入数据等待TXE、RXNE标志读DR得到收到的数据。2、数据位序MSB先行绝大多数器件都是高位先传MSB First。3、CS时序通信前CS拉低持续通信期间CS保持低一组数据传输完成后CS拉高很多芯片要求CS拉高后才能锁存收到的数据连续传输不要频繁翻转CS。四、通信速率SCLK时钟由主机提供常见范围几十KHz到几十MHz。限制因素PCB走线长度、芯片最高支持频率比如OLED一般10MHz。五、SPI硬件架构两种方式1、硬件SPIMCU内置SPI外设芯片外设自动生成SCLK、移位、CPU只需要读写DR数据寄存器。优势速度高、不占用CPU支持DMA。缺点固定引脚、资源有限。2、软件SPIGPIO模拟SCLK、MOSI、MISO。任意GPIO引脚软件循环翻转电平模拟时序。优点引脚自由无硬件通道限制。缺点占用CPU速率低时序受代码影响高频不稳定。六、SPI vs UART vs I2C特性SPII2CUART连线4线2线2线通信方式同步全双工同步半双工异步全双工寻址CS片选、无地址7/10位设备地址点对点速度最高几十MHz最高400KHz波特率1MHz应答无硬件ACK自带ACK无七、常见问题1、SPI是交换数据只读必须发送0xFF哑字节。2、MISO主机是输入严禁配置为推挽输出。3、CS片选极性问题。八、标准库SPI结构体SPI_InitTypeDefuint16_t SPI_Mode主机/从机宏定义uint16_t SPI_Direction全双工/半双工/单工宏定义uint16_t SPI_DataSize8/16bit一般选8bit宏定义uint16_t SPI_CPOL时钟极性宏定义uint16_t SPI_CPHA时钟相位宏定义uint16_t SPI_NSSNSS软件/硬件模式宏定义uint16_t SPI_BaudRatePrescaler波特率分频uint16_t SPI_FirstBit;MSB先行/LSB先行uint16_t SPI_CRCPolynomialCRC校验不用就随便写7九、SPI软件模拟1、模拟SPI4根线SCK、MOSI主机输出、MISO主机输入、CS片选STM32F103系列SCK推挽输出GPIO_Mode_Out_PPMOSI推挽输出GPIO_Mode_Out_PPCS推挽输出GPIO_Mode_Out_PPMISO上拉输入GPIO_Mode_IPU是flash发给STM32主机只读不能配为输出如果配为浮空输入外部干扰易读到乱码。2、Mode0时序片选CS拉低→先放MOSI数据→SCK拉高数据→读MISO→SCK拉低。1先放MOSI数据准备阶段前提主机先将片选信号(CS)拉低激活从机。在SCK产生第一个上升沿之前主机必须把第一位数据通常是最高位MSB稳定放MOSI线上。从机也会同步把第一位数据放在MISO线上。2SCK拉高数据锁存阶段SCK产生上升沿在这个瞬间主从机同时读取对方数据线上的电平状态并锁存到各自的移位寄存器中。3读MISO接收阶段主机内部硬件自动将MISO线上刚锁存的状态读入完成1位数据的接收。4SCK拉低切换阶段SCK产生下降沿主机和从机同时改变各自数据线的电平准备下一位数据然后流程回到第2步直到8位数据传完。总结放数据→拉高采样→读数据→拉低换数据。也就是数据在SCK为低电平时准备好在上升沿被读取。3、代码部分MySPI.c文件#include stm32f10x.h void MySPI_W_SS(uint8_t BitValue) { GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)BitValue); } void MySPI_W_SCK(uint8_t BitValue) { GPIO_WriteBit(GPIOA,GPIO_Pin_5,(BitAction)BitValue); } uint8_t MySPI_R_MISO() { return GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6); } void MySPI_W_MOSI(uint8_t BitValue) { GPIO_WriteBit(GPIOA,GPIO_Pin_7,(BitAction)BitValue); } void MySPI_Init() { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_PinGPIO_Pin_4|GPIO_Pin_5 |GPIO_Pin_7; GPIO_InitStructure.GPIO_SpeedGPIO_Speed_50MHz ; GPIO_Init(GPIOA, GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; GPIO_InitStructure.GPIO_PinGPIO_Pin_6; GPIO_InitStructure.GPIO_SpeedGPIO_Speed_50MHz ; GPIO_Init(GPIOA, GPIO_InitStructure); MySPI_W_SCK(0); MySPI_W_SS(1); } void MySPI_Start() { MySPI_W_SS(0); } void MySPI_Stop() { MySPI_W_SS(1); } uint8_t MySPI_SwapByte(uint8_t SendByte) { uint8_t i; uint8_t ReceiveByte 0x00; for(i 0;i8;i) { MySPI_W_MOSI(SendByte (0x80i)); MySPI_W_SCK(1); if(MySPI_R_MISO()1) { ReceiveByte|(0x80i); } MySPI_W_SCK(0); } return ReceiveByte ; }2、MySPI.h文件#ifndef __MYSPI_H #define __MYSPI_H void MySPI_Init(); void MySPI_Start(); void MySPI_Stop(); uint8_t MySPI_SwapByte(uint8_t SendByte); #endif本文完