SPI - 同步串行外设接口
SPI是一种高速、高效率的串行外围设备接口。由Motorola公司提出,是一种高速、全双工、同步通信总线。SPI以主从方式工作,通常是有一个主设备和一个或多个从设备,无应答机制。
- 全双工同步串行接口 (SPI)
- Master/Slave 模式可配置
- 支持最大 100MHz 时钟频率
- 支持 SPI Mode0/1/2/3
- 支持多片选
- 片选和时钟的极性和相位可配置
- 支持 CPU(中断)或 DMA 模式传输
- 支持 TX/RX FIFO 缓存
- 支持 Standard Single/Dual/Quad 数据模式(Slave 模式不支持 Dual/Quad)
- 支持 BIT 模式,用于 3Wire 场景,支持可编程 0~32bits 帧长度(仅支持 Master 模式,且不支持 DMA 和 FIFO 功能)
- 支持 DBI 模式,用于显示设备场景,用于传输视频数据
模块配置
驱动配置
驱动位于
Allwinner BSP --->
Device Drivers --->
SPI NG Drivers --->
<*> SPI NG Driver Support for Allwinner SoCs
[*] Support driver bit-aligned feature
[*] Support driver dbi feature
[*] Support driver camera feature
[*] Support atomic xfer function
<M> SPI Slave Test Driver
<M> SPI Camera Test Driver

