A.SPI特性
1.SPI一共有四根线,一根片选线CS,一根时钟线SCK,两根信号线MOSI或者是MISO
2.SPI的通信速率很快,不同舍设备可以达到10M+
3.通信方式
最大的特点使用主从模式,在总线上移动存在主设备和从设备,主设备发送数据,从设备接收数据。主从设备的通信信号,NSS,当主设备要发送数据时,将设一个引脚的电平拉低,从设备的对应NSS引脚接收低电平信号将会进入从设备接收模式。如果不通过硬件相连接NSS,也可以在使用软件方式来内部驱动NSS进入对应主从模式。
注意SPI的两个信号引脚MISO和MOSI,M代表Main,S代表Slave,在主从模式同一引脚并不都为输入获都为输出而是对应交换的。
主设备的SCK输出同步时钟到从设备上面,作为外部时钟驱动。
4.一些关键的设置:
第一、CPOL位设置,在SPI_CR1寄存器中,它决定了在空闲状态下SCK的电平状态。CPOL=1 表示空闲状态下是高电平。
第二、CPHA设置,时钟相位设置,设置在在SCK时钟的第几个时钟边沿进行采样,当CPHA=1时在第二个边沿进行数据采样。
第三、数据帧的格式,SPI_CR1的LSBFIRST设置MSB还是LSB在前;DFF位设置数据时16位还是8位的,SPI的数据寄存器是16位的根据设置自动无额定使用8位或16位。
第四、波特率设置,SPI_CR1中BR[2:0]设置对挂载的时候输入的分频
第五、主从模式设置,设置设置SPI_CR1寄存器的SSM位和SSI位设置SPI的NSS是工作的软件模式或是硬件模式
第六、设置工作模式,有全双工模式、单向只接收获只发送模式。设置SPI_CR1寄存器中的BIDIMODE等。
5.发送方式与中断
a.发送前应该先等待TXE 标志( 发送缓冲器空),表名上一帧数据 已经发送完毕;SPI_CR2寄存器中设置了TXEIE 位是对应的外设中断使能位,如果在打开了NVIC中的CPU中断通道,将会将进入中断,可以采用这种终端的发送方式
b.与上面类似,读数据前应该先等待RXNE 标志置位,表示接收缓冲器非空,可以读取数据,硬件自动清除标志位,如果使能对应RXNEIE,及打SPI开接收中断,如果在使能CPU级别的中断将会进入中断服务程序。
当然也可采用查询的方式来发送或接收。
另外如果需要尖山哦CPU开销,同时提高SPI的发送速率,还可以采用DMA来接管SPI的数据发送和接收,保证总线上通信的连续性。
6.CRC错误校验,CRCEN位启用校验,它会在数据发送完成之后,自动发送一个CRC校验值,从机检查与自己的SPI_TXCRCR数值是否匹配来判断是否有发送错误。
主模式失效事件 MODF ,溢出错误 OVR ,CRC错误标志 CRCERR,对应了一个错误中断ERRIE 通道。
7.软件配置:
第一步开启GPIO的复用:
SPI3的复用引脚:
SPI2的引脚复用:
先挂载GPIO时钟,使能SPI时钟,配置GPIO复用,将输出引荐NSS,SCK,MOSI,MISO,这厮个引脚在SPI通信中都可能出现双向的信号,应该配置成推挽输出。
初始化和读写函数如下:
void Init_SPI()
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE );
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI2, &SPI_InitStructure);
SPI_Cmd(SPI2, ENABLE);
}
u8 read_data()
{
u8 ch;
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) //查询发送器是否为空 {
ch = SPI_I2S_ReceiveData(SPI2);
}
return ch;
}
void send_data(u8 ch)
{
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) //查询接收器是否非空
{
SPI_I2S_SendData(SPI2,ch);
}
}
上面的发送方式是采用查询的方式,在想要发送数据的时候调用。
如果想要周期性的发送,或者采用中断发送,可以在想要发送的时候设置使能TXE的中断,变回当TXE位置位以后进入发送中断。
也可以打开接收中断使能,这样当外部有数据到达时,便会自动进入到中断,这是可以对接收到的数据进行处理。
如果想要周期性的发送,或者采用中断发送,可以在想要发送的时候设置使能TXE的中断,变回当TXE位置位以后进入发送中断。
也可以打开接收中断使能,这样当外部有数据到达时,便会自动进入到中断,这是可以对接收到的数据进行处理。
总的来看,SPI通信的使用方法跟SCI是非常像的,所以通信协议的制定可以参考一起拿使用SCI时的方式,一般来讲多用SPI于外部器件如Flash,EEPROM,AD等通信,SPI的速度快,简单易操作,非常常用。