Arduino_Core_STM32 SPI 库扩展详解:硬件 CS 引脚管理、Tx/Rx 双缓冲传输与 HAL 混合编程

发布时间:2026/10/15 3:51:20
Arduino_Core_STM32 SPI 库扩展详解:硬件 CS 引脚管理、Tx/Rx 双缓冲传输与 HAL 混合编程 嵌入式固件驱动开发硬件开发【免费下载链接】Arduino_Core_STM32STM32 core support for Arduino项目地址https://gitcode.com/gh_mirrors/ar/Arduino_Core_STM32点击查看免费下载导读STM32 的 SPI 外设与 AVR 平台不同其 NSS片选引脚可以由 SPI 外设本身直接驱动从而实现完全由硬件管理片选信号的通信流程。Arduino_Core_STM32仓库中的 SPI 库libraries/SPI在完整保留标准 Arduino SPI API 的基础上为此专门新增了构造函数重载、setSSEL()引脚重映射、transfer(const void *tx_buf, void *rx_buf, size_t count)双缓冲传输以及skipReceive只发不收参数等扩展能力。读完本文你将掌握如何创建绑定硬件 CS 引脚的 SPI 实例、如何在begin()之前重定义实例的默认引脚、如何用双缓冲一次性完成收发、以及如何通过getHandle()将 Arduino API 与 STM32Cube HAL 的 DMA 等能力混用。一、库定位与片选CS管理的两种模式SPI 库在library.properties中声明为This library is based on the official Arduino SPI library and adapted to STM32 boards即它在标准 Arduino SPI API 之上针对 STM32 平台做了适配与扩展核心类为SPIClass定义于 SPI.h全局默认实例为SPISPI.cpp。关于 CS 引脚的管理用户可以有两种选择软件管理 CS与标准 Arduino SPI 库一致由用户代码在传输前后手动拉低/拉高 CS 引脚digitalWrite(csPin, LOW)/digitalWrite(csPin, HIGH)硬件 CS 管理用户把一个由 SPI 外设关联的硬件 NSS 引脚交给 SPI 实例片选信号的产生与释放由 SPI 外设自动完成。两种方式的底层差异可以在 spi_com.c 的初始化代码中直接看到// Configure the SPI pins if (obj-pin_ssel ! NC) { handle-Init.NSS SPI_NSS_HARD_OUTPUT; // 硬件 NSS输出由外设驱动 } else { handle-Init.NSS SPI_NSS_SOFT; // 软件 NSS片选交给用户代码 }也就是说只要实例的pin_ssel被配置为有效引脚非NCHAL 的NSS就设置为SPI_NSS_HARD_OUTPUT片选由外设接管否则回退为SPI_NSS_SOFT由用户自己控制。二、替代构造函数SPIClass(mosi, miso, sclk, ssel)标准用法是直接用默认引脚构造全局实例SPIClass SPI;引脚取自variant*.h中定义的默认MOSI/MISO/SCK宏。本库新增了一个可传入完整引脚组的构造函数SPIClass::SPIClass(uint8_t mosi, uint8_t miso, uint8_t sclk, uint8_t ssel)参数说明定义见 SPI.cpp参数说明mosiSPI MOSI 引脚可接受引脚数字、Arduino 格式如Dx或 ST 格式如PxymisoSPI MISO 引脚格式同上sclkSPI 时钟引脚格式同上ssel可选参数必须是硬件 CS 引脚该引脚必须能由 SPI 外设管理。配置该引脚后片选将由 SPI 外设接管关键约束源码注释明确给出所有引脚必须挂接在同一个 SPI 外设上见 SPI.cpp 的注释spi_init()内部会通过pinmap_merge_peripheral()检查 MOSI/MISO/SCLK/SSEL 四路引脚是否都解析到同一个 SPI 实例若不一致会打印ERROR: SPI pins mismatch并返回spi_com.c哪些引脚可以作为硬件 SSEL需要查芯片数据手册或查看对应板卡PeripheralPins.c中的PinMap_SPI_SSEL[]表。以 STM32G4 系列为例G431C(6-8)U_G441CBU/PeripheralPins.cU_G441CBU/PeripheralPins.c#L280-L285) 中定义形如{PA_4, SPI1, ...}、{PA_15, SPI1, ...}的条目即这些引脚属于对应 SPI 外设可管理的硬件 NSS 引脚一旦配置了硬件 CS 引脚不能再传另一个 CS 引脚给本类的任何函数也不应该再通过digitalWrite()手动控制片选注意硬件 NSS 输出时默认带LL_GPIO_PULL_UP见上表PinMap_SPI_SSEL条目SCK 引脚则根据 CPOL 极性自动配置上拉/下拉spi_com.c。完整示例硬件 CS 管理以下是 README 给出的典型用法本文补全了语句分号并增加SPISettings定义适用于例如 STM32F1 系列上把 PC9 作为硬件 SSEL 的场景#include SPI.h // MOSI MISO SCLK SSEL SPIClass SPI_3(PC12, PC11, PC10, PC9); SPISettings settings(4000000, MSBFIRST, SPI_MODE0); void setup() { SPI_3.begin(); // 以默认 SPISettings 使能 SPI_3 SPI_3.beginTransaction(settings); // 以自定义 settings 重新配置 SPI_3 SPI_3.transfer(0x52); // 向第一个设备传输数据CS 由外设自动管理 SPI_3.endTransaction(); SPI_3.end(); // 禁用 SPI_3 实例 } void loop() { }三、begin()与beginTransaction()的初始化行为在深入 transfer 之前先明确实例初始化的行为这关系到硬件 CS 是否真正生效begin()内部会把实例重置为默认SPISettings(4000000, MSBFIRST, SPI_MODE0, busMode)即默认时钟 4 MHz、MSB 先发、SPI 模式 0SPI.cpp默认常量SPI_SPEED_CLOCK_DEFAULT同样定义为 4 MHzspi_com.hbeginTransaction(settings)只有在新 settings 与当前配置不同时才真正调用spi_init()重新初始化SPI.cppspi_init()会根据设定的目标频率从 2、4、8…直到 256 的预分频链中选取不低于目标频率的最高可用档位spi_com.c目标频率基于spi_getClkFreqInst()得到的该 SPI 实例外设时钟PCLK1/PCLK2 等计算end()调用spi_deinit()执行 HAL 反初始化并关闭 SPI 外设时钟SPI.cpp。对 STM32WB0x / STM32WL3x 等特殊系列外设时钟按SystemCoreClock / 4计算spi_com.c这一点会影响你能得到的实际波特率上限建议根据芯片数据手册确认。四、修改默认SPI实例的引脚setMISO/setMOSI/setSCLK/setSSEL当不想新建实例、只想改变全局SPI实例的引脚时可以使用以下 API每个函数都有接收pin_size_t引脚数字与PinName两种重载定义见 SPI.hvoid setMISO(uint32_t miso); // 或 setMISO(PinName miso) void setMOSI(uint32_t mosi); // 或 setMOSI(PinName mosi) void setSCLK(uint32_t sclk); // 或 setSCLK(PinName sclk) void setSSEL(uint32_t ssel); // 或 setSSEL(PinName ssel)[!WARNING]这些调用必须在begin()之前进行。原因从实现可见setXXX()只是改写_spi结构体中的PinName字段SPI.h真正的引脚 GPIO 配置与外设绑定是在后续begin()→spi_init()中完成的begin()之后再调用不会重新执行初始化流程。[!NOTE] 使用setSSEL()即可启用与 SPI 外设关联的硬件 CS 引脚管理pin_ssel非NC时NSS自动切换为SPI_NSS_HARD_OUTPUT。示例数字格式与 PinName 格式混用void setup() { SPI.setMISO(PC_4); // 使用 PinName 格式 PY_n SPI.setMOSI(PC2); // 使用引脚数字格式 PYn // SPI.setSSEL(PA_4); // 可选启用硬件 CS 管理 SPI.begin(2); // 以 2 MHz 波特率初始化默认 SPI 实例busMode 可选 }SPI.begin(2)传入了自定义时钟频率此时实例将以 2 MHz 为目标速率初始化。此外begin()还支持传入SPIBusModeSPI_CONTROLLER或SPI_PERIPHERAL见 SPI.h其中SPI_PERIPHERAL对应 HAL 的从机模式SPI_MODE_SLAVE见 spi_com.c可用于把 STM32 配置为 SPI 从设备。五、扩展 transfer 系列双缓冲收发与 skipReceive1. 新增双缓冲接口transfer(tx_buf, rx_buf, count)标准 Arduino SPI 只有单缓冲transfer(buf, count)收发共用同一块内存本库新增了收发分离的双缓冲版本void transfer(const void *tx_buf, void *rx_buf, size_t count);参数语义见 SPI.cpp 的完整注释tx_buf用户预先填好的待发送字节数组。若传NULL外设将以 0xFF 哑字节dummy bytes作为发送内容rx_buf接收缓冲区从设备发回的数据会写入这里。若传NULL收到的数据将被直接丢弃count本次要发送/接收的字节数。底层实现为spi_transfer()spi_com.c其逐字节流程是等待发送缓冲区可写TXP/TXE标志→ 写入下一字节tx_buf为NULL时写入0XFF→ 等待接收标志RXP/RXNE→ 读取一字节写入rx_bufrx_buf为NULL时仅读走丢弃。因此这个接口特别适合发送命令、读取响应的典型 SPI 事务例如uint8_t command[2] {0x1F, 0x00}; uint8_t response[2]; SPI.transfer(command, response, 2); // 发送 2 字节并同时接收 2 字节2. 所有标准 transfer 增加skipReceive参数本库给全部标准transfer()接口追加了一个可选的bool skipReceive参数用于跳过接收阶段、只做发送。两个常量定义于 SPI.h#define SPI_TRANSMITRECEIVE false // 默认值发送并接收 #define SPI_TRANSMITONLY true // 只发送不读取涉及的重载实现见 SPI.cppuint8_t transfer(uint8_t data, bool skipReceive); // 单字节 uint16_t transfer16(uint16_t data, bool skipReceive); // 双字节16 位 void transfer(void *buf, size_t count, bool skipReceive); // 多字节单缓冲不传该参数时默认取SPI_TRANSMITRECEIVE保持标准行为。transfer16()在非 MSB 先发LSB first模式下还会自动做字节交换以保持数据语义正确SPI.cpp。3. 传输超时保护所有传输带 1000 ms 的默认超时保护SPI_TRANSFER_TIMEOUT定义于 spi_com.h超时后spi_transfer()返回SPI_TIMEOUT。SPI_TRANSFER_TIMEOUT可被外部宏覆盖但不能设为 0 或负数源码中有#error防护。若需要可通过编译选项自定义该值。六、与 STM32Cube HAL 混合编程getHandle()当标准 Arduino API 无法满足需求例如要用 DMA 或直接操作 HAL 句柄时可以取回底层句柄SPI_HandleTypeDef *getHandle(void); // 传统 HALUSE_HALV2_DRIVER 未定义时 hal_spi_handle_t *getHandle(void); // HAL v2 驱动USE_HALV2_DRIVER 定义时实现见 SPI.h它返回_spi.handle的地址即与spi_t结构体spi_com.h中的 HAL 句柄共享同一份内存。README 的提示同样适用于此Use at your own risk——拿到句柄后可以调用HAL_SPI_Transmit_DMA()、HAL_SPI_Receive_DMA()等 HAL 层 API但要自行保证时序与资源管理的正确性并注意不要在begin()之前取用句柄此时尚未完成初始化。七、综合实战从仓库示例看两种 CS 用法仓库自带两个官方示例恰好分别对应两种 CS 管理方式软件 CS 示例DigitalPotControl 驱动 AD5206 数字电位器在digitalPotWrite()中手动digitalWrite(slaveSelectPin, LOW)/HIGH包住两次SPI.transfer()片选完全由用户代码控制软件 CS 示例寄存器读写BarometricPressureSensor 读取 SCP1000 气压传感器同样通过digitalWrite(chipSelectPin, LOW/HIGH)手工管理片选并使用SPI.transfer(0x00)获取从设备回读数据。若改用本文介绍的硬件 CS 模式则需把上述示例中的 CS 引脚替换为对应外设的硬件 NSS 引脚并在构造或setSSEL()中配置之后删除所有手动的digitalWrite(csPin, ...)调用片选信号由 SPI 外设在每次传输时自动产生。两种模式的取舍可总结为维度软件 CS硬件 CS本库扩展片选时机用户代码控制可自由拉长/自定义时序由 SPI 外设自动产生/释放时序紧凑多从机灵活任意 GPIO 均可作 CS受限只能用外设关联的 NSS 引脚一次只绑定一个设备使用成本每个事务前后各一次digitalWrite构造/setSSEL时配置一次即可八、易错点与使用建议引脚合法性ssel必须是PinMap_SPI_SSEL[]中存在的硬件 NSS 引脚且 MOSI/MISO/SCLK/SSEL 四脚必须属于同一个 SPI 外设否则spi_init()会报SPI pins mismatch/at least one SPI pin has no peripheral并放弃初始化spi_com.c调用顺序setMISO/setMOSI/setSCLK/setSSEL必须在begin()之前调用beginTransaction()必须在begin()之后调用配置了硬件 CS 后不要混用手动 CSREADME 明确要求配置硬件 CS 后不得再向任何函数传入其他 CS 引脚只发送场景对纯发送如写寄存器可使用SPI_TRANSMITONLY跳过接收减少不必要的等待哑字节需要读取从设备数据但无需发送有效内容时tx_buf传NULL即可外设会自动发送 0xFF。九、相关源码导航库主体与元数据libraries/SPISPI.h、SPI.cpp、library.properties、keywords.txt底层驱动封装spi_com.c、spi_com.h官方示例DigitalPotControl、BarometricPressureSensor硬件 SSEL 引脚映射表各系列板卡以 G431C(6-8)U_G441CBU/PeripheralPins.cU_G441CBU/PeripheralPins.c#L280-L285) 为参考各 variant 目录下均有同名文件SPI 能力查询宏digitalPinHasSPI()pins_arduino.h可用于判断某引脚是否具备 SPI 复用功能赞分享嵌入式固件驱动开发硬件开发【免费下载链接】Arduino_Core_STM32STM32 core support for Arduino项目地址https://gitcode.com/gh_mirrors/ar/Arduino_Core_STM32点击查看免费下载相关推荐ESP-IDF项目ESP32-P4 SPI控制器硬件CS信号数量解析ESP IDF项目ESP32 P4 SPI控制器硬件CS信号数量解析 在ESP32 P4开发过程中开发者经常需要了解其外设接口的具体规格特别是SPI控制器物联网嵌入式klog 过滤器完全教程从日期范围到复杂逻辑表达式的进阶玩法klog 过滤器完全教程从日期范围到复杂逻辑表达式的进阶玩法 如果你正在用 klog 过滤器 来管理自己的时间记录那么这篇教程就是为你准备的。klog 是一OpenSearch Analytics Framework理解分析引擎的 SPI 契约库与插件扩展体系OpenSearch Analytics Framework理解分析引擎的 SPI 契约库与插件扩展体系 本篇技术指南围绕 analytics framewo搜索引擎全文检索可观测性数据分析上一篇bup-configBup 备份系统的 Git 风格配置指南仓库标识、分块粒度与服务器去重下一篇PI 自定义工具完全指南用 registerTool 给 LLM 赋予新能力创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询