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_SCLK、ECSPI_MOSI、ECSPI_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 位有符号原始数据。

相关推荐
Sakura_j_h_6 小时前
EasyEDA PCB 快速设计Skills
c++·python·github
碧海银沙音频科技研究院6 小时前
基于炬芯ATS308X 设计的上位机usb 串口音乐EQ调节工具
stm32·单片机·嵌入式硬件
程序喵大人7 小时前
【C++入门】内存模型与资源管理 - 09 shared_ptr 和 weak_ptr 解决共享,也带来代价
开发语言·c++·资源管理·智能指针·内存模型
jimy19 小时前
C++ 中的 `T&&`:右值引用与转发引用
开发语言·c++
三佛科技-134163842129 小时前
过认证72W碳化硅方案(12V6A)主控HN1562+HN36228V 典型应用电路、变压器参数分享
单片机·嵌入式硬件·物联网·智能家居·pcb工艺
Brilliantwxx11 小时前
【STM32】 内部 Flash 详解:寄存器逐位拆解 + 工作流程顺序全讲 + EEPROM再实现
stm32·单片机·嵌入式硬件·1024程序员节
会编程的小孩11 小时前
C++ 定时器
c++
木子单片机11 小时前
基于单片机的DS18B20温度传感器测温系统设计
单片机·嵌入式硬件·51单片机·keil
华清远见IT开放实验室11 小时前
实验室建设案例 | 安徽三联学院物联网实验室
stm32·单片机·嵌入式硬件·物联网·华清远见
沫璃染墨12 小时前
从零入门计算机网络系列(十一):HTTP协议进阶——从重定向原理到GET/POST与Fiddler抓包
linux·服务器·开发语言·网络·c++·tcp/ip·html