# SPI 调试指南
## SPI 概述
SPI(Serial Peripheral Interface) 通信是一种全双工、同步的通信方式，通常由一个主设备和一个或多个从设备组成。主设备通过控制时钟信号 (SCK)，数据输入信号 (MOSI)，数据输出信号 (MISO) 和片选信号 (SS) 来与从设备进行通信。其中 MOSI 可以解释为 Master Out Slave In， MISO 可以解释为 Matser In Slave Out。

## 特点
该芯片共支持 7 路 SPI 控制器，其中 6 路 (SPI0-SPI5) 位于 LSIO 子系统， 1 路 (SPI6) 位于 DSP 子系统。
所有 SPI 控制器均支持主 / 从模式。
其中 SPI1 拥有两路片选，其他的只有一路片选。

### <span id="spi_hw_desc"/> 硬件说明
所有 SPI 控制器均可以运行 Master 或者 Slave 模式，两种模式均支持中断和 DMA 配置。 DMA 配置下 Master 及 Slave 的运行限制如下：
- SPI-Master：最高频率 50MHz
- SPI-Slave：最高频率 32MHz

中断配置运行限制如下：
- 作为 SPI-Slave 端：
  - MOSI： CPU**定频** 在 1.5GHz 时，实测可以达到 32MHz
  - MISO： CPU**定频** 在 1.5GHz 时，实测可以达到 16MHz
- 作为 SPI-Master 端：
  - 均可正常通讯，可以达到 32MHz。

## <span id="func-des"/> 功能描述

### 典型应用
![image-Topological_Connection_1](_static/_images/14-SPI_Debug_Guide_zh_CN/Topological_Connection_1.png)

SPI 一个主设备连接一个从设备，使用一个片选信号。

### 功能原理

#### 通信模式
SPI 采用主从模式工作，一个主设备可以与多个从设备通信，但一次只能与一个从设备通信。主设备通过片选线 (SS/CS) 选择要通信的从设备

#### 信号线说明
SPI 通信需要至少 4 条信号线，分别是：
SCLK(Serial Clock)：时钟信号，由主设备产生，用于同步数据传输 。
MOSI(Master Output Slave Input)：主设备输出，从设备输入的数据信号线。
MISO(Master Input Slave Output)：主设备输入，从设备输出的数据信号线。
SS/CS(Slave Select/Chip Select)：从设备选择信号，用于激活特定的从设备。

#### 数据传输说明
在每个时钟周期内， SPI 设备都会发送并接收一个 bit 大小的数据，实现全双工通信。数据通常以高位 (MSB) 在前的方式传输。

#### 通信流程简述
通信开始时，主设备拉低对应从设备的片选线，选择需要通信的从设备。
主设备在时钟信号的边缘将数据发送到 MOSI 线，并在同一时钟边缘接收从设备的响应数据。
主设备侦听从设备的响应数据，并在通信结束后释放片选线。

#### <span id="clk-pol-and-pha-des"/> 时钟极性与相位说明
SPI 有四种工作模式，这些模式由时钟极性 (CPOL) 和时钟相位 (CPHA) 两个参数定义，它们控制着数据采样和时钟信号的空闲状态。

![image-Timing_CPOL0_CPHA0](_static/_images/14-SPI_Debug_Guide_zh_CN/Timing_CPOL0_CPHA0.png)\
CPOL = 0 ， CPHA = 0 ： CLK 空闲状态 = 低电平，数据在上升沿采样，并在下降沿移出。

上图给出了 CPOL = 0 ， CPHA = 0 的时序图。在此模式下，时钟极性为 0 ，表示时钟信号的空闲状态为低电平。\
此模式下的时钟相位为 0 ，表示数据在上升沿采样 ( 由橙色虚线显示 )，并且数据在时钟信号的下降沿移出 ( 由蓝色虚线显示 )。

![image-Timing_CPOL0_CPHA1](_static/_images/14-SPI_Debug_Guide_zh_CN/Timing_CPOL0_CPHA1.png)\
CPOL = 0 ， CPHA = 1 ： CLK 空闲状态 = 低电平，数据在下降沿采样，并在上升沿移出。

上图给出了 CPOL = 0 ， CPHA = 1 的时序图。在此模式下，时钟极性为 0 ，表示时钟信号的空闲状态为低电平。\
此模式下的时钟相位为 1 ，表示数据在下降沿采样 ( 由橙色虚线显示 )，并且数据在时钟信号的上升沿移出 ( 由蓝色虚线显示 )。


