文章目录
- SPI总线通信协议基础与ICM20607传感器驱动开发指南
-
- [一、 SPI 总线物理架构与通信原理](#一、 SPI 总线物理架构与通信原理)
-
- [1. 四根核心信号线](#1. 四根核心信号线)
- [2. 全双工通信的本质](#2. 全双工通信的本质)
- [二、 核心参数:时钟极性与相位 (CPOL & CPHA)](#二、 核心参数:时钟极性与相位 (CPOL & CPHA))
- [三、 i.MX6ULL SPI 控制器底层初始化流程](#三、 i.MX6ULL SPI 控制器底层初始化流程)
- [四、 ICM20607 传感器实战通信逻辑](#四、 ICM20607 传感器实战通信逻辑)
-
- [1. 读写操作的"最高位潜规则" (Bit 7)](#1. 读写操作的“最高位潜规则” (Bit 7))
- [2. 传感器的唤醒与验证](#2. 传感器的唤醒与验证)
- [五、 获取六轴原始数据与代码实现](#五、 获取六轴原始数据与代码实现)
-
- [1. 寄存器地址映射表](#1. 寄存器地址映射表)
- [2. 数据位移拼接与符号处理 (C语言范例)](#2. 数据位移拼接与符号处理 (C语言范例))
- 总结
SPI总线通信协议基础与ICM20607传感器驱动开发指南
前言
SPI(Serial Peripheral Interface,串行外设接口)是嵌入式系统中应用最为广泛的同步串行通信总线之一。相比于 I2C 和 UART,SPI 具有全双工、高带宽的优势,常用于对数据刷新率要求较高的传感器(如六轴陀螺仪)、Flash 存储器和 TFT 显示屏。
本文将从 SPI 的底层物理时序出发,结合 i.MX6ULL 处理器与 ICM20607 六轴传感器的实际驱动开发,详细梳理 SPI 通信的配置流程与数据解析方法。当你需要在新的硬件平台上编写 SPI 驱动时,本文可作为标准参考手册。
一、 SPI 总线物理架构与通信原理
SPI 是一种基于**主从模式(Master-Slave)**的同步、全双工通信协议。通常由一个主控芯片(CPU/MCU)和一个或多个从设备组成。
1. 四根核心信号线
标准的 SPI 接口包含四根信号线,每一根都有明确的功能定义:
- SCLK (Serial Clock - 串行时钟):由主设备产生并控制。它决定了数据传输的速率,所有的信号采样和移位都必须严格在这个时钟的跳变沿进行。
- MOSI (Master Out Slave In - 主出从入):数据发送线。主设备通过这根线将数据一位一位地发送给从设备。
- MISO (Master In Slave Out - 主入从出):数据接收线。从设备通过这根线将数据一位一位地发送给主设备。
- CS / SS (Chip Select / Slave Select - 片选线) :由主设备控制,通常是低电平有效。当主设备想要与某个具体的从设备通信时,就会把连接该设备的 CS 线拉低。这使得 SPI 总线上可以挂载多个从设备。
2. 全双工通信的本质
SPI 的全双工意味着主设备在发送一个 Bit 的同时,必然会接收到一个 Bit 。
它的底层硬件其实是主设备和从设备内部各有一个移位寄存器(Shift Register)。当时钟脉冲到来时,双方的数据同时被推出去,进入对方的寄存器,形成一个环形链路。因此,在 SPI 通信中,"读"和"写"在物理上是同时发生的操作。
二、 核心参数:时钟极性与相位 (CPOL & CPHA)
不同的 SPI 从设备对时钟和数据的配合方式有不同的要求,这由两个参数决定,组合出四种工作模式(Mode 0 ~ Mode 3):
- CPOL (Clock Polarity, 时钟极性)
CPOL = 0:在没有数据传输时(空闲状态),SCLK 时钟线保持低电平。CPOL = 1:在没有数据传输时(空闲状态),SCLK 时钟线保持高电平。
- CPHA (Clock Phase, 时钟相位)
CPHA = 0:在时钟的第一个跳变沿(边沿)进行数据采样(读取数据),在第二个跳变沿进行数据移位(改变数据)。CPHA = 1:在时钟的第二个跳变沿进行数据采样,在第一个跳变沿进行数据移位。
工程经验 :在绝大多数传感器(包括本文的 ICM20607)和 Flash 芯片中,最常用的是 SPI Mode 0(CPOL=0, CPHA=0)。即空闲时钟为低电平,在第一个上升沿锁存(采样)数据。
三、 i.MX6ULL SPI 控制器底层初始化流程
要让 CPU 的 SPI 控制器工作,通常需要完成以下三个层次的基础建设:
- 开启时钟供电 :
配置 CPU 的 CCM(Clock Controller Module)寄存器,如CSCDR1,为 SPI 模块提供工作频率并开启时钟源。 - 引脚复用配置 (IOMUX) :
将 CPU 对应的物理引脚复用为 SPI 功能。需要配置IOMUXC_SW_MUX_CTL_PAD_xxx寄存器,将四个引脚分别映射为ECSPI_SCLK、ECSPI_MOSI、ECSPI_MISO和普通 GPIO(用作片选 CS,软件控制更灵活)。 - 核心控制器配置 (CONREG 寄存器) :
- 设置为 Master(主)模式。
- 配置突发长度(Burst Length)为 8 位(即每次传输 1 个字节)。
- 设置时钟分频,确保通信频率不超过从设备的最高支持频率。
- 配置 CPOL 和 CPHA(针对 ICM20607,均设置为 0)。
四、 ICM20607 传感器实战通信逻辑
ICM20607 是一款内置 3 轴加速度计和 3 轴陀螺仪的六轴传感器。通过 SPI 与它通信时,必须严格遵守它的协议规范。
1. 读写操作的"最高位潜规则" (Bit 7)
SPI 是没有寻址机制的,你要读写传感器的哪个寄存器,必须在发送的第一个字节里告诉它。ICM20607 规定,寄存器地址的最高位(Bit 7)用来区分是读还是写:
- 写寄存器(Bit 7 = 0) :
假设寄存器地址是addr,你要写入数据val。
操作流程:拉低 CS -> 发送addr & 0x7F(确保最高位是0) -> 发送val-> 拉高 CS。 - 读寄存器(Bit 7 = 1) :
假设你要读取地址addr的数据。
操作流程:拉低 CS -> 发送addr | 0x80(强制最高位置1) -> 发送 Dummy Byte (通常为 0x00或0xFF) -> 拉高 CS。
为什么要发送 Dummy Byte? 因为 SPI 是全双工的,主控必须产生时钟信号才能把从设备的数据"挤"出来。主控发送无意义的0x00给传感器,传感器就会在同一个时钟周期内把真实的寄存器数据发给主控。
2. 传感器的唤醒与验证
传感器上电后通常是不工作的,需要进行以下初始化:
- 解除休眠模式 :向
PWR_MGMT_1(地址 0x6B) 写入0x00,唤醒芯片并选择最稳定的内部时钟源。 - 关闭 FIFO 缓冲 :向
FIFO_EN(地址 0x23) 写入0x00。对于简单的裸机读取,关闭 FIFO 可以让我们直接读取最新的实时数据。 - 验证身份 (WHO_AM_I) :读取地址
0x75,正常情况下 ICM20607 会固定返回0xAF。这是测试底层 SPI 时序是否正确的最有效手段。
五、 获取六轴原始数据与代码实现
1. 寄存器地址映射表
ICM20607 输出的数据精度是 16 位的,但 SPI 每次只能读取 8 位数据。因此,每个传感器的数据都分为高 8 位寄存器 (High) 和低 8 位寄存器 (Low):
| 传感器通道 | 高字节地址 (H) | 低字节地址 (L) |
|---|---|---|
| 加速度 X 轴 | 59 (0x3B) | 60 (0x3C) |
| 加速度 Y 轴 | 61 (0x3D) | 62 (0x3E) |
| 加速度 Z 轴 | 63 (0x3F) | 64 (0x40) |
| 芯片温度 | 65 (0x41) | 66 (0x42) |
| 陀螺仪 X 轴 | 67 (0x43) | 68 (0x44) |
| 陀螺仪 Y 轴 | 69 (0x45) | 70 (0x46) |
| 陀螺仪 Z 轴 | 71 (0x47) | 72 (0x48) |
2. 数据位移拼接与符号处理 (C语言范例)
读取到高低两个字节后,我们需要用位运算将其拼接为一个完整的 16 位数据。
注意: 传感器的原始数据采用补码形式 存储,包含了正负方向。在 C 语言中,我们必须将其强转为 short(16位有符号整型)。这样如果最高位是 1,系统会自动将其识别为负数。
c
/* 封装好的底层 SPI 读取单字节函数假设为 spi_read_byte() */
// 从 ICM20607 读取一个完整的 16 位传感器数据
short icm20607_read_16bit_data(unsigned char reg_high_addr, unsigned char reg_low_addr) {
unsigned char h_byte, l_byte;
short final_data;
h_byte = spi_read_byte(reg_high_addr); // 读取高 8 位
l_byte = spi_read_byte(reg_low_addr); // 读取低 8 位
// 将高位左移 8 次,然后与低位进行按位或操作
final_data = (short)((h_byte << 8) | l_byte);
return final_data;
}
// 在主程序中获取陀螺仪 X 轴数据的调用示例:
void get_gyro_data(void) {
short gyro_x;
// 0x43 是陀螺仪 X 轴高位,0x44 是低位
gyro_x = icm20607_read_16bit_data(0x43, 0x44);
printf("Gyro X: %d\n", gyro_x);
}
总结
掌握了以上流程,就掌握了 SPI 传感器驱动的核心。总结起来就是四个关键点:正确配置 CPOL/CPHA 时序 、理解 Bit 7 的读写规则 、懂得发送 Dummy Byte 换取数据 ,以及熟练使用位运算拼接 16 位有符号原始数据。