设备树配置
spi0: spi@40250000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "allwinner,sunxi-spi-v1.3"; # 设备的版本或类型
reg = <0x0 0x04025000 0x0 0x1000>; # 模块寄存器地址
interrupts-extended =
<&plic0 16 IRQ_TYPE_LEVEL_HIGH>; # 总线中断号、中断类型
clocks = <&ccu CLK_PLL_PERI0_300M>,
<&ccu CLK_SPI0>, <&ccu CLK_BUS_SPI0>; # 设备使用的时钟
clock-names = "pll", "mod", "bus"; # 设备使用的时钟名称
resets = <&ccu RST_BUS_SPI0>; # 设备的reset时钟
dmas = <&dma 22>, <&dma 22>; # 控制器使用的dma通道号
dma-names = "tx", "rx"; # 控制器使用通道号对应的名字
clock-frequency = <100000000>; # 控制器支持的最大时钟频率(通常为100MHz)
sunxi,spi-num-cs = <1>; # 控制器支持的最大片选数量
status = "disabled"; # 控制器是否使能
};
spi1: spi@44026000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "allwinner,sunxi-spi-v1.2";
reg = <0x0 0x44026000 0x0 0x1000>;
interrupts-extended = <&plic0 17 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&aon_ccu CLK_PLL_PERI_CKO_307M>, <&ccu CLK_SPI1>, <&ccu CLK_BUS_SPI1>;
clock-names = "pll", "mod", "bus";
resets = <&ccu RST_BUS_SPI1>;
dmas = <&dma 23>, <&dma 23>;
dma-names = "tx", "rx";
clock-frequency = <100000000>;
sunxi,spi-num-cs = <2>;
status = "disabled";
};
spi2: spi@44027000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "allwinner,sunxi-spi-v1.5";
reg = <0x0 0x44027000 0x0 0x1000>;
interrupts-extended = <&plic0 18 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&aon_ccu CLK_PLL_PERI_CKO_307M>, <&ccu CLK_SPI2>, <&ccu CLK_BUS_SPI2>, <&aon_ccu CLK_AHB>;
clock-names = "pll", "mod", "bus", "ahb";
resets = <&ccu RST_BUS_SPI2>;
dmas = <&dma 24>, <&dma 24>;
dma-names = "tx", "rx";
clock-frequency = <100000000>;
sunxi,spi-num-cs = <1>;
status = "disabled";
};
提示
以上设备树配置示例中的寄存器地址(如 0x40250000)、中断号、compatible 字符串等,可能因芯片平台不同而有所差异。具体配置请参考对应芯片的 SDK 设备树文件。
:::
为了在SPI总线驱动代码中区分每一个SPI控制器,需要在设备树中为每个SPI控制器指定别名:
aliases {
spi0 = &spi0;
};
别名形式为字符串 "spi" 加连续编号的数字,在 SPI 总线驱动程序中可以通过 of_alias_get_id 函数获取对应SPI控制器的数字编号,从而区别每一个SPI控制器。
在设备端,配置了 SPI 的引脚,配置位于 pio 节点下
&pio {
spi0_pins_default: spi0@0 {
pins = "PC12", "PC2", "PC4"; /* clk, mosi, miso */
function = "spi0";
drive-strength = <10>;
};
spi0_pins_cs: spi0@1 {
pins = "PC3";
function = "spi0";
drive-strength = <10>;
bias-pull-up; /* cs, hold, wp should be pulled up */
};
spi0_pins_sleep: spi0@2 {
pins = "PC12", "PC2", "PC4", "PC3";
function = "gpio_in";
drive-strength = <10>;
};
};
配置设备节点
&spi0 {
pinctrl-0 = <&spi0_pins_default &spi0_pins_cs>;
pinctrl-1 = <&spi0_pins_sleep>;
pinctrl-names = "default", "sleep";
sunxi,spi-bus-mode = <SUNXI_SPI_BUS_NOR>;
sunxi,spi-cs-mode = <SUNXI_SPI_CS_SOFT>;
status = "okay";
spi_board0 {
device_type = "spi_board0";
compatible = "spi-nor";
spi-max-frequency = <100000000>;
m25p,fast-read = <1>;
/*individual_lock;*/
reg = <0x0>; //CS0
spi-rx-bus-width = <4>;
spi-tx-bus-width = <4>;
status = "okay";
};
};
sunxi,spi-num-cs:定义该 SPI 控制器最多支持多少个片选。sunxi,spi-bus-mode:定义 SPI 控制器所处的状态,可配置为如下几种模式。SUNXI_SPI_BUS_MASTER:处于 Master 模式,外接 SPI Device。SUNXI_SPI_BUS_SLAVE:处于 Slave 模式,被其他 Master 访问。SUNXI_SPI_BUS_DBI:处于 DBI 模式,外接 DBI 屏幕。SUNXI_SPI_BUS_BIT:处于 BIT 模式,使用 3Wire 方式进行数据传输。SUNXI_SPI_BUS_NOR:处于 NOR 模式,用于外接 SPI Nor Flash 的情况。SUNXI_SPI_BUS_NAND:处于 NAND 模式,用于外接 SPI Nand Flash 的情况。sunxi,spi-cs-mode:定义 SPI 控制器对片选的控制方式,可配置为如下几种模式。SUNXI_SPI_CS_AUTO:硬件自动控制,不需要驱动或软件介入。SUNXI_SPI_CS_SOFT:软件手动控制,由驱动完成相关操作。- 当
sunxi,spi-bus-mode配置为SUNXI_SPI_BUS_NOR/SUNXI_SPI_BUS_NAND时,sunxi,spi-cs-mode会强制使用SUNXI_SPI_CS_SOFT模式。 spi-max-frequency是该SPI外设的最大输出时钟频率,配置时不可超过dtsi中配置的SPI控制器最大时钟频率
模块驱动
SPI 总线驱动位于 drivers/spi-ng 下
├── bit
│ ├── spi-sunxi-bit.h # bit模式头文件,定义了BIT模式所使用的寄存器信息
│ └── spi-sunxi-bit.c # bit模式源文件,包含BIT模式功能配置的寄存器读写实现等
├── calibrate
│ ├── spi-sunxi-calibrate.c # 校准功能的源文件,涉及SPI设备的初始化及校准相关操作
│ └── spi-sunxi-calibrate.h # 校准功能的头文件,声明SPI设备校准相关的接口和定义
├── camera
│ ├── spi-sunxi-camera.c # 摄像头驱动的源文件,SPI与摄像头通信相关实现
│ └── spi-sunxi-camera.h # 摄像头驱动的头文件,定义摄像头相关的寄存器和接口
├── dbi
│ ├── spi-sunxi-dbi-api.h # DBI模式API头文件,提供给上层驱动调用的接口
│ ├── spi-sunxi-dbi.h # DBI模式头文件,定义DBI模式的寄存器信息
│ └── spi-sunxi-dbi.c # DBI模式源文件,实现DBI模式功能配置的寄存器读写等
├── Kconfig # 配置文件,定义驱动的配置选项和可选功能
├── Makefile # 编译构建文件,定义如何构建和编译SPI驱动程序
├── spi-sunxi.c # 主驱动源文件,包含Linux框架要求的控制器接口和Master/Slave模式实现
├── spi-sunxi-camera-test.c # 摄像头测试示例源文件,用于测试SPI与摄像头之间的通信
├── spi-sunxi-debug.h # debug相关头文件,定义寄存器和数据打印的接口
├── spi-sunxi.h # 主要头文件,包含Linux SPI控制器定义和BYTE模式的寄存器信息
└── spi-sunxi-slave-test.c # 从模式设备驱动示例源文件,模拟一个可读写的内存设备,类似于EEPROM
SPI 内核框架
Linux中SPI体系结构分为三个层次