![image-Timing_CPOL1_CPHA1](_static/_images/14-SPI_Debug_Guide_zh_CN/Timing_CPOL1_CPHA1.png)\
CPOL = 1 ， CPHA = 1 ： CLK 空闲状态 = 高电平，数据在下降沿采样，并在上升沿移出。

上图给出了 CPOL = 1 ， CPHA = 1 的时序图。在此模式下，时钟极性为 1 ，表示时钟信号的空闲状态为高电平。\
此模式下的时钟相位为 1 ，表示数据在下降沿采样 ( 由橙色虚线显示 )，并且数据在时钟信号的上升沿移出 ( 由蓝色虚线显示 )。


![image-Timing_CPOL1_CPHA0](_static/_images/14-SPI_Debug_Guide_zh_CN/Timing_CPOL1_CPHA0.png)\
CPOL = 1 ， CPHA = 0 ： CLK 空闲状态 = 高电平，数据在上升沿采样，并在下降沿移出。

上图给出了 CPOL = 1 ， CPHA = 0 的时序图。在此模式下，时钟极性为 1 ，表示时钟信号的空闲状态为高电平。\
此模式下的时钟相位为 0 ，表示数据在上升沿采样 ( 由橙色虚线显示 )，并且数据在时钟信号的下降沿移出 ( 由蓝色虚线显示 )。

这里归纳一下时钟极性和相位的说明，方便大家查阅：

| CPOL | CPHA | 描述 |
|------|------|------|
| 0    | 0    | 时钟在空闲时为低电平，数据在时钟的第一个边沿被采样。 |
| 0    | 1    | 时钟在空闲时为低电平，但数据在第二个边沿被采样。 |
| 1    | 0    | 时钟在空闲时为高电平，数据在时钟的第一个边沿被采样。 |
| 1    | 1    | 时钟在空闲时为高电平，但数据在第二个边沿被采样。 |

### 工作方式

**主设备初始化阶段**
设置时钟频率、模式 ( 时钟极性和相位 )。

**选择从设备阶段**
主设备将目标从设备的 SS 信号拉低，使其处于激活状态。

**数据传输阶段**
主设备通过 MOSI 发送数据，从设备通过 MISO 返回数据。
数据在每个时钟脉冲下移位，同时在主设备和从设备之间交换。

**通信结束阶段**
主设备将 SS 信号拉高，结束通信。未被选中的从设备不会响应。

## 驱动代码

### Linux SPI 驱动框架介绍
-  spi driver 层：主要实现对 SPI 硬件 IP 的操作，另外还实现了 spi framework 定义的接口。

-  spi framework 层：可以理解为 spi driver 的适配层，对下层定义了一组 driver 层需要实现的接口，对上提供了通用接口屏蔽了硬件细节。

-  spi char device 层：为用户空间提供节点，方便用户空间与内核空间进行数据交换。


### SPI 驱动代码位置
驱动代码位于： drivers/spi 目录下，主要有三个文件：
- drivers/spi/spi-dw-core.c
- drivers/spi/spi-dw-mmio.c
- drivers/spi/spi-dw-dma.c


### SPI DTS 配置说明
SPI 控制器的设备树定义位于 BSP 源码中的 `kernel/arch/arm64/boot/dts/hobot/x5.dtsi` 文件内。

<font color=red> 备注：</font>
x5.dtsi 中的节点主要声明 SoC 共有特性，和具体电路板无关，一般情况下不用修改。

SPI 控制器默认关闭，请根据实际硬件情况，在对应的 DTS 文件内使能相应的 SPI 控制器。 \

以使能 SPI2 为例：
```dts
&spi2 {
	status = "okay";  /*启用 SPI2控制器*/
	pinctrl-names = "default";  /*使用默认引脚配置*/
	pinctrl-0 = <&pinctrl_spi2>;  /*引用默认引脚配置节点*/
};
```

