C语言SPI虚拟总线中间层设计详解

Luna
Luna
发布时间:2026-10-13| 2人浏览过
原创

针对嵌入式开发中GPIO模拟SPI协议的复杂性,本文解析了一个轻量级C语言中间层架构的设计思路。通过区分共享的SPI总线与独立的SPI设备结构体,引入互斥锁机制解决并发抢占问题,并利用统一的消息传递接口屏蔽底层硬件细节。该设计实现了从物理接线到数据全双工传输的完整逻辑抽象,为MCU平台的通用SPI通信提供了标准化的实现方案。

SPI协议与基础特性

SPI(Serial Peripheral Interface,串行外设接口)通常采用一主多从的拓扑结构,由四根核心信号线组成:CLK(时钟)、MISO(主机输入/从机输出)、MOSI(主机输出/从机输入)以及CS/NSS(片选)。其基本接线规则为主从设备的一一对应连接:

  • 主机 CLK 连接从机 CLK
  • 主机 MISO 连接从机 MISO
  • 主机 MOSI 连接从机 MOSI
  • 主机 CS/NSS 连接从机 CS/NSS

与传统I2C协议依赖地址码不同,SPI通过CS/NSS电平切换来选中特定设备,这使得多个从机可以挂载在同一组CLK、MISO和MOSI总线上。理论上只要拥有足够的IO口,即可无限扩展设备数量;但在实际应用中,IO资源往往稀缺,因此常结合菊花链(Daisy Chain)设计以优化引脚占用。

工作本质:SPI通信的核心在于移位寄存器与环形缓冲区。主机将待发送数据写入发送缓冲器后,在时钟脉冲驱动下,数据逐位移出至MOSI;与此同时,MISO线上的数据也会同步移入主机的接收缓冲器。这种机制天然支持全双工通信,即在发送的同时完成接收。

SPI工作模式配置

为了确保主机与从机能够正确识别数据,双方必须配置相同的通信参数,主要由时钟极性(CPOL)和时钟相位(CPHA)决定,共形成4种标准工作模式:

参数说明
CPOL (Clock Polarity)0:空闲时CLK为低电平
1:空闲时CLK为高电平
CPHA (Clock Phase)0:第一个跳变沿采样数据
1:第二个跳变沿采样数据

组合后的四种模式如下:

MODECPOLCPHA
000
101
210
311

虚拟总线中间层架构设计

在软件实现中,直接操作底层寄存器较为繁琐且难以移植。通过定义虚拟总线中间层,可以将共享的信号线与独立的设备控制分离。设计中包含两个核心结构体与三个关键步骤:挂载、获取总线权限、释放总线。

第一步:数据结构定义

SPI总线(spi-bus):负责管理公共信号线及并发访问。由于CLK、MISO、MOSI是共享资源,必须引入互斥锁来防止数据传输时的抢占。

struct mr_soft_spi_bus
{
  void (*set_clk)(mr_uint8_t level);   // 操作 CLK 的函数指针
  void (*set_mosi)(mr_uint8_t level);  // 操作 MOSI 的函数指针
  mr_uint8_t (*get_miso)(void);        // 读取 MISO 的函数指针
  struct mr_soft_spi *owner;           // 当前该总线的所有者设备指针
  mr_uint8_t lock;                     // 互斥锁状态标志
};

SPI设备(spi-device):负责管理特有的片选信号与工作模式。每个从机仅需绑定自己的CS/NSS引脚。

struct mr_soft_spi
{
  mr_uint8_t mode         :2;          // SPI 工作模式 (Mode 0~3)
  mr_uint8_t cs_active    :1;          // CS/NSS 有效电平 (0或1)
  struct mr_soft_spi_bus *bus;         // 指向所属的SPI总线结构体
  void (*set_cs)(mr_uint8_t level);    // 操作 CS/NSS 的函数指针
};

第二步:挂载与总线控制

