SPI¶
1. SPI驱动说明¶
1.1 简介¶
SPI(Serial Peripheral Interface,串行外设接口)是一种高速、全双工、同步通信总线.
思澈平台SPI总线特性主要包括:
支持4到32Bit的数据位宽;
HPSYS 中的 SPI 最大时钟频率为 48MHz,LPSYS 中的 SPI 最大时钟频率为 8MHz;
发送和接收都支持(polling)/中断(INT)/DMA三种模式;
支持3线SPI或4线SPI;
支持主模式和从模式;
支持半双工和全双工通讯;
支持配置时钟极性相位;
支持配置数据大端和小端;
支持SPI/SSP/Microwire;
SPI 总线的工作过程,可以参考 SPI 总线设备
1.2 SPI总线 资源分布¶
思澈平台最多支持 4 个SPI实例,其中:2个在HCPU上,2个在 LCPU;
具体分布如下:
平台 |
HCPU |
LCPU |
|---|---|---|
52x |
SPI1/SPI2 |
|
56x |
SPI1/SPI2 |
SPI3/SPI4 |
55x |
SPI1/SPI2 |
SPI3/SPI4 |
58x |
SPI1/SPI2 |
SPI3/SPI4 |
SPI总线名称依次为“spi1”~“spi4”;
SPI总线的配置信息还可以参考spi_config.h,位于:solution2.0\sdk\customer\boards\include\config 下 sf32lb52x、sf32lb56x、sf32lb55x、sf32lb58x等目录下;
备注
HCPU可以使用LCPU的SPI,但LCPU只能使用LCPU的SPI
1.3 SPI 三种工作模式¶
SPI总线支持询(polling)/中断(INT)/DMA三种工作模式;
模式 |
CPU占用率 |
代码复杂度 |
适用场景 |
|---|---|---|---|
轮询 |
高,CPU持续参与,需不断检查状态,占用CPU资源 |
低 |
低速、小数据量、简单应用; |
中断 |
中,CPU无需持续查询状态,仅在事件发生时响应中断,减少CPU等待时间 |
低 |
中等数据量、实时性要求较高; |
DMA |
低,DMA控制器直接管理数据传输,CPU无需参与数据搬运 |
高 |
高速、大数据量、高吞吐量场景; |
备注
DMA模式,仅作用于数据段,适合高吞吐,低CPU占有率,通讯过程中不需要CPU参与,被CPU打断的概率低;
1.4 SPI 功能配置¶
1.4.1 SPI 功能配置说明¶
SPI总线支持的功能都可以配置。
配置功能通过结构体struct rt_spi_configuration实现。
struct rt_spi_configuration
{
rt_uint8_t mode; //配置相位和极性,主模式或从模式,数据大端或小端;
rt_uint8_t data_width; //配置数据位宽
rt_uint16_t frameMode; //配置SPI总线类型,cs有效电平,3线或4线,SPI ready, SPI
rt_uint32_t max_hz; //SPI总线时钟
};
配置详细说明:
mode
用途:配置相位和极性,主模式或从模式;
其中:
bit0 相位CPHA, 0-在首个时钟变化沿采样数据, 1-在第二个时钟变化沿采样数;
bit1 极性CPOL, 0-时钟信号初始状态为低电平, 1-时钟信号的初始电平是高电平;
CPHA 和 CPOL组合对应SPI的4种工作时序模式
如下图:
宏定义:
RT_SPI_MODE_0 CPHA:0,CPOL:0;
RT_SPI_MODE_1 CPHA:1,CPOL1:0;
RT_SPI_MODE_2 CPHA:0,CPOL:1;
RT_SPI_MODE_3 CPHA:1,CPOL:1;
bit2 数据大小端, 1-大端MSG, 0-小端LSB;
宏定义:
RT_SPI_LSB 小端LSB
RT_SPI_MSB 大端MSB
bit3 主从模式,其中:0-主模式master, 1-从模式Slave;
宏定义:
RT_SPI_MASTER 主模式
RT_SPI_MASTER 从模式
mdata_width
用途:配置数据位宽
其中:驱动支持的位宽包括8,16,32;
frameMode
用途:配置SPI总线类型,3线或4线;
其中:
bit6 SI/SO pin shared 即三线模式 其中:0-四线模式,1-三线模式;
宏定义:
RT_SPI_3WIRE
bit8~bit9 总线类型,支持:SPI/SSP/Microwire; 其中:0-SPI, 1-SSP, 2-Microwire;
宏定义:
RT_SPI_MOTO 对应SPI
RT_SPI_TI 对应SSP
RT_SPI_NM 对应Microwire
其他bit为强制默认配置,用户可以不用关注
1.4.2 SPI 功能配置示例¶
假设SPI 需求:spi master 4线 SPI_MODE_0 8bit数据位宽 时钟8M;
配置如下:
rt_spi_configuration spi_cfg;
spi_cfg.mode = RT_SPI_MODE_0 | RT_SPI_MSB | RT_SPI_MASTER; //master 大端 SPI_MODE_0
spi_cfg.data_width = 8; //数据位宽 8
spi_cfg.frameMode = RT_SPI_MOTO; // SPI
spi_cfg.max_hz = 8000000; // 时钟8M
1.5 SPI 总线硬件说明¶
SPI 4线 和3线 pin定义对比
SPI |
时钟 |
芯片选择 |
通讯pin |
|---|---|---|---|
4线 |
SPIx_CLK |
SPIx_CS |
SPIx_DIO/SPIx_DI |
3线 |
SPIx_CLK |
SPIx_CS |
SPIx_DIO |
3线SPI时,发送/接收会复用SPIx_DIO;
4线SPI时,发送使用SPIx_DIO,接收使用SPIx_DI;
1.6 SPI 总线接口¶
SPI打开、配置和通讯,相关接口如下
函数 |
描述 |
|---|---|
rt_device_find() |
根据SPI总线设备名称查找设备获取设备句柄 |
rt_device_open() |
打开SPI总线或设备 |
rt_device_close() |
关闭SPI总线或设备 |
rt_spi_bus_attach_device() |
挂载SPI设备到SPI总线 |
rt_spi_configure() |
配置SPI设备 |
rt_spi_take_bus() |
获取SPI总线控制权 |
rt_spi_release_bus() |
释放SPI总线控制权 |
rt_spi_take() |
手动配置CS |
rt_spi_release() |
手动释放CS |
rt_spi_transfer_message() |
自定义传输数据 |
rt_spi_transfer() |
传输一次数据 |
rt_spi_send() |
发送一次数据 |
rt_spi_recv() |
接受一次数据 |
rt_spi_send_then_send() |
连续两次发送 |
rt_spi_send_then_recv() |
先发送后接收 |
1.6.1 几种通讯接口的应用场景¶
函数 |
特点 |
使用场景 |
|---|---|---|
rt_spi_transfer_message() |
自定义传输message,应用最灵活,但配置相对麻烦 |
复杂或特殊的传输时序 |
rt_spi_transfer() |
只需自定义传输buff,支持半双工和全双工 |
通用的传输接口 |
rt_spi_transfer()/rt_spi_recv() |
半双工接口,单独发送或接收 |
半双工通用接口 |
rt_spi_send_then_send() |
半双工接口,一般用于写命令,先发命令或地址,再发要写入的参数 |
寄存器写 |
rt_spi_send_then_recv() |
半双工接口,一般用于读命令,先发命令或地址,再读取参数 |
寄存器读 |
1.6.2 CS的控制¶
SPI驱动默认都是硬件自动控制CS。
如果需要手动配置CS,需要rt_spi_transfer_message()配合rt_spi_take()/rt_spi_release()来实现;
应用示例:
rt_spi_take() //cs 拉低
struct rt_spi_message message;
/*cs_take和cs_release都为0时,关闭硬件自动控制cs*/
message.cs_take = 0;
message.cs_release = 0;
//...
rt_spi_transfer_message();
rt_spi_release() //cs 拉高
1.6.3 SPI跨线程调用¶
SPI跨线程调用的时候,需要调用rt_spi_take_bus()获取SPI总线的使用权;
使用完毕后,必须调用调用rt_spi_release_bus()释放SPI总线的使用权;
1.6.4 SPI总线使用流程¶
1.7 SPI的使用¶
SPI的使用需经过配置选型、硬件绑定、驱动调用三个核心步骤,以下为详细说明:
1.7.2 配置 Pinmux(绑定 IO 与 SPI总线)¶
Pinmux 配置的核心作用是建立物理 IO 引脚与目标SPI总线的硬件映射关系,告知芯片“哪个 IO 负责SPI通讯”。只有完成绑定,SPI总线才能和SPI设备进行通讯。
以4线spi配置示例
PA28 作为 SPI1_CLK(SPI1总线的时钟线);
PA29 作为 SPI1_CS(SPI1总线的芯片选择); PA24 作为 SPI1_DIO(SPI1总线的输出);
PA25 作为 SPI1_DI(SPI1总线的输入);
配置代码如下:
// 函数:HAL_PIN_Set(引脚标识, 功能别名, 上下拉配置, 核心选择)
HAL_PIN_Set(
PAD_PA28, // 物理 IO 引脚 PA28
SPI1_CLK, // SPI1总线的时钟线
PIN_NOPULL, // 上下拉配置(SPI master 作为输出, 配置NOPULL)
1 // 1:引脚位于HCPU;0:引脚位于LCPU
);
HAL_PIN_Set(
PAD_PA29, // 物理 IO 引脚 PA29
SPI1_CS, // SPI1总线的芯片选择
PIN_NOPULL, // 上下拉配置(SPI master 作为输出, 配置NOPULL)
1 // 1:引脚位于HCPU;0:引脚位于LCPU
);
HAL_PIN_Set(
PAD_PA24, // 物理 IO 引脚 PA24
SPI1_DIO, // SPI1总线的数据输出线
PIN_NOPULL, // 上下拉配置(SPI master 作为数据输出, 配置NOPULL)
1 // 1:引脚位于HCPU;0:引脚位于LCPU
);
HAL_PIN_Set(
PAD_PA25, // 物理 IO 引脚 PA25
SPI1_DI, // SPI1总线的数据输入线
PIN_PULLUP, // 上下拉配置(SPI master 作为数据输入, 配置PULLUP)
1 // 1:引脚位于HCPU;0:引脚位于LCPU
);
备注
3线SPI 数据线只使用SPIx_DIO,不会用到SPIx_DI;
1.7.3 应用SPI总线¶
以“TF卡使用SPI1总线”为例,代码参考:\sdk\rtos\rtthread\components\drivers\spi\spi_msd.c
/*绑定sdcard 设备到spi1总线*/
rt_hw_spi_device_attach("spi1", "sdcard");
/*查找spi设备"sdcard"*/
rt_device_t spi_dev = rt_device_find("sdcard");
/*打开spi设备"sdcard"*/
rt_device_open(spi_dev, RT_DEVICE_FLAG_RDWR);
/*配置spi设备"sdcard"*/
struct rt_spi_configuration cfg;
cfg.data_width = 8;
cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_3 | RT_SPI_MSB; /* SPI Compatible Mode 3 */
cfg.max_hz = 400 * 1000; /* 400K/s */
cfg.frameMode = RT_SPI_MOTO;
rt_spi_configure(msd->spi_device, &cfg);
/*spi设备"sdcard"通讯*/
rt_spi_take();//获取SPI总线
rt_spi_transfer_message();//传输
rt_spi_release();//释放SPI总线
1.8 SPI 中断模式的配置¶
SPI 中断模式,可以在rt_device_open(bus_handle, flag_open);时,通过flag_open来区分;
轮询模式
flag_open = RT_DEVICE_FLAG_RDWR
中断模式
flag_open = RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_INT_TX
以下显示所有支持的flag
#define RT_DEVICE_FLAG_RDONLY 0x001 /**< read only */
#define RT_DEVICE_FLAG_WRONLY 0x002 /**< write only */
#define RT_DEVICE_FLAG_RDWR 0x003 /**< read and write */
#define RT_DEVICE_FLAG_INT_RX 0x100 /**< INT mode on Rx */
#define RT_DEVICE_FLAG_DMA_RX 0x200 /**< DMA mode on Rx */
#define RT_DEVICE_FLAG_INT_TX 0x400 /**< INT mode on Tx */
#define RT_DEVICE_FLAG_DMA_TX 0x800 /**< DMA mode on Tx */
1.9 SPI DMA模式的配置¶
DMA模式配置包括menuconfig使能SPI总线DMA,配置dma_config,SPI总线flag_open包含RT_DEVICE_FLAG_DMA_TX /RT_DEVICE_FLAG_DMA_RX;
1.9.2 配置dma_config¶
在dma_config.c中,配置SPI相关的宏, SPI TX和SPI RX会占用不同的DMA通道。
#define SPI1_DMA_RX_IRQHandler DMAC1_CH3_IRQHandler /*DMA中断回调*/
#define SPI1_RX_DMA_IRQ_PRIO 0 /*DMA中断优先级*/
#define SPI1_RX_DMA_INSTANCE DMA1_Channel3 /*DMA中断通道实例*/
#define SPI1_RX_DMA_IRQ DMAC1_CH3_IRQn /*DMA中断*/
#define SPI1_RX_DMA_REQUEST DMA_REQUEST_29 /*SPI1_RX的DMA设备序号,序号唯一*/
#define SPI1_DMA_TX_IRQHandler DMAC1_CH4_IRQHandler /*DMA中断回调*/
#define SPI1_TX_DMA_IRQ_PRIO 0 /*DMA中断优先级*/
#define SPI1_TX_DMA_INSTANCE DMA1_Channel4 /*DMA中断通道实例*/
#define SPI1_TX_DMA_IRQ DMAC1_CH4_IRQn /*DMA中断*/
#define SPI1_TX_DMA_REQUEST DMA_REQUEST_28 /*SPI1_TX的DMA设备序号,序号唯一*/
下图展示了所有平台的SPI总线的DMA_REQUEST(悬空部分表格表示对应平台没有对应的SPI总线或SPI总线不支持DMA)
SPI总线 |
52x |
55x |
56x |
58x |
|---|---|---|---|---|
SPI1_TX |
DMAC1_28 |
DMAC1_28 |
DMAC1_28 |
DMAC1_28 |
SPI1_RX |
DMAC1_29 |
DMAC1_29 |
DMAC1_29 |
DMAC1_29 |
SPI2_TX |
DMAC1_30 |
DMAC1_30 |
DMAC1_30 |
DMAC1_30 |
SPI2_RX |
DMAC1_31 |
DMAC1_31 |
DMAC1_31 |
DMAC1_31 |
SPI3_TX |
DMAC2_16 |
DMAC2_16 |
DMAC3_16 |
|
SPI3_RX |
DMAC2_17 |
DMAC2_17 |
DMAC3_17 |
|
SPI4_TX |
DMAC2_18 |
DMAC2_18 |
DMAC3_18 |
|
SPI4_RX |
DMAC2_19 |
DMAC2_19 |
DMAC3_19 |
1.9.3 配置dma_config SPI 总线flag_open包含RT_DEVICE_FLAG_DMA_TX /RT_DEVICE_FLAG_DMA_RX¶
参考代码如下:
rt_device_open(spi_dev, RT_DEVICE_FLAG_DMA_RX | RT_DEVICE_FLAG_DMA_TX | RT_DEVICE_FLAG_RDWR);
1.9.4 SPI DMA通讯的buff要求¶
SPI DMA 通讯一般需要把读写buff放到sram,以满足通讯效率。
buff放到sram的两种方法:
通过声明的方式的让buff变量处于sram;
例如:
RETM_BSS_SECT_BEGIN(spi_buf)
static uint8_t sram_buff[12] RETM_BSS_SECT(sram_buff);
RETM_BSS_SECT_END
从heap申请sram内存;
例如:
void * buff = app_sram_alloc(12);

