第十一章 SPI
11.1 SPI通信¶
11.1.1 SPI通信简介¶
- SPI(Serial Peripheral Interface)是由Motorola公司开发的一种通用数据总线
- 四根通信线:SCK(Serial Clock)、MOSI(Master Output Slave Input)、MISO(Master Input Slave Output)、SS(Slave Select)
- 同步,全双工
- 支持总线挂载多设备(一主多从,不支持多主机)
11.1.2 硬件电路¶
- 所有SPI设备的SCK、MOSI、MISO分别连在一起
- 主机另外引出多条SS控制线,分别接到各从机的SS引脚
- 输出引脚配置为 推挽输出 ,输入引脚配置为 浮空 或 上拉输入
关于SPI引脚:
- MOSI为主机发送从机接收;MISO则是主机接收从机发送;SCK为时钟线,与I2C的SCL功能一样,都是为了实现同步通信;SS控制线的作用是选择从机,哪根线为低电平哪根线对应的从机就与主机通信,且同一时间只能用一个从机的SS为低电平。
- 未被选择的从机对应的MISO线,应为高阻态,防止主机接收数据错乱。
- 有几个从机就开辟几条SS线。
11.1.3 SPI移位结构¶
SPI的移位原理:
当SPI同时接收和发送一个字节数据时,以高位先行为例(SPI硬件外设可以配置高位先行或低位先行);在第一个边沿,主机的最高位放到MOSI同时从机的最高位放到MISO,其他位一起往左移;在第二个边沿,主机的高位移到从机的低位,同时从机的高位移到主机的低位,依此八次仅完成一个字节数据的交换。
若SPI仅接收或发送时,忽略对方发送或接受的数据即可,一般这个无用的数据位0x00或0xFF。
11.1.4 SPI时基单元¶
- 起始条件: SS从高电平切换到低电平
- 终止条件: SS从低电平切换到高电平
SPI交换字节有多种模式可以选择,主要取决于CPOL(时钟极性)和CPHA(时钟相位),一个四种组合方式。
Danger
四种模式中,最常用的时模式 0 ,而最符合移位模型示例的是模式 1 ,主要学习一种常用的模式 0 就行了。
交换一个字节 (模式0)
- CPOL=0:空闲状态时,SCK为低电平
- CPHA=0:SCK第一个边沿移入数据,第二个边沿移出数据
Danger
由于SCK第一个边沿就要移入数据,所以在起始条件生成以后,MOSI和MISO就要立刻移位数据,从而不会使得SCK第一个边沿移入的数据为空。
交换一个字节 (模式1)
- CPOL=0:空闲状态时,SCK为低电平
- CPHA=1:SCK第一个边沿移出数据,第二个边沿移入数据
交换一个字节 (模式2)
- CPOL=1:空闲状态时,SCK为高电平
- CPHA=0:SCK第一个边沿移入数据,第二个边沿移出数据
交换一个字节 (模式3)
- CPOL=1:空闲状态时,SCK为高电平
- CPHA=1:SCK第一个边沿移出数据,第二个边沿移入数据
11.1.5 SPI时序¶
主要根据芯片手册要求。
11.2 软件模拟SPI¶
GPIO引脚配置如下,三个输出为推挽输出,一个输入为上拉输入。
sw_spi.c
C
#include "sw_spi.h"
void SPI_Start(void)
{
SS_L();
}
void SPI_Stop(void)
{
SS_H();
}
uint8_t SPI_SwapByte(uint8_t SendByte)
{
uint8_t i, ReceiveByte=0x00;
for (i=0; i<8; i++)
{
// 高位先行
MOSI_Write(SendByte & (0x80 >> i));
// 移入数据
SCK_H();
// 读取MISO,并与ReceiveByte交换位
if (MISO_Status == 1) {ReceiveByte |= MISO_Status;}
SCK_L();
}
return ReceiveByte;
}
sw_spi.h
C
#ifndef SW_SPI_SW_SPI_H
#define SW_SPI_SW_SPI_H
#include "gpio.h"
void SPI_Start(void);
void SPI_Stop(void);
uint8_t SPI_SwapByte(uint8_t SendByte);
/* 这里用一种不同于I2C模拟的函数定义方式,不要感觉这样多此一举,
* 很多商家给你的代码就有很多这样的定义,我们应当可以看懂
*/
#define MOSI_Write(BitValue) HAL_GPIO_WritePin(MOSI_GPIO_Port, MOSI_Pin, ((BitValue) ? GPIO_PIN_SET : GPIO_PIN_RESET))
#define SS_H() HAL_GPIO_WritePin(SS_GPIO_Port, SS_Pin, GPIO_PIN_SET)
#define SS_L() HAL_GPIO_WritePin(SS_GPIO_Port, SS_Pin, GPIO_PIN_RESET)
#define SCK_H() HAL_GPIO_WritePin(SCK_GPIO_Port, SCK_Pin, GPIO_PIN_SET)
#define SCK_L() HAL_GPIO_WritePin(SCK_GPIO_Port, SCK_Pin, GPIO_PIN_RESET)
#define MISO_Status HAL_GPIO_ReadPin(MISO_GPIO_Port, MISO_Pin)
#endif //SW_SPI_SW_SPI_H
11.3 SPI硬件外设¶
11.3.1 SPI外设简介¶
- STM32内部集成了硬件SPI收发电路,可以由硬件自动执行时钟生成、数据收发等功能,减轻CPU的负担
- 可配置8位/16位数据帧、高位先行/低位先行
- 时钟频率: fPCLK / (2, 4, 8, 16, 32, 64, 128, 256)
- 支持多主机模型、主或从操作
- 可精简为半双工/单工通信
- 支持DMA
- 兼容I2S协议
STM32F103C8T6 硬件SPI资源:SPI1、SPI2
11.3.2 SPI框图¶
11.3.3 SPI基本结构¶
这里的数据控制器主要时用于与LSBFIRST寄存器配合,实现SPI低位或高位先行的配置。