跳转至

SPI 从机应用开发入门指南

本文档以 spi_slave.c 为例,带你在 HiDiTing V100 开发板上快速跑通第一个 SPI 从机通信应用,并了解如何基于它构建自己的应用。


SPI 从设备驱动背景知识

SPI 从设备工作原理

SPI(Serial Peripheral Interface)是一种高速、全双工、同步的串行通信总线,从机通过以下信号线与主设备通信:

信号线 说明 方向
SCLK 时钟信号,由主机产生 主机→从机
MOSI 主机输出,从机输入 主机→从机
MISO 主机输入,从机输出 从机→主机
CS 片选信号(低有效) 主机→从机

SPI Slave核心工作流程:

  • 等待CS信号激活(低电平)
  • 在SCLK边沿同步收发数据
  • CS信号释放后结束传输

通信模式配置

SPI Slave必须与主设备保持一致的通信模式配置:

模式 CPOL CPHA 说明
0 0 0 时钟空闲低电平,数据在第一个边沿采样
1 0 1 时钟空闲低电平,数据在第二个边沿采样
2 1 0 时钟空闲高电平,数据在第一个边沿采样
3 1 1 时钟空闲高电平,数据在第二个边沿采样

通信模式配置

  • 初始化:通过 uapi_spi_init() 对 SPI 进行初始化。
  • 读数据:使用 uapi_spi_slave_read() 接收数据。
  • 写数据:使用 uapi_spi_slave_write() 发送数据
  • 全双工读写数据:使用 uapi_spi_slave_writeread() 同时读写数据
  • 去初始化:通过 uapi_spi_deinit() 释放资源。

数据传输机制

基础传输特性

  • 同步传输:完全由主设备时钟控制
  • 全双工:可同时收发数据
  • 从动模式:不能主动发起传输

数据传输模式对比

模式 触发条件 优点 缺点 适用场景
轮询 主动查询 实现简单 CPU占用高 低速简单应用
中断 CS信号/数据就绪 实时响应 需处理中断嵌套 中等速率常规应用
DMA 硬件自动触发 解放CPU 配置复杂 高速大数据量传输

从设备寻址方式

寻址方式 实现方法 特点
硬件片选 专用CS引脚 每个从设备独立CS线
软件寻址 首字节地址 共享CS线,通过数据包识别
---

快速跑通 SPI_slave Demo

功能说明

SPI slave Demo 支持通过 AT 指令进行 SPI_slave 的配置和传输功能,相应的 AT 指令如下:

AT+SPISLAVE=50

AT指令参数说明

参数 默认值 说明 范围
loop_count 50 需要执行数据传输的次数 1 - 250

编译

完成一站式 CLI 环境配置后执行:

# 编译固件,编译生成的固件从 output/3322/fwpkggt 中获取 diting-community.fwpkg
fbb set-target pack_diting_community
fbb build

构建成功后,使用一站式 CLI 烧写固件并打开 UART2 串口监视器。以下为 Windows USB DFU 示例;将 COM3 替换为实际日志串口,其他平台和串口烧写参数参见一站式 CLI 开发环境使用指南

fbb flash -f "$env:FBB_SDK_DIR\output\3322\fwpkg\diting-community.fwpkg" --chip 3322 -d --timeout 180
fbb monitor --port COM3 --baud 750000

使用方式

  1. 运行当前 SPI Slave Demo 时,完成主从机物理连接后,通过 AT 指令启动从机并等待接收主机数据。产品应用应在任务或服务初始化中直接调用从机配置和接收接口,不依赖 AT 命令。物理接线图如下:

SPI 主从机物理接线示意图

连接说明:

  • MOSI(主出从入):
  • 主设备的MOSI → 从设备的MOSI(数据由主设备发送到从设备)。
  • MISO(主入从出):
  • 主设备的MISO ← 从设备的MISO(数据由从设备返回主设备)。
  • SCLK(时钟线):
  • 主设备的SCLK → 从设备的SCLK(主设备控制时钟同步)。
  • SS/CS(片选线):
  • 主设备的CS → 从设备的CS(低电平时激活从设备)。
  • 若从设备无需片选,可接地(始终激活);多从机时需独立CS线。

  • 烧录固件并启动设备

  • 通过串口发送 AT 命令,格式如下:

通过串口发送 AT 命令,格式如下

# SPI_slave 设置使用示例:启动 SPI demo开始进行 SPI 的配置、初始化以及验证数据传输功能
AT+SPISLAVE=50

预期结果

SPI 从设备配置结果(AT+SPISLAVE)

SPI 从设备配置结果(AT+SPISLAVE)

SPI 数据传输结果

SPI 数据传输结果

文件结构与代码走读

文件职责

文件 作用
samples/native_samples/spi/CMakeLists.txt 将 SPI Master、Slave 源文件构建为同一 Sample 组件。
samples/native_samples/spi/spi_slave.h 定义从机串口测试参数和命令表。
samples/native_samples/spi/spi_slave.c 实现从机引脚、总线属性、DMA 和收发流程。
samples/native_samples/spi/README.md 给出主从连接、命令和预期数据。

核心数据与常量

名称 作用 开发注意事项
spi_attr_t 配置从机角色、总线时钟、工作频率、CPOL、CPHA、帧格式和帧长。 is_slave 必须为真,其他时序参数必须与主机一致。
spi_extra_attr_t 配置 DMA 选择及扩展字段。 所有字段在调用初始化接口前都应赋确定值。
spi_xfer_data_t 描述从机收发缓冲区和长度。 从机必须在主机发起时钟前准备好缓冲区和接收调用。
spi_dma_config_t 配置 DMA 宽度、突发长度和优先级。 DMA 宽度、地址对齐及传输长度需要一致。
CONFIG_SPI_SLAVE_BUS_ID / CONFIG_SPI_TRANSFER_LEN 选择从机总线并限定 Sample 收发长度。 修改时同步检查主机配置和实际连线。

spi_slave_task_args_t 只负责接收 AT 测试循环次数,不属于驱动公共数据结构。

核心业务流程

  1. 配置从机 CLK、MOSI、MISO、CS 引脚的输入使能和复用模式。
  2. 填写从机模式的 spi_attr_tspi_extra_attr_t,清理旧状态后调用 uapi_spi_init()
  3. 初始化 spi_xfer_data_t 和 DMA 参数,在主机产生时钟前调用从机收发接口等待数据。
  4. 每次传输后按协议校验接收数据并准备下一帧;完成后关闭 DMA 并调用 uapi_spi_deinit()

AT 命令仅触发循环收发,完整属性和异常分支见 Sample 源文件。业务应用使用下一节的 app_run 方式组织生命周期,不复制 AT 注册和日志代码。

基于 SPI_slave Demo 开发自己的应用

上面的demo是使用AT指令触发运行,HiDiTing还支持app_run方式触发应用在系统启动时自动运行,以下示例将以app_run的方式开发一个开发者自己的应用

  • app_run(func) 是HiDiTing中应用层注册应用函数的宏,基于 GCC 编译器属性和自定义段区(section)自动注册来实现集中调用应用函数,
    系统启动时会自动遍历所有用 app_run 注册过的函数并执行,无需在系统 main 函数里逐个调用函数。

代码清单

新建一个 SPI 从设备应用(以 my_spi_slave 为例)通常需要以下步骤:

  • 新建 my_spi_slave.c 源文件
  • 新建 my_spi_slave.h 头文件
  • 新建 CMakeLists.txt 源文件
  • my_spi_slave.c 中实现 SPI slave 操作函数
  • my_spi_slave.h 中定义宏常量和函数声明
  • CMakeLists.txt 源文件编译规则

my_spi_slave文件结构:

samples/native_samples/my_spi_demo/
├── my_spi_master.c
├── my_spi_master.h
├── my_spi_slave.c
├── my_spi_slave.h
└── CMakeLists.txt

CMakeLists.txt 示例

# 在新建的 CMakeLists.txt 中添加应用的源文件
set(SOURCES
    ${CMAKE_CURRENT_SOURCE_DIR}/my_spi_master.c
    ${CMAKE_CURRENT_SOURCE_DIR}/my_spi_slave.c
)