#### DTS 配置 SPI 使用 DMA
如果需要使用 DMA，则需要在对应的 DTS 文件内绑定对应的 DMA 握手，以 SPI2 为例：
```dts
&spi2 {
	status = "okay";  /*启用 SPI2控制器*/
	pinctrl-names = "default";  /*使用默认引脚配置*/
	pinctrl-0 = <&pinctrl_spi2>;  /*引用默认引脚配置节点*/
	dma-names = "tx", "rx";  /*定义 DMA 传输和接收的名称*/
	dmas = <&axi_dmac 25>, <&axi_dmac 24>;  /*声明 DMA 握手通道*/
};
```

SPI6 绑定时需要指定 dsp_axi_dma，如下所示：
```dts
&spi6 {
	status = "okay";  /*启用 SPI2控制器*/
	pinctrl-names = "default";  /*使用默认引脚配置*/
	pinctrl-0 = <&pinctrl_dsp_spi>;  /*引用默认引脚配置节点*/
	dma-names = "tx", "rx";  /*定义 DMA 传输和接收的名称*/
	dmas = <&dsp_axi_dma 22>, <&dsp_axi_dma 21>;  /*声明 DMA 握手通道*/
};
```

SPI DMA 握手列表如下：

|                | RX | TX |
|----------------|----|----|
| SPI0           | 20 | 21 |
| SPI1           | 22 | 23 |
| SPI2           | 24 | 25 |
| SPI3           | 26 | 27 |
| SPI4           | 28 | 29 |
| SPI5           | 30 | 31 |
| SPI6(DSP_DMAC) | 21 | 22 |

## 功能使用
上述文章对驱动代码和典型的 dts 配置进行了说明，接下来换成拥有双片选的 SPI1 进行更详细的使用说明

#### 使用 SPI1 双片选的配置
首先需要在 x5.dtsi 里面确认一下 `num-cs` 节点是否为 2
```dts
spi1: spi@34010000 {
	compatible = "horizon,dwc-ssi-1.01a";  /*指定 SPI 设备1控制器兼容的硬件型号*/
	status = "disabled";  /*标记 SPI 设备1控制器为禁用状态*/
	......
	num-cs = <2>;  /*指定 SPI 设备1控制器的片选信号数量，这里特别说明一下，该芯片只有 SPI1有两个片选，其他的 SPI 写2无效*/
	......
};
```

其次我们在设备树中引用的时候可以参考如下配置
```dts
&spi1 {
	status = "okay";  /*启用 SPI 设备1控制器*/
	pinctrl-names = "default";  /*使用默认引脚配置*/
	pinctrl-0 = <&pinctrl_spi1 &pinctrl_spi1_ssn1>;  /*引用默认引脚控制配置节点，选定主设备和片选信号*/
	dma-names = "tx", "rx";  /*定义 DMA 传输和接收的名称*/
	dmas = <&axi_dmac 23>, <&axi_dmac 22>;  /*声明 DMA 握手通道*/

	spidev@0 {
		compatible = "dr,x5-spidev";  /*指定 spidev0的兼容信息*/
		spi-max-frequency = <32000000>;  /*设置 spidev0的最大频率*/
		reg = <0>;  /*指定 spidev0的寄存器索引*/
	};

	spidev@1 {
		compatible = "dr,x5-spidev";  /*指定 spidev1的兼容信息*/
		spi-max-frequency = <32000000>;  /*设置 spidev1的最大频率*/
		reg = <1>;  /*指定 spidev 的寄存器索引*/
	};
};
```
两个片选分别对应 spidev@0 和 spidev@1 ，系统正常启动之后，我们可以在找到 `/dev/spidev1.0` 和 `/dev/spidev1.1` 两个节点。


#### 时钟极性和相位设置
我们在使用的时候可能会遇到对 SPI 时钟极性和相位要求的设备，比如某些设备要求 CPOL=1 ， CPHA=1 。
那么我们基于上述的设备树设置，可以增加 `spi-cpha;`,`spi-cpol;` 两个节点。
```dts
&spi1 {
	status = "okay";  /*启用 SPI 设备1控制器*/
	pinctrl-names = "default";  /*使用默认引脚配置*/
	pinctrl-0 = <&pinctrl_spi1 &pinctrl_spi1_ssn1>;  /*引用默认引脚控制配置节点，选定主设备和片选信号*/
	dma-names = "tx", "rx";  /*定义 DMA 传输和接收的名称*/
	dmas = <&axi_dmac 23>, <&axi_dmac 22>;  /*声明 DMA 握手通道*/

	spidev@0 {
		compatible = "dr,x5-spidev";  /*指定 spidev0的兼容信息*/
		spi-max-frequency = <32000000>;  /*设置 spidev0的最大频率*/
		reg = <0>;  /*指定 spidev0的寄存器索引*/
		spi-cpha;  /*配置 spidev0时钟相位,声明即为1*/
		spi-cpol;  /*配置 spidev0时钟极性,声明即为1*/
	};

	spidev@1 {
		compatible = "dr,x5-spidev";
		spi-max-frequency = <32000000>;
		reg = <1>;
	};
};
```
增加的 `spi-cpha;`,`spi-cpol;` 两个节点，我们也可以在 `kernel\drivers\spi\spi.c` 中找到解析，这里就不过多阐述。