当物理接线完成后,需先建立关联:mr_soft_spi_attach(spi, spi_bus),将设备绑定到总线上。

在使用传输功能前,必须调用mr_soft_spi_bus_take()获取总线控制权。该函数执行以下逻辑:

  1. 检查所有权:若当前设备已是所有者,则仅加锁并拉低片选(视电平有效情况而定)。
  2. 竞争获取:若非所有者,进入循环等待互斥锁变为解锁状态(MR_UNLOCK)。
  3. 状态切换:一旦获得锁,将原设备(若有)的片选置为无效,关闭旧连接;随后更新owner指针为本设备,并拉低本设备的片选信号,开始通信。

使用完毕后,调用mr_soft_spi_bus_release(spi)释放总线。它会将片选恢复为非有效状态,并将互斥锁重置为解锁,允许其他设备接入。

第三步:统一消息传输接口

为了简化上层应用逻辑,设计了标准的消息结构体mr_soft_spi_msg及传输函数mr_soft_spi_transfer:

struct mr_soft_spi_msg
{
  mr_uint8_t read_write;      // 读写模式:SPI_WR / SPI_RD / SPI_RDWR / SPI_WR_THEN_RD
  mr_uint8_t *send_buffer;    // 发送数据地址
  mr_size_t send_size;        // 发送数据长度
  mr_uint8_t *recv_buffer;    // 接收数据地址
  mr_size_t recv_size;        // 接收数据长度
};

transfer内部封装了具体的移位逻辑。根据read_write的类型,循环调用底层的mr_soft_spi_bus_transmit函数(用于单次字节的全双工交互),自动处理收发缓冲区指针的移动,并在所有数据传输结束后自动释放总线,确保线程安全。

C语言实战应用示例

以下代码演示如何在STM32等平台上使用该中间层进行SPI写操作:

/* 1. 配置GPIO中断接回调 */
void set_clk(mr_uint8_t level) {
    GPIO_WriteBit(GPIOA, GPIO_Pin_0, level);
}
mr_uint8_t get_miso(void) {
    return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_2);
}
void set_cs(mr_uint8_t level) {
    GPIO_WriteBit(GPIOA, GPIO_Pin_3, level);
}

int main(void) {
    /* 初始化系统时钟与GPIO模式(省略部分硬件初始化细节) */
    
    /* 2. 实例化并配置SPI总线 */
    struct mr_soft_spi_bus spi_bus;
    spi_bus.set_clk = set_clk;
    spi_bus.set_mosi = set_clk; /* 实际应为 set_mosi 函数 */
    spi_bus.get_miso = get_miso;
    spi_bus.lock = MR_UNLOCK;
    spi_bus.owner = MR_NULL;

    /* 3. 实例化并配置SPI设备 */
    struct mr_soft_spi spi_device;
    spi_device.mode = SPI_MODE_0;
    spi_device.cs_active = LEVEL_LOW; /* 假设低电平片选有效 */
    spi_device.set_cs = set_cs;

    /* 4. 建立连接 */
    mr_soft_spi_attach(&spi_device, &spi_bus);

    /* 5. 准备发送数据 */
    mr_uint8_t buffer[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    struct mr_soft_spi_msg msg;
    msg.send_buffer = buffer;
    msg.send_size = sizeof(buffer);
    msg.recv_buffer = NULL;
    msg.recv_size = 0;
    msg.read_write = SPI_WR; /* 仅发送模式 */

    /* 6. 执行通信 (内部自动处理取锁、传输、释放) */
    mr_soft_spi_transfer(&spi_device, msg);
}

通过上述分层设计,底层只需关注单个字节的移位发送,而业务层无需关心复杂的时序与时钟翻转,极大地降低了代码维护成本。开源项目的详细实现请参考 gitee.com/chen-fanyi/mr-library/tree/master/mr-library/device/mr_soft_spi 。

以上就是C语言软件spi虚拟总线中间层设计的详细内容,更多关于C语言软件spi虚拟总线中间层的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。