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指令参数说明
| 参数 | 默认值 | 说明 | 范围 |
|---|---|---|---|
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
使用方式
- 运行当前 SPI Slave Demo 时,完成主从机物理连接后,通过 AT 指令启动从机并等待接收主机数据。产品应用应在任务或服务初始化中直接调用从机配置和接收接口,不依赖 AT 命令。物理接线图如下:

连接说明:
- MOSI(主出从入):
- 主设备的MOSI → 从设备的MOSI(数据由主设备发送到从设备)。
- MISO(主入从出):
- 主设备的MISO ← 从设备的MISO(数据由从设备返回主设备)。
- SCLK(时钟线):
- 主设备的SCLK → 从设备的SCLK(主设备控制时钟同步)。
- SS/CS(片选线):
- 主设备的CS → 从设备的CS(低电平时激活从设备)。
-
若从设备无需片选,可接地(始终激活);多从机时需独立CS线。
-
烧录固件并启动设备
-
通过串口发送 AT 命令,格式如下:

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

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 测试循环次数,不属于驱动公共数据结构。
核心业务流程
- 配置从机 CLK、MOSI、MISO、CS 引脚的输入使能和复用模式。
- 填写从机模式的
spi_attr_t、spi_extra_attr_t,清理旧状态后调用uapi_spi_init()。 - 初始化
spi_xfer_data_t和 DMA 参数,在主机产生时钟前调用从机收发接口等待数据。 - 每次传输后按协议校验接收数据并准备下一帧;完成后关闭 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
新demo完成后,逐项验收:
- 设备启动后串口输出
my_spi_slave_app start。 - 输出 SPI 传输的接收数据。
- 最后输出
my_spi_app done。
app_run运行配置
app_run应用默认是关闭的,如需启用此应用,需用户手动在acore.prelds文件中添加 KEEP (*(SORT(.zinitcall.app_run*.init))) 具体参考示意图如下:

注意事项
引脚配置注意事项:
- 确保选择的 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 相关配置参数 |