当前位置: 首页 > news >正文

stm32——SPI协议

stm32——SPI协议


STM32的SPI(Serial Peripheral Interface,串行外设接口)协议是一种高速、全双工、同步的串行通信协议,广泛评估微控制器与各种外设(如传感器、器件、显示器、模块等)之间的数据传输。STM32微控制器内置的SPI外设具有高度可配置性,支持多种操作模式和数据格式。

1. SPI协议概述

  • 主从架构(Master-Slave Architecture):SPI通信采用主从模式。通常有一个主设备(Master)和多个从设备(Slave)。主设备负责发起通信、提供时钟信号,并选择通信的从设备。从设备在被选中时响应主设备的请求。
  • 同步通信(Synchronous Communication):数据传输由一个共享的时钟信号(SCK)同步。主设备产生并控制SCK信号。
  • 全双工(Full-Duplex):SPI支持同时进行数据发送和接收,即发送和接收通道是独立的。

2. SPI的四根基本信号线

典型的SPI通信使用四根信号线:

  1. SCK (Serial Clock):串行时钟线。由主设备生成,用于同步主设备和从设备之间的数据传输。
  2. MOSI (Master Out Slave In):主设备输出,从设备输入。主设备通过此线向从设备发送数据。
  3. MISO(Master In Slave Out):主设备输入,从设备输出。从设备通过此线向主设备发送数据。
  4. SS/ CS (Slave Select / Chip Select) :从设备选择/片选线。由主设备控制,用于选择通信的从设备。当SS/CS线为低电平(通常为活动低电平)时,对应的从设备被选中并准备好进行通信;当为高电平时,从设备在非选中状态,其MISO线通常会呈高阻态,允许从其他设备共享MISO线。

图1:SPI通信基本连接图(单主多从)

             +---------------------+                 +---------------------+|       Master        |                 |       Slave 1       ||                     |                 |                     |SCK <---|---------------------|------> SCK <---|                     |MOSI <---|---------------------|------> MOSI <---|                     |MISO <---|---------------------|<------ MISO <---|                     |CS1 <---|---------------------|------> SS/CS <---|                     |CS2 <---|---------------------+                 +---------------------+|       Master        ||                     |                 +---------------------+|                     |                 |       Slave 2       ||                     |                 |                     |SCK <---|---------------------|------> SCK <---|                     |MOSI <---|---------------------|------> MOSI <---|                     |MISO <---|---------------------|<------ MISO <---|                     |CS1 <---|                     |                 |                     |CS2 <---|---------------------|------> SS/CS <---|                     |+---------------------+                 +---------------------+

说明:

  • SCKMOSIMISO线路在多个从设备之间是共享的。
  • 每个从设备都需要布线独立的SS/CS线路,主设备通过将对应从设备的SS/CS线路拉低来选择通信的从设备。

3. STM32 SPI工作模式(CPOL和CPHA)

SPI协议的灵活体现在其四种工作模式上,这由时钟时钟(CPOL)和时钟相位(CPHA)两个参数决定。

  • CPOL (Clock Polarity)

    :时钟按钮。定义了SCK信号在空闲状态时的电平。

    • CPOL=0:SCK闲置时间为低水平。
    • CPOL=1:SCK闲暇时间为高档次。
  • CPHA (Clock Phase)

    :时钟相位。定义了数据在SCK的哪个边沿被采样。

    • CPHA=0:数据在SCK的第一个时钟边沿(上升沿或下降沿,CPOL)进行采样。
    • CPHA=1:数据在SCK的第二个时钟边沿(上升沿或下降沿,CPOL)进行采样。

这四个组合组成了四种SPI模式:

  • Mode 0 : CPOL=0, CPHA=0 (闲置低电平,第一个边沿采样)
  • 模式1:CPOL=0,CPHA=1(闲置低水平,第二个边沿采样)
  • 模式2:CPOL=1,CPHA=0(闲置高水平,第一个边沿采样)
  • 模式3:CPOL=1,CPHA=1(闲置高水平,第二个边沿采样)

图2:SPI四种工作模式的交互图

SCK (CPOL=0, CPHA=0)
空闲低电平____        ____        ____
_____|    |______|    |______|    |____^        ^|        |采样点    移位点 (数据输出)SCK (CPOL=0, CPHA=1)
空闲低电平____        ____        ____
_____|    |______|    |______|    |____^        ^|        |移位点    采样点SCK (CPOL=1, CPHA=0)
空闲高电平
____        ____        ____|______|    |______|    |____^        ^|        |采样点    移位点 (数据输出)SCK (CPOL=1, CPHA=1)
空闲高电平
____        ____        ____|______|    |______|    |____^        ^|        |移位点    采样点

