(一)全双工同步传输,硬件上强制性同时收发。只支持一主多从模式。
2.从设备(Slave):响应主设备的指令,在时钟信号控制下收发数据
(1)CPOL(Clock Polarity时钟极性):定义了时钟线在空闲状态(即CS有效但SCK还没开始跳变时)的电平。
(2)CPHA(Clock Phase时钟相位):定义了数据采样的时刻。
2.连接说明:独立片选,主机为每个从设备提供一条独立的CS信号线
①寻址:主机通过拉低目标从设备专属的 CS 线,来选中并激活该设备。
② 通信:主机与被唯一选中的从设备进行全双工SPI通信。其他从设备因 CS 为高电平而忽略总线活动。
2.连接说明:整个菊花链被看作一个大型的、分布式的移位寄存器。只有第一台从机的MISO是直接连接主机的MOSI,后面的从机都是连接前面的从机的MOSI。
②通信:主机产生 N × 数据位 个时钟脉冲(N为从机数量)。数据从主机的 MOSI 移入第一个从机。在后续的每个时钟脉冲,数据依次向后级从机传递,最后一个从机的数据移入主机的 MISO。
③结束:当主机拉高 CS 时,所有从机同时锁存并处理各自移位寄存器中的数据。
2.连接说明:由多个GPIO承担CS的功能,经过N-N²译码器后实现少量IO口控制多个从SPI设备。
②使能:译码器根据地址,将一条对应的输出线拉低(激活对应从设备的CS)。
③通信:主机与被唯一选中的从设备进行标准的全双工SPI通信。
④结束:主机拉高 CS(通过改变地址线),结束通信。以少量主机引脚(如3个)控制大量从设备(如8个),在节省引脚和通信灵活性之间取得平衡。
一、传输方式:
(一)全双工同步传输,硬件上强制性同时收发。只支持一主多从模式。
SPI设备具有移位寄存器,主机的发送和从机的接收都是通过移位最低位的数据进行的。所以主机发送了多少bit,从机也会向主机发送多少bit
无硬件应答机制、占用引脚较多(相比I2C)、无错误校验
=========================================================================
二、传输类型:
(一)主从式通信:
1.主设备(Master):负责生成时钟信号并控制通信时序
2.从设备(Slave):响应主设备的指令,在时钟信号控制下收发数据
一个SPI主设备可以连接多个从设备,通过片选信号选择要通信的特定从设备。片选信号默认高电平,当下拉成低电平时,从机就会接收主机的数据。
=========================================================================
三、引脚数:4
(一)CS/SS:
Slave Select,片选引脚。用于选择不同设备,每根片选线对应一个设备。
(二)SCK:
Serial Clock,时钟引脚。保证通信双方的使用同一频率时钟通信,完全由主机提供。
(三)MOSI:
Master Out Slave In,主机输出,从机输入。(主机输出的数据,输入给从机)
(四)MISO:
Master In Slave Out,主机输入,从机输出。(从机输出的数据,输入给主机)
=========================================================================
四、数据传输流程、方式
(一)通讯开始:
主机拉低SS/CS电平,表示选中目标从机。(SS/CS空闲高电平)
(二)通讯结束:
主机拉高SS/CS电平,表示结束通讯
(三)数据传输--主从设备的配置需要相同
相关参数有:高/低位先行,上/下边沿检测、上/下边沿修改数据
1.根据数据的采样时间和数据的改变时间,分有四种模式。
(1)CPOL(Clock Polarity时钟极性):定义了时钟线在空闲状态(即CS有效但SCK还没开始跳变时)的电平。
CPOL = 0:空闲时,时钟是低电平。
CPOL = 1:空闲时,时钟是高电平。
(2)CPHA(Clock Phase时钟相位):定义了数据采样的时刻。
CPHA = 0:在时钟的第一个边沿采样数据。
CPHA = 1:在时钟的第二个边沿采样数据。
| 模式 | CPOL | CPHA | 时钟空闲状态 | 数据采样时刻 | 数据移位时刻 |
|---|---|---|---|---|---|
| 0 | 0 | 0 | 低电平 | 时钟的上升沿 | 时钟的下降沿 |
| 1 | 0 | 1 | 低电平 | 时钟的下降沿 | 时钟的上升沿 |
| 2 | 1 | 0 | 高电平 | 时钟的下降沿 | 时钟的上升沿 |
| 3 | 1 | 1 | 高电平 | 时钟的上升沿 | 时钟的下降沿 |
(3)以模式0说明
初始状态:
SCLK 保持低电平(CPOL=0)。
主设备将待发送数据的最高位(MSB)放到MOSI线上。
从设备将其待发送数据的最高位(MSB)放到MISO线上。
此时数据线已经稳定。
第一个时钟上升沿(采样时刻):
主设备产生一个时钟上升沿。
在这个上升沿瞬间,主设备会读取(采样) MISO线上的电平(即从设备发来的bit),并将其移入自身移位寄存器的最低位(LSB)。
同样在这一瞬间,从设备会读取(采样) MOSI线上的电平(即主设备发来的bit),并将其移入自身移位寄存器的最低位(LSB)。
简单说:时钟上升沿是“收数据”的时刻。
第一个时钟下降沿(设置时刻):
主设备将时钟拉低。
在时钟变为低电平后,主设备和从设备会同时将各自移位寄存器中的下一个bit(原次高位)推到MOSI和MISO线上。
简单说:时钟下降沿之后是“准备新数据”的时刻。
重复:
上述过程重复8次(对于8位数据)。每一个时钟周期(一个上升沿+一个下降沿),主从设备都互相交换一个bit
传输完成:
8个时钟脉冲后,最初在主设备移位寄存器里的8位数据,已经全部移到了从设备的移位寄存器中。
同时,最初在从设备移位寄存器里的8位数据,也全部移到了主设备的移位寄存器中。
一次完整的SPI通信,总是同时完成数据的发送和接收
2.数据传输示意图

