SPI 主机应用开发入门指南
本文档以 spi_master.c 为例,带你在 HiDiTing V100 开发板上快速跑通第一个 SPI 主机通信应用,并了解如何基于它构建自己的应用。
SPI 主机驱动背景知识
SPI 工作原理
SPI(Serial Peripheral Interface)是一种高速、全双工、同步的串行通信总线,主机通过以下信号线与从设备通信:
| 信号线 | 说明 | 方向 |
|---|---|---|
| SCLK | 时钟信号,由主机产生 | 主机→从机 |
| MOSI | 主机输出,从机输入 | 主机→从机 |
| MISO | 主机输入,从机输出 | 从机→主机 |
| CS | 片选信号(低有效) | 主机→从机 |
SPI主机驱动主要实现以下功能:
- 初始化SPI控制器硬件
- 配置通信参数(时钟频率、模式等)
- 管理数据传输过程
- 处理错误和中断
通信模式配置
SPI通信有四种工作模式,由时钟极性(CPOL)和时钟相位(CPHA)决定:
| 模式 | CPOL | CPHA | 说明 |
|---|---|---|---|
| 0 | 0 | 0 | 时钟空闲低电平,数据在第一个边沿采样 |
| 1 | 0 | 1 | 时钟空闲低电平,数据在第二个边沿采样 |
| 2 | 1 | 0 | 时钟空闲高电平,数据在第一个边沿采样 |
| 3 | 1 | 1 | 时钟空闲高电平,数据在第二个边沿采样 |
操作流程
- SPI 初始化:通过
uapi_spi_init()对 SPI 进行初始化。 - 读数据:使用
uapi_spi_master_read()读取数据。 - 写数据:使用
uapi_spi_master_write()写入数据。 - 全双工读写数据:使用
uapi_spi_master_writeread()同时读写数据 - 去初始化:通过
uapi_spi_deinit()释放资源。
数据传输机制
基础传输特性
- 同步传输:完全由主设备时钟控制,决定通信速率和时序。从机必须严格遵循主机时钟边沿采样数据。
- 全双工:可同时收发数据,主机通过MOSI发送数据的同时,通过MISO接收从机返回的数据(同一时钟周期内完成)。
- 从动模式:主机通过拉低SS/CS片选信号启动通信,无需等待从设备请求(从机无法主动触发通信)。
数据传输模式对比
| 模式 | 触发条件 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 轮询 | 主动查询 | 实现简单 | CPU占用高 | 低速简单应用 |
| 中断 | CS信号/数据就绪 | 实时响应 | 需处理中断嵌套 | 中等速率常规应用 |
| DMA | 硬件自动触发 | 解放CPU | 配置复杂 | 高速大数据量传输 |
快速跑通 SPI_master Demo
功能说明
SPI master Demo 支持通过 AT 指令进行 SPI_master 的配置和传输功能,相应的 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
使用方式
在使用该 demo 之前要正确的连接 SPI 的主机和 SPI 从机 SPI接线图如下所示:

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

注意: 先启动从设备,后启动主设备, 如果顺序相反大概率会导致主设备系统复位。
预期结果
SPI 主设备配置结果(AT+SPIMASTER)

SPI 数据传输结果

文件结构与代码走读
文件职责
| 文件 | 作用 |
|---|---|
| samples/native_samples/spi/CMakeLists.txt | 将 SPI Master、Slave 源文件构建为同一 Sample 组件。 |
| samples/native_samples/spi/spi_master.h | 定义主机串口测试参数和命令表。 |
| samples/native_samples/spi/spi_master.c | 实现主机引脚、总线属性、DMA 和收发流程。 |
| samples/native_samples/spi/README.md | 给出主从连接、命令和预期数据。 |
核心数据与常量
| 名称 | 作用 | 开发注意事项 |
|---|---|---|
spi_attr_t |
配置主从角色、总线时钟、工作频率、CPOL、CPHA、帧格式和帧长。 | 主从两端的时序、帧格式和帧长必须一致。 |
spi_extra_attr_t |
配置 DMA 选择及 QSPI 扩展字段。 | 普通 SPI 场景仍应显式初始化未使用字段,避免未定义值。 |
spi_xfer_data_t |
描述收发缓冲区和字节数。 | 缓冲区生命周期必须覆盖同步传输调用。 |
spi_dma_config_t |
配置 DMA 宽度、突发长度和优先级。 | DMA 宽度应与帧宽、地址对齐和缓冲区长度匹配。 |
CONFIG_SPI_MASTER_BUS_ID / CONFIG_SPI_TRANSFER_LEN |
选择总线并限定 Sample 收发长度。 | 改动前核对板级引脚和从设备协议。 |
spi_master_task_args_t 仅承载 AT 测试的循环次数,自定义应用可直接使用业务配置,不需要保留 AT 参数结构。
核心业务流程
- 配置 CLK、MOSI、MISO、CS 引脚复用,填写主机模式的
spi_attr_t和spi_extra_attr_t。 - 清理可能的旧状态后调用
uapi_spi_init();启用 DMA 时使用与数据宽度匹配的spi_dma_config_t。 - 填写
spi_xfer_data_t,调用主机同步收发接口;按返回值和实际协议校验接收数据,而不是只打印缓冲区。 - 完成全部传输后关闭 DMA 并调用
uapi_spi_deinit();超时和传输失败也进入同一清理路径。
AT 命令只负责输入循环次数并触发此流程。完整引脚配置、属性值和错误处理见 Sample 源文件;下一节保留面向应用开发的 app_run 入口,不重复 AT 实现。
基于 SPI_master Demo 开发自己的应用
上面的demo是使用AT指令触发运行,HiDiTing还支持app_run方式触发应用在系统启动时自动运行,
以下示例将以app_run的方式开发一个开发者自己的应用
- app_run(func) 是HiDiTing中应用层注册应用函数的宏,基于 GCC 编译器属性和自定义段区(section)自动注册来实现集中调用应用函数,
系统启动时会自动遍历所有用 app_run 注册过的函数并执行,无需在系统 main 函数里逐个调用函数。
代码清单
新建一个 SPI 主设备应用(以 my_spi_master 为例)通常需要以下改动:
- 新建
my_spi_master.c源文件 - 新建
my_spi_master.h头文件 - 新建
CMakeLists.txt源文件 - 在
my_spi_master.c中实现 SPI master 操作函数 - 在
my_spi_master.h中定义宏常量和函数声明 - 在
CMakeLists.txt源文件编译规则
my_spi_master文件结构:
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
${ROOT_DIR}/src/include/driver
)
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_master_entry(void)
{
osal_printk("my_spi_app start\n");
// step1:参数配置
spi_attr_t config = { 0 };
config.is_slave = false;
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[] = {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_master_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_master_app done\n");
}
app_run(my_spi_master_entry);
测试验证
完成一站式 CLI 环境配置后执行:
# 编译固件,编译生成的固件从 output/3322/fwpkggt 中获取 diting-community.fwpkg
fbb set-target pack_diting_community
fbb build
新demo完成后,逐项验收:
- 设备启动后串口输出
my_spi_master_app start - 能够正确发送和接收数据
- 发送的数据内容为 0x01 到 0x03
- 接收到的数据正确显示
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_master_example_cmd_register() 或 AT参数指令和参数是否正确 |
| SPI 通信失败 | 引脚配置错误或时钟频率不匹配 | 检查引脚配置和时钟频率设置 |
| DMA 模式报错 | DMA 配置错误 | 检查 DMA 相关配置参数 |