set(PUBLIC_HEADER
    ${CMAKE_CURRENT_SOURCE_DIR}
)

set(PRIVATE_HEADER

)

set(PUBLIC_DEFINES
    MY_SPI_MASTER_DEMO_ENABLE
    MY_SPI_SLAVE_DEMO_ENABLE
)

build_component()

关键代码片段

#include "spi.h"
#include "app_init.h"

void my_spi_slave_entry(void)
{
    osal_printk("my_spi_slave_app start\n");

    // step1:参数配置
    spi_attr_t config = { 0 };
    config.is_slave = true;
    config.bus_clk = 1000000; // 1 MHz
    config.freq_mhz = 1;
    config.clk_polarity = 0;
    config.clk_phase = 0;
    config.frame_format = 0;
    config.frame_size = 0x1f;

    // step2:初始化
    uapi_spi_init(MY_SPI_BUS_ID, &config, NULL);

    // step3:数据传输缓冲区配置
    uint8_t tx_data[3] = {0x01, 0x02, 0x03};
    uint8_t rx_data[3] = {0};
    spi_xfer_data_t data = {
        .tx_buff = tx_data,
        .tx_bytes = sizeof(tx_data),
        .rx_buff = rx_data,
        .rx_bytes = sizeof(rx_data),
    };

    // step4:数据传输
    if (uapi_spi_slave_writeread(MY_SPI_BUS_ID, &data, 0xFFFFFFFF) == ERRCODE_SUCC) {
        osal_printk("SPI transaction success\n");
        for (int i = 0; i < sizeof(rx_data); i++) {
            osal_printk("rx_data[%d] = %x\n", i, rx_data[i]);
        }
    }

    osal_printk("my_spi_slave_app done\n");
}
app_run(my_spi_slave_entry);

测试验证

完成一站式 CLI 环境配置后执行:

# 编译固件,编译生成的固件从 output/3322/fwpkggt 中获取 diting-community.fwpkg
fbb set-target pack_diting_community
fbb build
1. 烧录固件并启动设备 2. 通过串口观察输出日志:

新demo完成后,逐项验收:

  • 设备启动后串口输出 my_spi_slave_app start
  • 输出 SPI 传输的接收数据。
  • 最后输出 my_spi_app done

app_run运行配置

app_run应用默认是关闭的,如需启用此应用,需用户手动在acore.prelds文件中添加 KEEP (*(SORT(.zinitcall.app_run*.init))) 具体参考示意图如下:

apprun运行配置

acore.prelds

注意事项

引脚配置注意事项:

  • 确保选择的 DI、DO、CLK 和 CS 引脚正确连接
  • 引脚模式需要与硬件匹配

时钟频率注意事项:

  • 主从设备必须使用相同的时钟频率
  • 常见频率:1MHz、2MHz、5MHz、16MHz

中断模式注意事项:

  • 中断模式需要正确注册回调函数
  • 中断处理函数应尽量简短

QSPI 模式注意事项:

  • QSPI 模式需要额外配置 D2 和 D3 引脚
  • 需要确保硬件支持 QSPI 模式

资源说明:

  • 使用 DMA 模式需要额外配置 DMA 相关参数

常见错误

错误现象 原因 解决方法
undefined reference to 'uapi_spi_init' 未链接 SPI 驱动库 检查 CMakeLists.txt 中是否正确添加了源文件
undefined reference to 'uapi_at_cmd_table_register' 未链接 AT 命令库 检查 CMakeLists.txt 中是否正确添加了 AT 相关头文件路径
编译报 at_ret_t 未定义 未包含 at.h 头文件 添加 #include "at.h"
串口无输出 AT 命令未注册 确认调用了 at_diting_spi_slave_example_cmd_register() 或 AT参数指令和参数是否正确
SPI 传输失败 配置错误或引脚冲突 检查 SPI 配置和针脚设置
DMA 模式报错 DMA 配置错误 检查 DMA 相关配置参数