说明:

  • 采样点(Sampling Edge):接收设备读取数据位的时钟边沿。
  • 升降点(Shift Edge):发送设备将数据位输出到数据线的时钟边沿。

在通信过程中,主设备和从设备必须配置为相同的SPI模式,否则通信将无法正常进行。

4. STM32 SPI数据传输流程

SPI通信是基于移位寄存器的。主设备和从设备内部都有一个移位寄存器。当主设备启动通信时,它会拉低目标从设备的SS/CS线,然后开始生成SCK时钟信号。

在每个时钟周期:

  • 主设备将要发送的数据位从其MOSI线上移出。
  • 从设备将要发送的数据位从其MISO线路移出。
  • 同时,主设备从MISO线接收数据位,从设备从MOSI线接收数据位。

这个过程就像两个升降台首尾相连,数据在主从设备之间“循环”移动。即使只需要单向传输数据,SPI也通常会进行双向数据交换(例如,主设备发送数据时,从设备会发送一些无效数据或初始数据)。

图3:SPI数据传输地图(全双工)

      +-----------+           +-----------+|   Master  |           |   Slave   ||   Shift   |           |   Shift   || Register  |           | Register  |+-----------+           +-----------+|                         |MOSI <-----|-------------------------|-----> 数据从主设备发送到从设备(Master Out, Slave In)MISO <-----|-------------------------|<---- 数据从从设备发送到主设备(Master In, Slave Out)SCK  <-----|-------------------------|-----> 时钟信号 (由主设备提供)SS/CS<-----|------------[选择从设备]------> 片选信号 (由主设备控制)

说明:

  • 在每个时钟边沿,主设备和从设备同时进行数据的发送和接收。
  • 数据通常为MSB ( Most Significant Bit) First(最高有效位优先)传输,但STM32也支持LSB First(最低有效位优先)传输。

5. STM32 SPI高级特性

STM32的SPI外设还支持以下高级功能,以提高通信效率和可靠性:

  • 数据帧格式:支持Motorola和TI帧格式。
  • 数据大小:可配置4位到16位数据帧。
  • 预分频器(Prescaler):SPI时钟可以通过预分频器进行分频,以调整通信速度。SPI时钟速度不能超过内部中断频率的一半。
  • DMA(Direct Memory Access):支持DMA传输,可以将数据直接从内存传输到SPI外设或从SPI外设传输到内存,从而减少CPU的干预,提高数据吞吐量。
  • CRC (Cyclic Redundancy Check):支持硬件CRC校验,提高数据传输的可靠性。
  • 中断(Interrupts):SPI外设置可以生成多种中断请求,如串口中断、串口非空中断、错误中断等,方便软件进行事件处理。
  • 半双工和只收/只发送模式:除了全双工模式,STM32 SPI还支持半双工(消耗毛发数据线)以及只接收或只发送模式。

6. STM32 SPI配置流程(HAL库示例)

使用STM32Cube HAL库配置SPI通常包括以下步骤:

  1. 使能时钟:使能SPI外部设置和相关GPIO端口的时钟。

  2. GPIO配置:配置SPI引脚(SCK、MOSI、MISO、SS/CS)为复用功能或通用推挽输出(用于软件SS)。

  3. SPI参数初始化

    :配置SPI实例,包括:

    • 模式(主/从)
    • 数据方向(全双工、半双工等)
    • 数据大小(8位、16位)
    • CPOL、CPHA
    • 波特率预分频器
    • 软件/硬件 NSS 管理
    • 主要传输顺序(MSB/LSB)
  4. 使能SPI外设: 启用SPI模块。

  5. 数据传输:调用HAL库函数进行数据发送、接收或收发,可以通过阻塞模式(Polling)、中断模式(Interrupt)或DMA模式。


代码示例(STM32)

1.STM32 SPI操作的整体流程

  1. GPIO初始化:配置SPI相关的GPIO引脚(SCK, MISO, MOSI, 以及任选的SS/CS)作为对应的复用功能。
  2. SPI外设时钟使能:使能SPI外设时钟。
  3. SPI初始化:配置SPI的工作模式(主/从)、数据方向、数据帧大小、时钟相位、时钟相位、波特率预分频参数等。
  4. SS/CS 引脚控制(如果使用软件 SS):如果不使用硬件 SS,则需要手动控制一个 GPIO 引脚作为片选信号。
  5. 数据传输:调用HAL库提供的发送、接收或收发函数。
  6. 错误处理与中断/DMA回调(如果使用):在中断或DMA模式下,处理中断回调函数中的数据和错误。

