SPI总线通信协议基础与ICM20607传感器驱动开发指南

文章目录

  • 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):

  1. CPOL (Clock Polarity, 时钟极性)
    • CPOL = 0:在没有数据传输时(空闲状态),SCLK 时钟线保持低电平
    • CPOL = 1:在没有数据传输时(空闲状态),SCLK 时钟线保持高电平
  2. CPHA (Clock Phase, 时钟相位)
    • CPHA = 0:在时钟的第一个跳变沿(边沿)进行数据采样(读取数据),在第二个跳变沿进行数据移位(改变数据)。
    • CPHA = 1:在时钟的第二个跳变沿进行数据采样,在第一个跳变沿进行数据移位。

工程经验 :在绝大多数传感器(包括本文的 ICM20607)和 Flash 芯片中,最常用的是 SPI Mode 0(CPOL=0, CPHA=0)。即空闲时钟为低电平,在第一个上升沿锁存(采样)数据。


三、 i.MX6ULL SPI 控制器底层初始化流程

要让 CPU 的 SPI 控制器工作,通常需要完成以下三个层次的基础建设:

  1. 开启时钟供电
    配置 CPU 的 CCM(Clock Controller Module)寄存器,如 CSCDR1,为 SPI 模块提供工作频率并开启时钟源。
  2. 引脚复用配置 (IOMUX)
    将 CPU 对应的物理引脚复用为 SPI 功能。需要配置 IOMUXC_SW_MUX_CTL_PAD_xxx 寄存器,将四个引脚分别映射为 ECSPI_SCLKECSPI_MOSIECSPI_MISO 和普通 GPIO(用作片选 CS,软件控制更灵活)。
  3. 核心控制器配置 (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. 传感器的唤醒与验证

传感器上电后通常是不工作的,需要进行以下初始化:

  1. 解除休眠模式 :向 PWR_MGMT_1 (地址 0x6B) 写入 0x00,唤醒芯片并选择最稳定的内部时钟源。
  2. 关闭 FIFO 缓冲 :向 FIFO_EN (地址 0x23) 写入 0x00。对于简单的裸机读取,关闭 FIFO 可以让我们直接读取最新的实时数据。
  3. 验证身份 (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 位有符号原始数据

相关推荐
十五年专注C++开发1 小时前
HDF5: 大数据的 “超级容器“
大数据·数据库·c++·hdf5
白玉cfc1 小时前
OC底层原理:alloc&init&new
c++·macos·ios·objective-c·xcode
-凌凌漆-2 小时前
【QML】qml和C++中同时使用单例模式
java·c++·单例模式
6Hzlia2 小时前
【Hot 100 刷题计划】 LeetCode 101. 对称二叉树 | C++ DFS 极简递归模板
c++·leetcode·深度优先
誰能久伴不乏2 小时前
Qt 混合编程核心原理:C++ 与 QML 通信机制详解
linux·c++·qt·架构·状态模式
进击的小头2 小时前
第17篇:嵌入式通用串行外设:UART_SPI_I2C接口原理与外设扩展应用
单片机·嵌入式硬件
ximu_polaris2 小时前
设计模式(C++)-结构型模式-享元模式
c++·设计模式·享元模式
Hello!!!!!!2 小时前
C++基础(五)——屏幕和文件输入输出
开发语言·c++·算法
ytttr8732 小时前
C++ LZW 文件压缩算法实现
开发语言·c++