极性和相位可以参考上文 [ 功能描述 ](#span-id-func-des) 里的 [ 时钟极性与相位说明 ](#span-id-clk-pol-and-pha-des) 章节



### 用户态阶段使用

将使用一个代码案例来介绍 SPI 的功能验证，主要包括环境配置，测试命令执行及测试代码等。

确认内核的 CONFIG_SPI_SPIDEV 为使能状态 :
```c
/* arch/arm64/configs/hobot_x5_soc_defconfig */
...
CONFIG_SPI_SPIDEV=m
...
```

确认在当前硬件的 DTS 内需要测试的 SPI 控制器节点下创建了一个 dummy 从设备：

```dts
&spi2 {
	spidev@0 {
		compatible = "dr,x5-spidev";
		spi-max-frequency = <32000000>;
		reg = <0>;
	};
}
```

#### SPI 内部回环测试
SPI 内部回环测试仅 SPI Master 支持，其原理是 SPI 硬件 IP 的 tx fifo 将数据发给 rx fifo 从而形成回环。

我们使用 `/app/platform_samples/chip_base_test/05_spi_test` 目录下的 spidev_tc 代码程序进行测试
测试命令及结果参考如下 ( 以 SPI2 为 Slave， SPI4 为 Master)：
```bash
root@buildroot:/app/platform_samples/chip_base_test/05_spi_test# ./spidev_tc -D /dev/spidev2.0 -v -s 1000000 -e 3 -l -S 256 -I 1
spi mode: 0x20
bits per word: 8
max speed: 1000000 Hz (1000 kHz)
Userspace spi read and write test, test_len=256 iterations=1
TX | 67 C6 69 73 51 FF 4A EC 29 CD BA AB F2 FB E3 46 7C C2 54 F8 1B E8 E7 8D 76 5A 2E 63 33 9F C9 9A  |g.isQ.J.)......F|.T.....vZ.c3...|
TX | 66 32 0D B7 31 58 A3 5A 25 5D 05 17 58 E9 5E D4 AB B2 CD C6 9B B4 54 11 0E 82 74 41 21 3D DC 87  |f2..1X.Z%]..X. ^ .......T...tA!=..|
TX | 70 E9 3E A1 41 E1 FC 67 3E 01 7E 97 EA DC 6B 96 8F 38 5C 2A EC B0 3B FB 32 AF 3C 54 EC 18 DB 5C  |p.>.A..g>.~...k..8\*..;.2.<T...\|
TX | 02 1A FE 43 FB FA AA 3A FB 29 D1 E6 05 3C 7C 94 75 D8 BE 61 89 F9 5C BB A8 99 0F 95 B1 EB F1 B3  |...C...:.)...<|.u..a..\.........|
TX | 05 EF F7 00 E9 A1 3A E5 CA 0B CB D0 48 47 64 BD 1F 23 1E A8 1C 7B 64 C5 14 73 5A C5 5E 4B 79 63  |......:.....HGd..#...{d..sZ. ^ Kyc|
TX | 3B 70 64 24 11 9E 09 DC AA D4 AC F2 1B 10 AF 3B 33 CD E3 50 48 47 15 5C BB 6F 22 19 BA 9B 7D F5  |;pd$...........;3..PHG.\.o"...}.|
TX | 0B E1 1A 1C 7F 23 F8 29 F8 A4 1B 13 B5 CA 4E E8 98 32 38 E0 79 4D 3D 34 BC 5F 4E 77 FA CB 6C 05  |.....#.)......N..28.yM=4._Nw..l.|
TX | AC 86 21 2B AA 1A 55 A2 BE 70 B5 73 3B 04 5C D3 36 94 B3 AF E2 F0 E4 9E 4F 32 15 49 FD 82 4E A9  |..!+..U..p.s;.\.6.......O2.I..N.|
RX | 67 C6 69 73 51 FF 4A EC 29 CD BA AB F2 FB E3 46 7C C2 54 F8 1B E8 E7 8D 76 5A 2E 63 33 9F C9 9A  |g.isQ.J.)......F|.T.....vZ.c3...|
RX | 66 32 0D B7 31 58 A3 5A 25 5D 05 17 58 E9 5E D4 AB B2 CD C6 9B B4 54 11 0E 82 74 41 21 3D DC 87  |f2..1X.Z%]..X. ^ .......T...tA!=..|
RX | 70 E9 3E A1 41 E1 FC 67 3E 01 7E 97 EA DC 6B 96 8F 38 5C 2A EC B0 3B FB 32 AF 3C 54 EC 18 DB 5C  |p.>.A..g>.~...k..8\*..;.2.<T...\|
RX | 02 1A FE 43 FB FA AA 3A FB 29 D1 E6 05 3C 7C 94 75 D8 BE 61 89 F9 5C BB A8 99 0F 95 B1 EB F1 B3  |...C...:.)...<|.u..a..\.........|
RX | 05 EF F7 00 E9 A1 3A E5 CA 0B CB D0 48 47 64 BD 1F 23 1E A8 1C 7B 64 C5 14 73 5A C5 5E 4B 79 63  |......:.....HGd..#...{d..sZ. ^ Kyc|
RX | 3B 70 64 24 11 9E 09 DC AA D4 AC F2 1B 10 AF 3B 33 CD E3 50 48 47 15 5C BB 6F 22 19 BA 9B 7D F5  |;pd$...........;3..PHG.\.o"...}.|
RX | 0B E1 1A 1C 7F 23 F8 29 F8 A4 1B 13 B5 CA 4E E8 98 32 38 E0 79 4D 3D 34 BC 5F 4E 77 FA CB 6C 05  |.....#.)......N..28.yM=4._Nw..l.|
RX | AC 86 21 2B AA 1A 55 A2 BE 70 B5 73 3B 04 5C D3 36 94 B3 AF E2 F0 E4 9E 4F 32 15 49 FD 82 4E A9  |..!+..U..p.s;.\.6.......O2.I..N.|
Test times: 0 Data verification Successful
root@buildroot:/app/platform_samples/chip_base_test/05_spi_test#

```


#### SPI 外部回环测试

SPI 外部回环测试是指定一个 SPI Slave，一个 SPI Master，对应线连接进行的测试。 EVB 的板子 40pin 上只有 SPI2 , 所以在 EVB 上做外部回环测试的时候需要注意硬件通路。\
我们首先要找到 40pin 的 SPI2 引脚，以及原理图中的 SPI1 通路，可以看到我们的 SPI1 实际上和 JTAG 口有复用关系，所以想要进行 SPI1 和 SPI2 的回环测试，就需要\
将 JTAG 口的 SPI1 引脚和 40pin 引脚连接起来。

下图是 SPI1 与 JTAG 复用的原理图中的部分截图，可以看到 SPI1 通过了电平转换芯片接到了 JTAG 口。\
![spi_external_loopback_2](_static/_images/14-SPI_Debug_Guide_zh_CN/spi_external_loopback_2.png)

![spi_external_loopback_1](_static/_images/14-SPI_Debug_Guide_zh_CN/spi_external_loopback_1.png)

下图是 EVB 板上的 JTAG 口实物图，仅供参考。\
![spi_external_loopback_3](_static/_images/14-SPI_Debug_Guide_zh_CN/spi_external_loopback_3.png)

当我们硬件通路都搭建好之后，我们进入下一步\
这里以 SPI2 作为 Slave , EVB 板上原 JTAG 引脚配置为 SPI1 ， 作为 Master( 使用双片选中的 SPI1.1 )， 修改 SPI2 DTS 以支持 Slave 功能：

```c
&spi2 {
	status = "okay";
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_spi2>;
	spi-slave;

	slave@0 {
		compatible = "dr,x5-spidev";
		spi-max-frequency = <32000000>;
		reg = <0>;
	};
};
```

修改 SPI1 DTS 以支持 Master 功能：(SPI1 具有两个片选，所以这里我们定义了两个设备子节点，系统正常启动之后，体现在文件系统中，就会有两个设备，/dev/spi1.0 和 /dev/spi1.1 )，
注意，`num-cs = <2>;` 这个属性，上文我们有提到使用双片选的时候需要添加到 dts 里。

```c
&spi1 {
	num-cs = <2>;
	status = "okay";
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_spi1 &pinctrl_spi1_ssn1>;

	spidev@0 {
		compatible = "dr,x5-spidev";
		spi-max-frequency = <32000000>;
		reg = <0>;
	};

	spidev@1 {
		compatible = "dr,x5-spidev";
		spi-max-frequency = <32000000>;
		reg = <1>;
	};
};
```

测试命令及结果参考如下 ( 以 SPI2 为 Slave， SPI1.1 为 Master)：

```c

1 、打开一个终端，操作 SPI 从设备：

root@buildroot:~# /app/platform_samples/chip_base_test/05_spi_test/spidev_tc -D /dev/spidev2.0 -e 1 -v -S 64 -I 1
spi mode: 0x0
bits per word: 8
max speed: 500000 Hz (500 kHz)
Userspace spi read test, test_len=64 iterations=1

( 说明：上述命令执行之后，程序会一直等待，直到接收到从 SPI Master 发送的数据。)



2 、打开另一个终端，操作 SPI 主设备：
root@buildroot:~# /app/platform_samples/chip_base_test/05_spi_test/spidev_tc -D /dev/spidev1.1 -e 2 -v -S 64 -I 1
spi mode: 0x0
bits per word: 8
max speed: 500000 Hz (500 kHz)
Userspace spi write test, test_len=64 iterations=1
TX | 67 C6 69 73 51 FF 4A EC 29 CD BA AB F2 FB E3 46 7C C2 54 F8 1B E8 E7 8D 76 5A 2E 63 33 9F C9 9A  |g.isQ.J.)......F|.T.....vZ.c3...|
TX | 66 32 0D B7 31 58 A3 5A 25 5D 05 17 58 E9 5E D4 AB B2 CD C6 9B B4 54 11 0E 82 74 41 21 3D DC 87  |f2..1X.Z%]..X. ^ .......T...tA!=..|
Test times: 0
root@buildroot:~#

( 说明：上述命令执行之后， SPI 主设备就直接发送数据出去了 )



3 、这个时候可以观察到 SPI 从设备的终端会显示接收到的数据，整体状态形如下述结果：

root@buildroot:~# /app/platform_samples/chip_base_test/05_spi_test/spidev_tc -D /dev/spidev2.0 -e 1 -v -S 64 -I 1
spi mode: 0x0
bits per word: 8
max speed: 500000 Hz (500 kHz)
Userspace spi read test, test_len=64 iterations=1
RX | 67 C6 69 73 51 FF 4A EC 29 CD BA AB F2 FB E3 46 7C C2 54 F8 1B E8 E7 8D 76 5A 2E 63 33 9F C9 9A  |g.isQ.J.)......F|.T.....vZ.c3...|
RX | 66 32 0D B7 31 58 A3 5A 25 5D 05 17 58 E9 5E D4 AB B2 CD C6 9B B4 54 11 0E 82 74 41 21 3D DC 87  |f2..1X.Z%]..X. ^ .......T...tA!=..|
rate: tx 0.1kbps, rx 0.1kbps
Test times: 0
root@buildroot:~#


```
**注：在进行外部回环测试时，需要先执行 SPI Slave 程序，再执行 SPI Master 程序。假如先执行 SPI Master 程序，后执行 SPI Slave 程序，可能会由于 Master 与 Slave 不同步导致 SPI 接收数据出现丢失。如果想进行多次测试，可以写脚本多次执行测试程序，来保证 Master 与 Slave 之间的同步。**


#### SPI 通信案例
这边结合 spidev_tc 和一款 IMU 通信，给大家提供一些其他的通信参考。

在保证硬件正常的情况下，驱动正常加载之后，我们根据手册的要求，要向 0x15 寄存器中写入 0x80,

然后我们才能从 IMU 读出相应的值，示例如下：
```
root@buildroot:/app# /app/platform_samples/chip_base_test/05_spi_test/spidev_tc -D /dev/spidev1.0 -s 5000000 -e 2 -S 2 -v -p "\x15\x00"
spi mode: 0x0
bits per word: 8
max speed: 5000000 Hz (5000 kHz)
TX | 15 00 __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __  |..|
root@buildroot:/app# /app/platform_samples/chip_base_test/05_spi_test/spidev_tc -D /dev/spidev1.0 -s 5000000 -e 1 -S 2 -v -p "\x80\x00"
spi mode: 0x0
bits per word: 8
max speed: 5000000 Hz (5000 kHz)
RX | FF 0F __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __  |..|
root@buildroot:/app#
```

我们可以观察 IMU 的 ID： 0x0F 能被正常读取，证明 IMU 和 芯片 SPI 功通讯成功。

如果不想使用默认的 SPI 节点配置，可以根据 IMU 实际情况做驱动适配，这里提供一些参考。

设备树的配置如下：
```dts
&spi1 {
	status = "okay";
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_spi1 &pinctrl_spi1_ssn1>;
	dma-names = "tx", "rx";
	dmas = <&axi_dmac 23>, <&axi_dmac 22>;

	bmi08g@0 {
		compatible = "bmi088_gyro";  /*指定该 IMU 的兼容性标识*/
		reg = <0>;  /*指定设备的寄存器索引*/
		spi-max-frequency = <5000000>;
		interrupt-parent = <&ls_gpio1_porta>;  /*指定中断的父设备*/
		interrupts = <6 IRQ_TYPE_EDGE_RISING>;  /*定义中断号和触发类型*/
	};

	bmi08a@1 {
		compatible = "bmi08a";  /*指定该 IMU 的兼容性标识*/
		reg = <1>;  /*指定设备的寄存器索引*/
		spi-max-frequency = <5000000>;
		interrupt-parent = <&ls_gpio1_porta>;  /*指定中断的父设备*/
		interrupts = <4 IRQ_TYPE_EDGE_RISING>;  /*定义中断号和触发类型*/
	};
};

```
驱动文件根据情况可以放到 `kernel\drivers\iio\imu` 中，相应的修改 `kernel\drivers\iio` 中的 `Kconfig` 和 `Makefie`，以及对 deconfig 文件进行修改。

#### 测试用例源码： spidev_tc.c

SDK 中该用例源码的目录位置 \
{platform_source_code}/app/samples/platform_samples/chip_base_test/05_spi_test/spidev_tc.c

这里就不赘述了，编译完成的 spidev 在系统镜像中的如下位置 \
/app/platform_samples/chip_base_test/05_spi_test/spidev_tc

详细的使用说明可以参考 [SPI 压力测试章节 ](../chip_base_test/5-spi_stress.html#span-id-test-script-user-manual) 中对 spidev_tc 源码参数的解释

#### 测试用例源码编译方法：

将代码放到具备交叉编译的环境下，使用：`/opt/arm-gnu-toolchain-11.3.rel1-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gcc -o spidev_tc spidev_tc.c -lpthread` 编译，得到 spidev_tc 可执行文件。将该可执行文件拷贝到目标板上，参考上文内容使用。


## 常见的 SPI 通信错误及排查方法
在实际的 SPI 通信过程中，常常会遇到一些问题，下面将介绍一些常见的 SPI 通信错误以及相应的排查方法。

### 时钟极性或相位设置错误导致的问题
当 SPI 设备的时钟极性 (CPOL) 或相位 (CPHA) 设置错误时，会导致通信失败或数据传输错误。在调试时，需要确保主设备和从设备的时钟极性和相位设置保持一致，否则通信将无法正常进行。可通过检查软件配置或寄存器设置来确认时钟极性和相位的设置是否正确。

### 数据传输不稳定的常见原因
数据传输不稳定通常是由于信号干扰、电源噪声、线路长度等因素引起的。为了解决这些问题，可以采取一些措施，如增加数据线路的阻抗匹配、加入隔离器件、优化供电设计等，以提高数据传输的稳定性。

### 如何通过调试工具解决 SPI 通信问题
在调试 SPI 通信问题时，可以借助一些调试工具来辅助分析。例如使用逻辑分析仪可以观察时钟信号、数据信号的波形，以判断通信是否正常。此外，还可以通过串口调试助手等工具实时监测数据传输过程中的状态，有助于快速定位问题并进行调试处理。