3.三个重要参数代码配置相关如下
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;//SPI的时钟极性,低
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;//SPI的时钟相位,第一边沿
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;//SPI的数据传输顺序,MSB先行
(1)SPI_CPOL:控制时钟空闲时的电平
①SPI_CPOL_Low:时钟空闲时 低电平
②SPI_CPOL_High:时钟空闲时 高电平
(2)SPI_CPHA:控制在时钟的哪个边沿采样数据
①SPI_CPHA_1Edge:第1个边沿进行数据采样
②SPI_CPHA_2Edge:第2个边沿进行数据采样
(3)SPI_FirstBit:控制是先发最高位还是最低位
①SPI_FirstBit_MSB:高位先行
②SPI_FirstBit_LSB:低位先行
=========================================================================
五、引脚配置
(一)输出引脚MOSI:推挽输出
推挽模式的高低电平都有驱动性,且电平变化快
(二)输入引脚MISO:上拉输入/浮空输入
=========================================================================
六、主从机连接示意图,及其数据传输方式说明
(一)独立片选
1.共用信号线:SCK、MOSI、MISO
2.连接说明:独立片选,主机为每个从设备提供一条独立的CS信号线
3.数据传输方式:
①寻址:主机通过拉低目标从设备专属的 CS 线,来选中并激活该设备。
② 通信:主机与被唯一选中的从设备进行全双工SPI通信。其他从设备因 CS 为高电平而忽略总线活动。
③结束:主机拉高 CS,结束与该从设备的本次通信。

———————————————————————————————————————————
(二)菊花链
1.共用信号线:SCK、CS
2.连接说明:整个菊花链被看作一个大型的、分布式的移位寄存器。只有第一台从机的MISO是直接连接主机的MOSI,后面的从机都是连接前面的从机的MOSI。
3.数据传输方式:
①启动:主机拉低 CS,启动对所有从机的同步通信。
②通信:主机产生 N × 数据位 个时钟脉冲(N为从机数量)。数据从主机的 MOSI 移入第一个从机。在后续的每个时钟脉冲,数据依次向后级从机传递,最后一个从机的数据移入主机的 MISO。
③结束:当主机拉高 CS 时,所有从机同时锁存并处理各自移位寄存器中的数据。

———————————————————————————————————————————
(三)片选译码器
1.共用信号线:MOSI、MISO、SCK。
2.连接说明:由多个GPIO承担CS的功能,经过N-N²译码器后实现少量IO口控制多个从SPI设备。
3.数据传输方式:
①寻址:主机在地址线上输出目标从设备的二进制地址。
②使能:译码器根据地址,将一条对应的输出线拉低(激活对应从设备的CS)。
③通信:主机与被唯一选中的从设备进行标准的全双工SPI通信。
④结束:主机拉高 CS(通过改变地址线),结束通信。以少量主机引脚(如3个)控制大量从设备(如8个),在节省引脚和通信灵活性之间取得平衡。

=========================================================================
七、STM32F103C8T6标准库代码(硬件SPI)
#include "spi.h"
#include "delay.h"
// SPI初始化 - 修改函数名
void RC522_SPI_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
// 使能GPIO和SPI时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE);
// 配置SPI引脚 (SCK->PA5, MISO->PA6, MOSI->PA7)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置CS引脚 (PA4)
GPIO_InitStructure.GPIO_Pin = RC522_CS_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(RC522_CS_PORT, &GPIO_InitStructure);
// 配置RST引脚 (PA0)
GPIO_InitStructure.GPIO_Pin = RC522_RST_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(RC522_RST_PORT, &GPIO_InitStructure);
// SPI1配置
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//SPI的工作模式,双线全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//SPI的工作模式,主模式
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;//SPI的数据大小,8位
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;//SPI的时钟极性,低
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;//SPI的时钟相位,第一边沿
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;//SPI的数据传输顺序,MSB先行
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;//SPI的片选方式,软件控制
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;//SPI的波特率预分频
SPI_InitStructure.SPI_CRCPolynomial = 7;
// 使用SPI_Init函数,传入参数
SPI_Init(SPI1, &SPI_InitStructure);
// 使能SPI1
SPI_Cmd(SPI1, ENABLE);
// 初始化引脚状态
RC522_CS_HIGH();
RC522_RST_HIGH();
}
// SPI读取一个字节
uint8_t SPI_ReadByte(void)
{
// 等待发送缓冲区空
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
// 发送虚拟数据以接收数据
SPI_I2S_SendData(SPI1, 0xFF);
// 等待接收缓冲区非空
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
// 返回接收到的数据
return SPI_I2S_ReceiveData(SPI1);
}
// SPI写入一个字节
void SPI_WriteByte(uint8_t data)
{
// 等待发送缓冲区空
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
// 发送数据
SPI_I2S_SendData(SPI1, data);
// 等待接收完成
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
// 读取接收到的数据(丢弃)
SPI_I2S_ReceiveData(SPI1);
}
7692

被折叠的 条评论
为什么被折叠?