用户空间
包括所有使用SPI设备的应用程序,在这一层用户可以根据自己的实际需求,将spi设备进行一些特殊的处理,此时控制器驱动程序并不清楚和关注设备的具体功能,SPI设备的具体功能是由用户层程序完成的。例如,和MTD层交互以便把SPI接口的存储设备实现为某个文件系统,和TTY子系统交互把SPI设备实现为一个TTY设备,和网络子系统交互以便把一个SPI设备实现为一个网络设备,等等。当然,如果是一个专有的SPI设备,我们也可以按设备的协议要求,实现自己的专有协议驱动。同时这部分我们不用关注。
内核空间
内核空间我们同样的会分为一下三部分:
SPI 驱动核心层(SPI Core)
SPI Core是Linux内核用来维护和管理spi的核心部分,SPI Core提供操作接口函数,允许一个spi master,spi driver和spi device初始化时在SPI Core中进行注册,以及退出时进行注销。
SPI设备驱动层(SPI Device Driver)
SPI Driver是对应于spi设备端的驱动程序,通过接口函数向SPI Core进行注册,SPI Driver的作用是将spi设备挂接到spi总线上。
SPI控制器驱动层(SPI Master Driver)
SPI Master针对不同类型的spi控制器硬件,实现spi总线的硬件访问操作。SPI Master通过接口函数向SPI Core注册一个控制器。
硬件
这一层是实际的物理器件,其中包括我们的spi控制器以及与控制器相连的各个spi子设备,通过spi总线能够与cpu进行数据的交互。
驱动框架
根据SPI控制 器集成了BYTE/BIT/DBI三个IP的特性,将驱动分 3 个 IP 域和 5 个功能逻辑块:
master/slave/flash/dbi/bit。其中BIT/DBI为一一对应关系,BYTE则对应Master/Flash/Slave三个不同功能逻辑块。

Master
该模式属于BYTE模式的一种,数据的最小单位为BYTE,即一次发送会产生8个CLK将一个Byte数据全部发送出去。支持Single/Dual/Quad三种模式,常见的使用常见为通用SPI外设。
Flash
该模式属于BYTE模式的一种,但驱动专门针对Nor/Nand Flash传输过程做了优化,当外设连接的是Flash的情况下,可以提高读写性能。支持1-1-1,1-1-2,1-1-4三种SPI-Flash模式。只能使用于SPI-Flash外设。
Slave
该模式属于BYTE模式的一种,但此时控制器处于从机模式下,等待其他Master的访问,在该模式下仅支持使用Single Mode进行数据传输。 针对数据同步的问题,增加ready脚的sync机制,该机制为纯软件逻辑,需要对方的Master端的适配支持,才能正常使用。若不用ready同步机制,则需要上层应用在传输时进行相应的delay等待。