1.SPI协议
SPI(Serial Peripheral interface)串行外围设备接口,是同步全双工的通信总线,在芯片的管脚上最多只占用四根线。
1.1 物理层
SPI接口是Motorola 首先提出的全双工三线/四线同步串行外围接口,采用主从模式(Master Slave)架构。时钟由Master控制,在时钟移位脉冲下,数据按位传输,高位在前,低位在后(MSB first);SPI接口有2根单向数据线,为全双工通信。 SPI总线被广泛地使用在FLASH、ADC、LCD等设备与MCU间,要求通讯速率较高的场合。
SS/NSS/CS
:从设备选择信号线(片选信号线)。由主设备控制,选择指定的从设备。
当主机要选择从设备时,把该从设备的SS信号线设置为低电平,该从设备即被选中,即片选有效,接着主机开始与被选中的从设备进行SPI通讯。所以SPI通讯以SS线置低电平为开始信号,以SS线被拉高作为结束信号。
SCK (Serial Clock):时钟信号线。用于通讯数据同步,只能由主设备产生,两个设备之间通讯时,通讯速率受限于低速设备
MOSI(Master Output, Slave Input):主设备输出/从设备输入引脚。该引脚在主模式下发送数据,在从模式下接收数据。
MISO(Master Input, Slave Output):主设备输入/从设备输出引脚。该引脚在从模式下发送数据,在主模式下接收数据。
1.2 协议
在时钟信号控制下,主机将要发送的数据写到数据缓存区(Memory),缓存区经过8位移位寄存器,移位寄存器通过MOSI信号线将数据一位一位的移到从机,从机将MISO接口收到的数据经过移位寄存器一位一位的移到数据缓存区(Memory)。同时从机也将自己移位寄存器数据通过MOSI发送给主机,两个移位寄存器数据完成交互,读写同时进行。
因此,SPI读写操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;若主机要读取从机的一个字节,就必须发送一个空字节引发从机传输。
1.2.1 时钟极性与相位极性
1.2.1 时钟极性与时钟相位(对应着SPI的4个模式)
- 时钟极性CPOL:指SPI通讯设备处于空闲状态时,SCK信号线的电平信号(即SPI通讯开始前、 CS片选线为高电平时SCK的状态)。
CPOL = 0:SCK在空闲状态时为低电平
CPOL = 1 :SCK在空闲状态时为高电平
- 时钟相位CPHA:指数据的采样的时刻
CPHA = 0:MOSI/MISO数据线上的信号将会在SCK时钟线的“奇数边沿”被采样(第一个跳变沿开始)
CPHA = 1:MOSI/MISO数据线上的信号将会在SCK时钟线的“偶数边沿”被采样(第二个跳变沿开始)
模式 | CPOL | CPHA | CLK空闲 | 采样时刻 |
0 | 0 | 0 | 低电平 | 第一个边沿(奇) |
1 | 0 | 1 | 低电平 | 第二个边沿(偶) |
2 | 1 | 0 | 高电平 | 第一个边沿(奇) |
3 | 1 | 1 | 高电平 | 第二个边沿(偶) |
由CPOL
及CPHA
的不同状态,SPI分成了四种模式,主机与从机必须工作在相同的模式下才可以正常通讯,实际中采用较多的是“模式0”与“模式3”。
2.CubeMx初始化
2.1 硬件设置
2.1.1 模式配置
- 全双工主机/从机模式
- 办双工主机/从机模式
- 只接收主机/从机模式
- 只发送主机/从机模式
2.1.2 硬件NSS信号
- 不使能
- NSS输入信号
- NSS输出信号
2.1.3 基本参数
- Frame Format帧格式:Motorola摩托罗拉 或 TI
- Data size数据大小:8位 或 16位
- First Bit:MSB/LSB先行
2.1.4 时钟参数
- 波特率分配因子:总线频率/分频比 = SPI波特率
2.1.5 极性配置
- CPOL时钟极性:low or high
- CPHA时钟相位:1 edge or 2 edge
2.1.6 高级参数
- CRC循环校验:使能/失能
- NSS信号类型:软件
3 MX SPI库函数
void MX_SPI1_Init(void)
{
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER; //主机模式
hspi1.Init.Direction = SPI_DIRECTION_2LINES; //双向全双工
hspi1.Init.DataSize = SPI_DATASIZE_8BIT; //8位数据宽度
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; // CPOL=0, CLK极性为低电平
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; // CPHA=0, CLK相位为第一个边沿采样
hspi1.Init.NSS = SPI_NSS_SOFT; //软件片选信号
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256; //分频比256 波特率=总线频率/256
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; //最高位在前
hspi1.Init.TIMode = SPI_TIMODE_DISABLE; // 不使用TI模式
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; //不使用CRC校验
hspi1.Init.CRCPolynomial = 7; //CRC多项式为7
hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
hspi1.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
Error_Handler();
}
}
发送,接收,发送接收函数
HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size, uint32_t Timeout);
如果需要用到中断或者DMA,则使用相应的函数即可
结束,由于作者水平有限,欢迎读者指正。