SPI使用说明
REVISION HISTORY¶
Revision No. | Description |
Date |
---|---|---|
1.0 | 04/18/2023 | |
1.1 | 04/08/2025 |
1. 概述¶
Serial Peripheral Interface (串行外围设备接口),简称SPI, 是 Motorola 公司推出的一种高速同步串行接口技术,主要应用在EEPROM,Flash,实时时钟(RTC),数模转换器(ADC),数字信号处理器(DSP) 等设备。
SPI驱动简要框架如上图,Sigmaster driver向SPI Core注册控制器及设备,用户APP通过SPI device创建的文件接口与底层硬件的交互,进而实现读写功能。

MSPI,即Master spi device,是SigmaStar提供的专门作为spi通信主设备的IP,可实现与各种外接的spi slave device通讯,满足绝大部分spi通讯协议设备的需求。
2. 关键字说明¶
-
MISO / SDO:Master input slave output 主机输入,从机输出(数据来自从机)
-
MOSI / SDI:Master output slave input 主机输出,从机输入(数据来自主机)
-
SCLK / SCK : Serial Clock 串行时钟信号,由主机产生发送给从机
-
CS / SS:Slave Select 片选信号,由主机发送,以控制与哪个从机通信,通常是低电平有效信号
-
CPOL / CKP:时钟极性,表示时钟的默认状态下的电平高低状态
-
CPHA / CKE:时钟相位,表示采集数据时是在时钟信号的具体相位
-
SPI Mode :SPI的时钟极性和相位的配置可以输出4种SPI模式
SPI Mode CPOL CPHA 0 [00] 0 0 1 [01] 0 1 2 [10] 1 0 3 [11] 1 1
3. 功能描述¶
-
MSPI(Master SPI)只能作为master device,不能作为slave device
-
硬件支持2组MSPI,支持Motorola SPI标准时序,MSPI的寄存器bank和支持最大软件片选如下:
MSPI Bus bank addr cs_num 0 1110H 2 1 1119H 2 -
FIFO mode下支持全双工读写/半双工读写,DMA mode下仅支持半双工读写
-
支持4线通讯(MISO + MOSI + SCLK + CS)和3线通讯(MOSI + SCLK + CS)
-
8字节读写缓冲区(FIFO mode),字节传输1bit到16bit可配置位宽度
-
支持GPIO片选,需要从dtsi当中添加,详细方法见6.2.3. cs-ext章节
-
Source Clock:12M、104M、108M、144M,实际SPI的通讯频率可选档位则是每种Source Clock分频的8个档位(分频系数为2、4、8、16、32、64、128、256),共计32个档位,因此SPI通讯频率范围是46875K~72MHz,目标频率可通过赋值struct mspi_setup结构体中的max_speed_hz变量进行设定(参考6.3.1.用户态读写SPI章节);而实际的通讯频率会选择最接近目标频率但小于目标频率的档位。频率档位表如下。
NO. SPI Clock Rate 1 46875 2 93750 3 187500 4 375000 5 421861 6 421875 7 562500 8 750000 9 843723 10 843750 11 1125000 12 1500000 13 1687446 14 1687500 15 2250000 16 3000000 17 3374892 18 3375000 19 4500000 20 6000000 21 6749785 22 6750000 23 9000000 24 13499571 25 13500000 26 18000000 27 26999143 28 27000000 29 36000000 30 53998287 31 54000000 32 72000000
4. 硬件连接¶
MSPI只支持一主多从架构,顾名思义一路总线上面可以只有一个主设备和一个或多个从设备,从设备选择信号线,常称为片选信号线,也称为CS、SS,以下用 SS 表示。 当有多个 SPI 从设备与 SPI 主机相连时,设备的其它信号线 SCLK、MOSI 及 MISO 同时并联到相同的 SPI 总线上,即无论有多少个从设备,都共同只使用这 3 条线;而每个从设备都有独立的这一条 SS 信号线,本信号线独占主机的一个引脚,即有多少个从设备,就有多少条片选信号线。 SPI 协议中没有设备地址,它使用 SS 信号线来寻址,当主机要选择从设备时,把该从设备的 SS 信号线设置为低电平(默认为低电平),该从设备即被选中,即片选有效,接着主机开始与被选中的从设备进行 SPI 通讯。所以SPI 通讯以 SS 线置低电平为开始信号,以 SS 线被拉高作为结束信号。