2.具体函数操作顺序(使用STM32CubeIDE生成的HAL库为例)

假设我们使用STM32CubeIDE生成了项目,并且已经配置好SPI外设。以下是核心的代码片段和函数调用顺序:

2.1. 物品工作(通常由CubeIDE自动生成)

main.c文件中,你会看到以下结构:

// 定义一个SPI句柄
SPI_HandleTypeDef hspi1; // 假设使用SPI1// GPIO初始化函数声明
static void MX_GPIO_Init(void);
// SPI初始化函数声明
static void MX_SPI1_Init(void);int main(void)
{/* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init(); // 1. HAL库初始化/* Configure the system clock */SystemClock_Config(); // 2. 系统时钟配置/* Initialize all configured peripherals */MX_GPIO_Init();   // 3. GPIO初始化MX_SPI1_Init();   // 4. SPI外设初始化/* Infinite loop */while (1){/* Add user code here */}
}
2.2. GPIO 初始化函数 ( MX_GPIO_Init)

该函数通常由CubeIDE根据你在GPIO配置页面的设置自动生成。它使能GPIO时钟,并配置各个引脚的模式、上下拉、速度等。

// Example: GPIO init for SPI1 (Master, Full-Duplex, Software NSS)
static void MX_GPIO_Init(void)
{GPIO_InitTypeDef GPIO_InitStruct = {0};/* GPIO Ports Clock Enable */__HAL_RCC_GPIOA_CLK_ENABLE(); // Enable GPIOA clock (assuming SPI1 uses PA5, PA6, PA7)__HAL_RCC_GPIOC_CLK_ENABLE(); // Enable GPIOC clock (assuming PC4 for SS/CS)/*Configure GPIO pin : PC4 (CS pin) */GPIO_InitStruct.Pin = GPIO_PIN_4;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // Output Push-Pull for SS/CSGPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; // Low speed for SS/CSHAL_GPIO_Init(GPIOC, &GPIO_InitStruct);/*Configure GPIO pins : PA5 (SCK), PA6 (MISO), PA7 (MOSI) */GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7; // SCK, MISO, MOSIGPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // Alternate Function Push-PullGPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; // High speed for SPI dataGPIO_InitStruct.Alternate = GPIO_AF5_SPI1; // Set alternate function to SPI1HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
2.3. SPI外设初始化函数 ( MX_SPI1_Init)

该函数也是由CubeIDE自动生成,负责配置SPI的各种工作参数。

static void MX_SPI1_Init(void)
{/* SPI1 parameter configuration*/hspi1.Instance = SPI1; // 选择SPI1外设hspi1.Init.Mode = SPI_MODE_MASTER; // 配置为SPI主模式hspi1.Init.Direction = SPI_DIRECTION_2LINES; // 双线全双工模式 (MOSI和MISO)hspi1.Init.DataSize = SPI_DATASIZE_8BIT; // 数据帧大小为8位hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; // 时钟极性:空闲时SCK为低电平 (CPOL=0)hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; // 时钟相位:数据在第一个时钟边沿采样 (CPHA=0)hspi1.Init.NSS = SPI_NSS_SOFT; // 软件片选管理 (如果使用硬件SS,改为SPI_NSS_HARD_INPUT/OUTPUT)hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16; // 波特率预分频,例如PCLK / 16hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; // MSB优先传输hspi1.Init.TIMode = SPI_TIMODE_DISABLE; // 禁用TI模式hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; // 禁用CRC校验hspi1.Init.CRCPolynomial = 10; // CRC多项式 (如果CRC开启)// 调用HAL库函数进行SPI初始化if (HAL_SPI_Init(&hspi1) != HAL_OK){Error_Handler(); // 初始化失败,调用错误处理函数}
}

函数操作顺序总结(初始化阶段):

  1. HAL_Init():初始化HAL库,包括系统时钟、Systick等。

  2. SystemClock_Config():配置系统时钟(通常由CubeIDE生成)。

  3. MX_GPIO_Init()
    
    • __HAL_RCC_GPIOx_CLK_ENABLE():使能所有相关GPIO端口的时钟。
    • HAL_GPIO_Init():配置每个SPI引脚的模式、速度、上下拉、复用功能等。
  4. MX_SPI1_Init()
    
    • __HAL_RCC_SPIx_CLK_ENABLE():使能SPI外部设定的时钟。
    • 配置hspi1结构体成员(Instance, Mode, Direction, DataSize, CLKPolarity, CLKPhase, NSS, BaudRatePrescaler, FirstBit, TIMode, CRCCalculation, CRCPolynomial)。
    • HAL_SPI_Init(&hspi1): 调用HAL库函数执行SPI硬件初始化。
2.4. 数据传输操作

main函数的while(1)循环中,可以执行数据传输操作。在每次通信之前,确保将目标设备的SS/CS引脚拉低,通信结束后再拉高。

使用软件NSS(推荐):

#define SLAVE_CS_PORT GPIOC
#define SLAVE_CS_PIN GPIO_PIN_4// 示例:发送数据
uint8_t txData[] = {0x01, 0x02, 0x03};
uint8_t rxData[3];
HAL_StatusTypeDef status;// 1. 拉低片选信号,选择从设备
HAL_GPIO_WritePin(SLAVE_CS_PORT, SLAVE_CS_PIN, GPIO_PIN_RESET); // CS low// 2. 发送数据 (阻塞模式)
status = HAL_SPI_Transmit(&hspi1, txData, sizeof(txData), HAL_MAX_DELAY); // 发送3个字节
if (status != HAL_OK)
{// Handle Error
}// 或者:接收数据 (阻塞模式)
status = HAL_SPI_Receive(&hspi1, rxData, sizeof(rxData), HAL_MAX_DELAY); // 接收3个字节
if (status != HAL_OK)
{// Handle Error
}// 或者:发送和接收数据 (阻塞模式)
status = HAL_SPI_TransmitReceive(&hspi1, txData, rxData, sizeof(txData), HAL_MAX_DELAY); // 同时发送和接收3个字节
if (status != HAL_OK)
{// Handle Error
}// 3. 拉高片选信号,释放从设备
HAL_GPIO_WritePin(SLAVE_CS_PORT, SLAVE_CS_PIN, GPIO_PIN_SET); // CS high// 4. 等待一段时间或执行其他操作
HAL_Delay(10); // 例如,等待10ms

函数操作顺序总结(数据传输阶段 - 支撑模式):

  1. HAL_GPIO_WritePin(SLAVE_CS_PORT, SLAVE_CS_PIN, GPIO_PIN_RESET):拉低 SS/CS从设备中选中。
  2. 选择传输函数
    • HAL_SPI_Transmit(&hspi1, pTxData, Size, Timeout): 发送数据。
    • HAL_SPI_Receive(&hspi1, pRxData, Size, Timeout): 接收数据(发送空字节)。
    • HAL_SPI_TransmitReceive(&hspi1, pTxData, pRxData, Size, Timeout):同时发送和接收数据。
  3. HAL_GPIO_WritePin(SLAVE_CS_PORT, SLAVE_CS_PIN, GPIO_PIN_SET):拉高SS/CS以从设备释放。
2.5. 中断和DMA模式(高级应用)

如果使用中断或DMA模式,数据传输函数此时非阻塞的,并且需要处理回调函数:

中断模式:

  1. 使能SPI中断

    MX_SPI1_Init()
    

    之后,通常会包含以下代码(或在CubeIDE中勾选):

    HAL_NVIC_SetPriority(SPI1_IRQn, 0, 0); // 设置中断优先级
    HAL_NVIC_EnableIRQ(SPI1_IRQn); // 使能SPI1中断
    
  2. 传输函数

    • HAL_SPI_Transmit_IT(&hspi1, pTxData, Size)
    • HAL_SPI_Receive_IT(&hspi1, pRxData, Size)
    • HAL_SPI_TransmitReceive_IT(&hspi1, pTxData, pRxData, Size)
  3. 中断回调函数

    • HAL_SPI_TxCpltCallback(&hspi1): 发送完成回调
    • HAL_SPI_RxCpltCallback(&hspi1): 接收完成回调
    • HAL_SPI_TxRxCpltCallback(&hspi1): 收发完成回调
    • HAL_SPI_ErrorCallback(&hspi1): 错误回调

DMA模式:

  1. 配置DMA通道:在CubeIDE中配置DMA(Tx和Rx)。
  2. 传输函数
    • HAL_SPI_Transmit_DMA(&hspi1, pTxData, Size)
    • HAL_SPI_Receive_DMA(&hspi1, pRxData, Size)
    • HAL_SPI_TransmitReceive_DMA(&hspi1, pTxData, pRxData, Size)
  3. DMA中断回调函数:与中断模式类似,DMA传输完成后会触发对应的SPI回调函数。

函数操作顺序总结(中断/DMA模式):

  1. 初始化阶段
    • 确定中断或DMA配置正确(使能中断/DMA通道,设置优先级)。
    • HAL_SPI_Init()内部会处理中断使能(如果是中断模式)或DMA关联。
  2. 传输等级
    • HAL_GPIO_WritePin()(SS/CS 低)
    • 调用HAL_SPI_Transmit_IT/DMA()HAL_SPI_Receive_IT/DMA()HAL_SPI_TransmitReceive_IT/DMA()。这些函数会立即返回。
    • 在主程序中,可以执行其他任务。
    • 等待或响应回调:在HAL_SPI_TxCpltCallbackHAL_SPI_RxCpltCallbackHAL_SPI_TxRxCpltCallback中处理数据完成的逻辑。
    • 在这些回调函数中,通常会执行HAL_GPIO_WritePin()(SS/CS high)来从设备释放,或者触发下一个传输。
    • 处理HAL_SPI_ErrorCallback中的错误。

3、注意事项

  • 时钟匹配:确保STM32主设备和从设备的SPI模式(CPOL和CPHA)完全匹配。
  • SS/CS管理:这是SPI通信的关键。一定要在每次通信前拉低SS/CS,结束通信后拉高,以确保从设备正确响应。如果是多从设备,每次只能选中一个从设备。
  • 数据蜡烛图:确定发送和接收蜡烛图的大小足以容纳所有数据。
  • 错误处理:经常检查HAL函数的返回值,并在出现HAL_ERRORHAL_TIMEOUT时进行适当的错误处理。
  • 同步与异步:阻塞模式(轮询)简单但效率低;中断和DMA模式更复杂但能提高CPU利用率和数据吞吐量。
  • 数据字节顺序:默认为MSB First,如果从设备为LSB First,需要在初始化时修改FirstBit成员。

以上是使用STM32 HAL库进行SPI通信的典型函数操作顺序。实际项目中,可能会根据具体的设备和应用需求进行参数。


总结

STM32的SPI协议是一种强大而灵活的串行通信方式。了解其主从架构、四线连接、CPOL/CPHA定义的工作模式以及数据传输机制,对于在STM32项目中有效地应用SPI关键。STM32提供了丰富的硬件特性和灵活的软件配置选项,能够满足各种应用场景下的高速、可靠的通信需求。

相关文章:

  • JDK21深度解密 Day 8:Spring Boot 3与虚拟线程整合
  • JVM 核心组件深度解析:堆、方法区、执行引擎与本地方法接口
  • 【课堂笔记】标签传播算法Label Propagation Algorithm(LPA)
  • VMware-workstation安装教程--超详细(附带安装包)附带安装CentOS系统教程
  • 在QT中,利用charts库绘制FFT图形
  • 安装win11之后,电脑经常会跳出“无法在此设备上加载驱动程序”的提示。无法加载的驱动程序分别为“pcdsrvc_x64.pkms”“iqvw64e.sys”
  • 学习海康VisionMaster之表面缺陷滤波
  • 在 RK3588 上通过 VSCode 远程开发配置指南
  • MySQL访问控制与账号管理:原理、技术与最佳实践
  • 软件工程方法论:在确定性与不确定性的永恒之舞中寻找平衡
  • Redis 常用数据类型和命令使用
  • Linux环境搭建MCU开发环境
  • MCP架构全解析:从核心原理到企业级实践
  • Kubernetes架构与核心概念深度解析:Pod、Service与RBAC的奥秘
  • Protos-SIP:经典 SIP 协议模糊测试工具!全参数详细教程!Kali Linux教程!
  • C#WinForm程序时方法很多时Form.cs文件会很长,如何分别写入多个文件,partial class的作用体现出来了。
  • MyBatis01
  • jetpack compose 界面刷新的几种方式 如何避免无效的界面刷新
  • Linux --OS和PCB
  • NLP学习路线图(十三):正则表达式
  • 网站做的支付宝接口/关键词站长工具
  • 北京公司注册代理/聊城seo
  • 石龙网站建设/刷粉网站推广免费
  • 网站域名怎么做变更/前端seo是什么意思
  • 网站备案需要些什么/网站友情链接
  • 营销型网站免费模板/百中搜