5. Uboot用法介绍¶
5.1. Uboot Config配置¶
在编译Uboot时需要选择的配置如下:
SigmaStar drivers-> <*> SigmaStar MSPI-> Device drivers-> Generic Driver Options-> <*> Enable Driver Model Device drivers-> <*> SPI Support-> <*> Enable Driver Model for SPI drivers Command line interface-> Device access commands-> <*> sspi - Command to access spi device
5.2. Uboot DTS配置¶
spi0: spi0@1F222000 { compatible = "sstar,mspi"; reg = <0x1F222000 0x200>; mspi-group = <0>; use-dma = <0>; cs-num = <2>; cs-ext = <PAD_UNKNOWN>; 4to3-mode; clk-out-mode = <27000000>; status = "okay"; };
属性 | 描述 | 备注 |
---|---|---|
compatible | 用于匹配驱动进行驱动注册,需与代码中一致 | 禁止修改 |
reg | 用于指定SPI寄存器bank的地址 | 不需要修改 |
mspi-group | 用于指定SPI外设编号序列号 | 不需要修改 |
use-dma | 用于指定是否使能DMA模式 | 可根据需要修改 |
cs-num | 用于指定Engine自带的cs pad的数量 | 和cs-ext配合使用 |
cs-ext | 用于指定除Engine自带的cs外要使用的pad index | 可根据需要修改 |
4to3-mode | 用于指定是否开启4Wires作为3Wires使用 | 可根据需要修改 |
clk-out-mode | 用于指定是否开启clk-out-mode | 可根据需要修改 |
status | 用于选择是否使能SPI master驱动 | 可根据需要修改 |
5.3. Uboot cmd参数说明及实例截图¶
参数 | 设定值 | 说明 |
---|---|---|
1 | sspi | 命令名 |
2 | 0:0.1@750000 | SPI总线:CS:SPI模式:频率 |
3 | 16 | 发送的数据长度为16bit |
4 | 55 | 发送的16进制数据0x55 |
6. Kernel用法介绍¶
6.1. Kernel Config配置¶
在编译Kernel时需要选择的配置如下:
Device drivers-> [*] SStar SoC platform drivers-> <*> SStar MSPI driver Device drivers-> [*] SPI support-> <*> User mode SPI device driver support
6.2. Dts配置¶
spi0: spi@1f222000 { compatible = "sstar,mspi"; mspi-group = <0>; clocks = <&CLK_mspi0>; reg = <0x0 0x1F222000 0x0 0x200>; #address-cells = <1>; #size-cells = <0>; interrupts = <GIC_SPI INT_IRQ_MSPI_0 IRQ_TYPE_LEVEL_HIGH>; dma-enable; cs-num = <1>; //cs-ext = <PAD_UNKNOWN>; //4to3-mode; //clk-out-mode = <27000000>; status = "ok"; spidev0@0 { compatible = "lwn,bk4"; reg = <0>; }; spidev0@1 { compatible = "lwn,bk4"; reg = <1>; }; }; spi1: spi@1f223200 { compatible = "sstar,mspi"; mspi-group = <1>; clocks = <&CLK_mspi1>; reg = <0x0 0x1F223200 0x0 0x200>; #address-cells = <1>; #size-cells = <0>; interrupts = <GIC_SPI INT_IRQ_MSPI_1 IRQ_TYPE_LEVEL_HIGH>; dma-enable; cs-num = <1>; //cs-ext = <PAD_UNKNOWN>; //4to3-mode; //clk-out-mode = <27000000>; status = "ok"; spidev0@0 { compatible = "lwn,bk4"; reg = <0>; }; spidev0@1 { compatible = "lwn,bk4"; reg = <1>; }; };
SPI master驱动中支持配置的属性如下:
属性 | 描述 | 备注 |
---|---|---|
compatible | 用于匹配驱动进行驱动注册,需与代码中一致 | 禁止修改 |
reg | 用于指定SPI寄存器bank的地址 | 不需要修改 |
interrupts | 用于指定使用的硬件中断号及属性 | 不需要更改 |
clocks | 用于指定使用的时钟源 | 不需要更改 |
mspi-group | 用于指定SPI外设编号序列号 | 不需要修改 |
dma-enable | 用于指定是否使能DMA模式,注释表示fifo mode | 可根据需要修改 |
clk-out-mode | 用于指定是否开启clk-out-mode | 可根据需要修改 |
cs-num | 用于指定Engine自带的cs pad的数量 | 和cs-ext配合使用 |
cs-ext | 用于指定除Engine自带的cs外要使用的pad index | 可根据需要修改 |
4to3-mode | 用于指定是否开启4Wires作为3Wires使用 | 可根据需要修改 |
status | 用于选择是否使能SPI master驱动 | 可根据需要修改 |
spidev | 用于配置spidev设备的节点个数 | 可根据需要增删 |
6.2.1. dma mode¶
SigmaStar SPI Master支持两种基本通信模式:fifo mode和dma mode。当SPI Master处于fifo mode工作模式时,SPI Master驱动将需要发送的数据写入SPI Master的发送buffer中,同时将接收到的数据从接收buffer中读出。发送buffer和接收buffer为SPI Master外设中的寄存器,发送buffer和接收buffer的容量为各8bytes。由于SPI Master工作在fifo mode时,需要软件参与发送buffer和接收buffer的操作,所以波形会受到软件调度的影响,在每次发送的之间会产生一定间隔。
当SPI Master处于dma mode工作模式时,驱动只需将需要发送的数据的地址和接收的数据需要存放的地址设置到SPI Master DMA相关的寄存器中后,SPI Master会自动连续发送和接收数据,此过程不需要软件参与。因此当SPI Master工作在dma mode时,SPI的波形连续性较好。
当SPI Master和SPI Device通讯时发送或接收的数据量较少,可以考虑使用fifo mode,工作效率较高。当SPI Master和SPI Device通讯时发送或接收的数据量较大,如接收SPI Sensor数据等等,此类大量数据通讯如果使用fifo mode会使系统的CPU占用率很高,影响系统效率,在大量数据通讯的场景下可以考虑使用dma mode。
[注意] 当前SigmaStar SPI Master dma mode只支持半双工(half-duplex)的工作模式,SPI Master的发送和接收共用同一个dma channel。当设备需要使用全双工(full-duplex)通讯时,只能使用fifo mode的工作模式。dma-enable属性指定的是工作在半双工模式下使用fifo mode或者dma mode,当Device驱动需要使用全双工通讯时,Master驱动会自动切换到fifo mode,可以在dma mode打开时,通过传输的参数来启用全双工,所以如有希望使用全双工模式时,也可以指定dma-enable属性。
6.2.2. cs-num¶
最大值硬件预设的cs数量,cs-num = n,软件片选号就是MSPI的片选0 ~ n-1;如要使用MSPI软件片选cs0,cs1,需要配置cs-num = 2。
6.2.3. cs-ext¶
由driver实现的额外添加cs引脚的功能。当硬件预设cs引脚不够用时,可以在dtsi当中配置额外cs引脚来作为扩充,cs-ext片选序号从cs-num的值开始,如cs-num = 2,那么第一个cs-ext引脚为cs2。配置额外cs方法为打开dtsi节点下的cs-ext属性并配置所要作为扩充为cs的pad id,同时,建议在xxx-padmux.dtsi当中也把对应的pad配置为GPIO MODE,如下为把PAD_SAR_ADC17作为cs的扩充:
<PAD_SPI0_CK PINMUX_FOR_SPI0_MODE_1 MDRV_PUSE_SPI0_CK>, <PAD_SPI0_DI PINMUX_FOR_SPI0_MODE_1 MDRV_PUSE_SPI0_DI>, <PAD_SPI0_DO PINMUX_FOR_SPI0_MODE_1 MDRV_PUSE_SPI0_DO>, <PAD_SPI0_CZ PINMUX_FOR_SPI0_MODE_1 MDRV_PUSE_SPI0_CZ>, <PAD_PM_INTOUT PINMUX_FOR_SPI0_CZ1_MODE_1 MDRV_PUSE_SPI0_CZ2>, <PAD_I2C0_SCL PINMUX_FOR_GPIO_MODE MDRV_PUSE_SPI0_CZ3>,
6.2.4. clk-out-mode¶
clk-out-mode为SigmaStar SPI Master所支持的一种特殊模式。当SPI Master处于clk-out-mode时,SPI Master的MOSI和MISO不再按给定的数据发送或接收数据,SPI Master的Clock信号会不断输出方波。此功能的应用场景为使用SPI Master输出的clock作为其他Device的工作Clock。clk-out-mode属性的值用于指定输出clock的频率,配置clk-out-mode只需要在dtsi当中把对那个的clk-out-mode属性打开并配置相应要输出的波形频率就可以:
clk-out-mode = <3750000>; //使能clk-out-mode,输出3.75M频率波形
可选的输出频率与MSPI正常工作的频率一样,参考频率档位表。
6.2.5. 4 to 3 mode¶
某些平台的SPI Master 3-wires mode不支持dma mode,如果此时有3-wires dma mode的通讯需求时可以开启4 to 3 mode模式。硬件上将Master的MOSI和MISO短接并连接至Device的SDAT,如下图:

当驱动在读数据时,会将MOSI自动切换为GPIO Input Mode从而不干扰MISO的波形输入,从而达到将四线模式用作三线模式。如果要使用此功能,只要把dtsi节点当中的4to3-mode属性打开就可以,即
4to3-mode; //使能4to3-mode
注意 : 4to3 mode的MSPI的引脚都不能作为其它mode。 3线模式的MISO可以作为其它功能,前提是mode的优先级比MSPI高,如gpio。
6.2.6. spidev¶
如上面6.2. Dts配置所示,如果spi0节点下有spidev子节点,reg属性表示选择片选号。那么当我们开启CONFIG_SPI_SPIDEV时,就可以在/dev/目录下,看到spidev0.0(mspi0 cs0),spidev1.0(mspi1 cs0),两个节点,即spidev##bus.##cs。如果配置cs-num和cs-ext片选个数之和为2,才会生成spidev0.1(mspi0 cs1)和spidev1.1(mspi1 cs1)。
6.3. PADMUX配置¶
MSPI在Uboot及Kernel环境下的padmux配置方法一致,只需要根据选择的引脚在对应的padmux.dtsi中加入如下所示的代码,以MSPI0举例,每种PADMUX模式都需要配置4个引脚:
// MODE 1 <PAD_GPIOE_14 PINMUX_FOR_MSPI0_MODE_1 MDRV_PUSE_SPI0_CK>; <PAD_GPIOE_16 PINMUX_FOR_MSPI0_MODE_1 MDRV_PUSE_SPI0_DI>; <PAD_GPIOE_17 PINMUX_FOR_MSPI0_MODE_1 MDRV_PUSE_SPI0_DO>; <PAD_GPIOE_15 PINMUX_FOR_MSPI0_MODE_1 MDRV_PUSE_SPI0_CZ>;
第一列为引脚索引号,可以在/drivers/sstar/inlcude/{chipname}/gpio.h中查到;
第二列为模式定义,在/drivers/sstar/gpio/{chipname}/hal_pinmux.c中m_hal_gpio_st_padmode_info_tbl数组里,罗列了所有引脚的复用关系,查询该引脚支持哪些复用功能可以查询该数组;
第三列为引脚及搭配模式的索引名称,可在/drivers/sstar/include/drv_puse.h里查询。
6.4. 模块使用介绍¶
内核有自带一套用于测试MSPI Master的Device Driver 以及用户程序:drivers/spi/spidev.c 和tools/spi/spidev_test.c。
6.4.1. 用户态读写SPI¶
-
要使用内核自带测试程序spidev_test.c,需要修改配置文件打开 CONFIG_SPI_SPIDEV 选项和在dts种配置spidev子节点,最终内核会在/dev文件夹下生成spidevX.X文件。
-
用户态测试用例,在tools/spi/目录下执行make获取执行文件spidev_test。
spidev_test常用的操作有:
参数 描述 -D 用于选择操作的设备节点,如:/dev/spidev0.0,默认为:/dev/spidev1.1 -s 用于选择spi时钟速率,单位为Hz -H 用于和 -O 搭配选择spi的通讯时序 -O 用于和 -H 搭配选择spi的通讯时序 -C 用于指定CS为高有效 -L 用于指定字节序为LSB -3 用于指定通讯模式为三线模式 -v 用于指定打印出发送&接收的数据内容 -i 用于指定发送的文件(二进制文件) -o 用于指定接收的数据保存成文件的路径(二进制文件) -p 用于指定发送的数据,如:“1234\xde\xad” 当前驱动所支持的功能上表所列,具体的使用方法可以参考help命令:
spidev_test help
。参数可以自由组合,请根据需要调整。 -
spidev_test核心发送/接收函数示例如下:
static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len) { int ret; int out_fd; struct spi_ioc_transfer tr = { .tx_buf = (unsigned long)tx, .rx_buf = (unsigned long)rx, .len = len, .delay_usecs = delay, .speed_hz = speed, .bits_per_word = bits, }; if (mode & SPI_TX_OCTAL) tr.tx_nbits = 8; else if (mode & SPI_TX_QUAD) tr.tx_nbits = 4; else if (mode & SPI_TX_DUAL) tr.tx_nbits = 2; if (mode & SPI_RX_OCTAL) tr.rx_nbits = 8; else if (mode & SPI_RX_QUAD) tr.rx_nbits = 4; else if (mode & SPI_RX_DUAL) tr.rx_nbits = 2; if (!(mode & SPI_LOOP)) { if (mode & (SPI_TX_OCTAL | SPI_TX_QUAD | SPI_TX_DUAL)) tr.rx_buf = 0; else if (mode & (SPI_RX_OCTAL | SPI_RX_QUAD | SPI_RX_DUAL)) tr.tx_buf = 0; } ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); if (ret < 1) pabort("can't send spi message"); if (verbose) hex_dump(tx, len, 32, "TX"); if (output_file) { out_fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, 0666); if (out_fd < 0) pabort("could not open output file"); ret = write(out_fd, rx, len); if (ret != len) pabort("not all bytes written to output file"); close(out_fd); } if (verbose) hex_dump(rx, len, 32, "RX"); }
-
当struct spi_ioc_transfer tr中.rx_buff不为null,.tx_buff为null时,MSPI Master执行的为半双工读操作;当.rx_buff为null,.tx_buff不为null时,执行的为半双工写操作;当.rx_buff和.tx_buff都不为null时,即使dma mode被打开了,执行的也是fifo mode的全双工读写操作。对应的波形示意图如下:
图7-2 waveform diagram图中MOSI对应的为.tx_buff中的数据,MISO对应的为.rx_buff中的数据。
6.4.2. 内核态读写SPI¶
内核态读写MSPI可以参考/drivers/spi/spidev.c的实现,封装接口构建并初始化结构体spi_transfer和spi_message,然后调用内核接口spidev_sync():
spidev_sync_read(struct spidev_data *spidev, size_t len) { struct spi_transfer t = { .rx_buf = spidev->rx_buffer, .len = len, .speed_hz = spidev->speed_hz, }; struct spi_message m; spi_message_init(&m); spi_message_add_tail(&t, &m); return spidev_sync(spidev, &m); } spidev_sync_write(struct spidev_data *spidev, size_t len) { struct spi_transfer t = { .tx_buf = spidev->tx_buffer, .len = len, .speed_hz = spidev->speed_hz, }; struct spi_message m; spi_message_init(&m); spi_message_add_tail(&t, &m); return spidev_sync(spidev, &m); }
7. FAQ¶
7.1 spi通信抓不到波形¶
-
驱动是否正常加载,看启动过程是否有报错。
-
确认padmux配置是否正确。
-
确认硬件电路是否有异常,将引脚配置为GPIO输出模式,通过电压表测量GPIO高低电平能否正常跳变。
7.2 spi读不到设备的数据¶
-
确认mspi配置的时钟速率是否大于外设支持的最大时钟。
-
确认数据引脚是否连错,MSPI的DO为MISO,DI为MOSI。
-
抓发送的波形,是否符合外设需要的波形。
-
外设是否正常。
7.3 波形杂乱¶
-
确认电压是否匹配
-
可能引脚驱动能力不够,增加引脚驱动能力。
-