#Linux 平台下 I2C 传感器数据读取程序的代码梳理及 LSM6DS3 传感器的适配修改
学习自存
包括修改 I2C 从机地址、替换寄存器地址、修改传感器初始化函数和数据读取逻辑、修改数据拼接函数,以及编译和运行时的注意事项。
原始代码是野火原子读取陀螺仪传感器数据的实验例程,与例程不同的是,例程读取陀螺仪(MPU6050),我没有这个模块,使用的GY-LSM6DS3模块,查看挂载在i2c-3上的器件情况时,我的输出是
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: – – – – – – – – – – – – – – – –
10: – – – – – – – – – – – – – – – –
20: – – – – – – – – – – – – – – – –
30: – – – – – – – – – – – – – – – –
40: – – – – – – – – – – – – – – – –
50: – – – – – – – – – – – – – – – –
60: – – – – – – – – – – – – 6b – – –
70: – – – – – – – – – – – – – – – –

一、代码梳理讲解

这是一个 Linux 平台下 I2C 传感器数据读取程序,原本用于读取 MPU6050(加速度 + 陀螺仪二合一传感器),核心逻辑是通过 I2C 总线与传感器通信,配置传感器寄存器并读取加速度 / 陀螺仪的原始数据。下面分模块讲解:

1. 核心依赖与宏定义
  • 依赖头文件:linux/i2c.h/linux/i2c-dev.h 提供 I2C 总线操作接口(ioctl/read/write),stdint.h 提供标准整数类型。

  • 寄存器宏定义:MPU6050 的配置寄存器(采样率、电源管理、量程)和数据寄存器(加速度 X/Y/Z、陀螺仪 X/Y/Z 的高低字节)。

  • 从机地址:Address 0x68 是 MPU6050 的默认 I2C 地址(未接 AD0 引脚时)。

2. 核心函数功能
函数名 功能
main 打开 I2C 设备文件 → 初始化传感器 → 循环读取并打印 6 轴数据(1 秒 / 次)
mpu6050_init 配置 MPU6050 寄存器:唤醒传感器、设置采样率、低通滤波、加速度量程
i2c_write I2C 写操作:向传感器指定寄存器写入配置值
i2c_read I2C 读操作:从传感器指定寄存器读取 1 字节数据
GetData 读取数据寄存器的高低字节 → 拼接为 16 位有符号整数(原始传感器数据)
3. I2C 通信流程(关键)
  • 写操作(i2c_write):先发送「寄存器地址」,再发送「要写入的值」(2 字节数据)。

  • 读操作(i2c_read):先发送「要读取的寄存器地址」(告诉传感器 “接下来读这个寄存器”),再读取 1 字节数据。

  • 数据拼接(GetData):传感器数据是 16 位(高字节在前、低字节在后),需拼接为 short 类型(支持正负值,对应加速度 / 陀螺仪的正负方向)。

二、需要修改的地方(适配 GY-LSM6DS3)

GY-LSM6DS3 是 LSM6DS3 芯片的模块(加速度 + 陀螺仪 + 温度),与 MPU6050 的核心差异是:I2C 地址不同、寄存器地址不同、配置参数不同。结合你的 i2cdetect -a 3 输出(I2C 地址 0x6B),需修改以下 4 个核心部分:

1. 第一步:修改 I2C 从机地址

LSM6DS3 实际 I2C 地址是 0x6Bi2cdetect 输出的 6b),替换原 MPU6050 的地址:

// 原代码(MPU6050 地址)

\#define Address 0x68

// 修改后(LSM6DS3 地址)

\#define Address 0x6B  // 必须与 i2cdetect 检测到的一致
2. 第二步:替换 LSM6DS3 的寄存器地址

LSM6DS3 的配置寄存器和数据寄存器与 MPU6050 完全不同,需替换所有寄存器宏定义(关键!):

// \*\*\*\*\*\*\*\*\*\* 替换为 LSM6DS3 的寄存器地址 \*\*\*\*\*\*\*\*\*\*

// 配置寄存器(核心必须改)

#define CTRL1\_XL     0x10  // 加速度配置寄存器(量程、采样率)

#define CTRL2\_G      0x11  // 陀螺仪配置寄存器(量程、采样率)

#define CTRL3\_C      0x12  // 通用控制寄存器(使能传感器、I2C 模式)

// 数据寄存器(加速度 X/Y/Z 高低字节)

#define OUTX\_L\_A     0x28  // 加速度 X 低字节

#define OUTX\_H\_A     0x29  // 加速度 X 高字节

#define OUTY\_L\_A     0x2A  // 加速度 Y 低字节

#define OUTY\_H\_A     0x2B  // 加速度 Y 高字节

#define OUTZ\_L\_A     0x2C  // 加速度 Z 低字节

#define OUTZ\_H\_A     0x2D  // 加速度 Z 高字节

// 数据寄存器(陀螺仪 X/Y/Z 高低字节)

#define OUTX\_L\_G     0x22  // 陀螺仪 X 低字节

#define OUTX\_H\_G     0x23  // 陀螺仪 X 高字节

#define OUTY\_L\_G     0x24  // 陀螺仪 Y 低字节

#define OUTY\_H\_G     0x25  // 陀螺仪 Y 高字节

#define OUTZ\_L\_G     0x26  // 陀螺仪 Z 低字节

#define OUTZ\_H\_G     0x27  // 陀螺仪 Z 高字节

// \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
3. 第三步:修改传感器初始化函数(mpu6050_init → 改为 lsm6ds3_init

LSM6DS3 的配置逻辑与 MPU6050 不同,需重新配置 CTRL1_XL/CTRL2_G/CTRL3_C 寄存器(决定传感器是否唤醒、采样率、量程):

// 原函数名 mpu6050\_init 可改为 lsm6ds3\_init(语义更清晰)

static int lsm6ds3\_init(int fd, uint8\_t addr)

{
   // 1. CTRL3\_C: 0x00 → 使能传感器(默认值已满足,也可显式配置)

   //    - 0x00: 禁用循环模式、I2C 正常模式、传感器唤醒

  i2c\_write(fd, addr, CTRL3\_C, 0x00);  

   // 2. CTRL1\_XL: 加速度配置(0x60 → 采样率 416Hz,量程 ±2g,低通滤波 100Hz)

   //    二进制 01100000 → 高 4 位 0110=416Hz,低 4 位 0000=±2g

   i2c\_write(fd, addr, CTRL1\_XL, 0x60);  

   // 3. CTRL2\_G: 陀螺仪配置(0x60 → 采样率 416Hz,量程 ±250 dps)

   //    二进制 01100000 → 高 4 位 0110=416Hz,低 4 位 0000=±250 dps

   i2c\_write(fd, addr, CTRL2\_G, 0x60);  
   return 0;

}
  • 配置说明:

    • 采样率 416Hz:兼顾响应速度和数据稳定性(可选其他值,如 0x40=208Hz)。

    • 加速度量程 ±2g:适合常规运动检测(可改为 0x80=±4g、0xA0=±8g、0xC0=±16g)。

    • 陀螺仪量程 ±250 dps:适合低速旋转(可改为 0x80=±500 dps、0xA0=±1000 dps、0xC0=±2000 dps)。

4. 第四步:修改 main 函数中的数据读取逻辑

LSM6DS3 的数据寄存器顺序是「低字节在前、高字节在后」(与 MPU6050 相反!),且寄存器地址不同,需修改:

int main(int argc, char \*argv\[])

{

   int fd;

   if (argc < 2) {

       printf("Wrong use !!!!\n");

       printf("Usage: %s \[dev]\n", argv\[0]);

       return -1;

   }

   fd = open(argv\[1], O\_RDWR);

   if (fd < 0) {

       printf("Can't open %s \n", argv\[1]);

       exit(1);

   }

  // 调用修改后的 LSM6DS3 初始化函数

   lsm6ds3\_init(fd, Address);

   while (1) {

       // 读取加速度数据(注意:LSM6DS3 是 低字节在前,高字节在后!)

      printf("ACCE\_X:%6d\n ", GetData\_LSM6DS3(fd, Address, OUTX\_L\_A));  // 先读低字节

       printf("ACCE\_Y:%6d\n ", GetData\_LSM6DS3(fd, Address, OUTY\_L\_A));

       printf("ACCE\_Z:%6d\n ", GetData\_LSM6DS3(fd, Address, OUTZ\_L\_A));

       // 读取陀螺仪数据
       printf("GYRO\_X:%6d\n ", GetData\_LSM6DS3(fd, Address, OUTX\_L\_G));

       printf("GYRO\_Y:%6d\n ", GetData\_LSM6DS3(fd, Address, OUTY\_L\_G));

       printf("GYRO\_Z:%6d\n\n ", GetData\_LSM6DS3(fd, Address, OUTZ\_L\_G));

       sleep(1);

       usleep(1000 \* 10);  // 微调延时,避免数据拥堵

   }

   close(fd);

   return 0;

}
5. 第五步:修改数据拼接函数(GetDataGetData_LSM6DS3

LSM6DS3 数据是「低字节(L)在前,高字节(H)在后」,与 MPU6050 相反,需调整拼接顺序:

// 适配 LSM6DS3:先读低字节,再读高字节,拼接为 16 位数据

static short GetData\_LSM6DS3(int fd, uint8\_t addr, unsigned char REG\_L)

{

&#x20;   uint8\_t L, H;  // 用 uint8\_t 更规范(避免符号问题)

&#x20;   // 读低字节(REG\_L 是低字节地址)

&#x20;   if (i2c\_read(fd, addr, REG\_L, \&L) != 0) {

&#x20;       printf("Read L byte failed!\n");

&#x20;       return 0;

&#x20;   }

&#x20;   usleep(1000);  // 微小延时,确保传感器准备好高字节数据

&#x20;   // 读高字节(高字节地址 = 低字节地址 + 1)

&#x20;   if (i2c\_read(fd, addr, REG\_L + 1, \&H) != 0) {

&#x20;       printf("Read H byte failed!\n");

&#x20;       return 0;

&#x20;   }

&#x20;   // 拼接:高字节左移 8 位 + 低字节(注意是有符号扩展)

&#x20;   return (short)((H << 8) | L);

}

三、其他注意事项

  1. I2C 设备文件路径:运行程序时,需指定你的 I2C 总线设备文件 /dev/i2c-3(因为 i2cdetect -a 3 检测到传感器),命令如下:
sudo ./your\_program /dev/i2c-3  # 必须加 sudo(I2C 操作需要 root 权限)
  1. 编译命令:编译时无需额外链接库(依赖的是系统内核提供的 I2C 接口),直接用 gcc:
gcc lsm6ds3\_read.c -o lsm6ds3\_read
  1. 调试技巧
  • 若读取失败(返回 0 或固定值),先检查 i2c_write/i2c_read 的返回值(是否为 0,0 表示成功),添加错误打印:
// 例如在 lsm6ds3\_init 中添加错误检查

if (i2c\_write(fd, addr, CTRL3\_C, 0x00) != 0) {

&#x20;   printf("CTRL3\_C write failed!\n");

&#x20;   return -1;

}
  • 确认 LSM6DS3 的 VCC 电压(3.3V,不要接 5V,会烧毁模块)。

  • 若 I2C 总线占用,可先执行 i2cdetect -a 3 确认传感器是否仍在 0x6B 地址。

四、最终修改后的核心代码片段(汇总)

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>

// ********** LSM6DS3 寄存器地址 **********
#define CTRL1_XL     0x10  // 加速度配置寄存器
#define CTRL2_G      0x11  // 陀螺仪配置寄存器
#define CTRL3_C      0x12  // 通用控制寄存器

// 加速度数据寄存器(低字节在前)
#define OUTX_L_A     0x28
#define OUTX_H_A     0x29
#define OUTY_L_A     0x2A
#define OUTY_H_A     0x2B
#define OUTZ_L_A     0x2C
#define OUTZ_H_A     0x2D

// 陀螺仪数据寄存器(低字节在前)
#define OUTX_L_G     0x22
#define OUTX_H_G     0x23
#define OUTY_L_G     0x24
#define OUTY_H_G     0x25
#define OUTZ_L_G     0x26
#define OUTZ_H_G     0x27

// LSM6DS3 I2C 地址(i2cdetect 检测到的 0x6B)
#define Address      0x6B

// 函数声明
static int lsm6ds3_init(int fd, uint8_t addr);
static int i2c_write(int fd, uint8_t addr, uint8_t reg, uint8_t val);
static int i2c_read(int fd, uint8_t addr, uint8_t reg, uint8_t *val);
static short GetData_LSM6DS3(int fd, uint8_t addr, unsigned char REG_L);

int main(int argc, char *argv[])
{
    int fd;
    if (argc < 2) {
        printf("Wrong use !!!!\n");
        printf("Usage: %s [dev]\n", argv[0]);
        return -1;
    }

    fd = open(argv[1], O_RDWR);
    if (fd < 0) {
        printf("Can't open %s \n", argv[1]);
        exit(1);
    }

    if (lsm6ds3_init(fd, Address) != 0) {
        printf("LSM6DS3 init failed!\n");
        close(fd);
        return -1;
    }

    while (1) {
        printf("ACCE_X:%6d\n ", GetData_LSM6DS3(fd, Address, OUTX_L_A));
        printf("ACCE_Y:%6d\n ", GetData_LSM6DS3(fd, Address, OUTY_L_A));
        printf("ACCE_Z:%6d\n ", GetData_LSM6DS3(fd, Address, OUTZ_L_A));
        printf("GYRO_X:%6d\n ", GetData_LSM6DS3(fd, Address, OUTX_L_G));
        printf("GYRO_Y:%6d\n ", GetData_LSM6DS3(fd, Address, OUTY_L_G));
        printf("GYRO_Z:%6d\n\n ", GetData_LSM6DS3(fd, Address, OUTZ_L_G));
        sleep(1);
    }

    close(fd);
    return 0;
}

// LSM6DS3 初始化
static int lsm6ds3_init(int fd, uint8_t addr)
{
    // 配置 CTRL3_C:使能传感器(默认唤醒)
    if (i2c_write(fd, addr, CTRL3_C, 0x00) != 0) return -1;
    // 配置加速度:416Hz 采样率,±2g 量程
    if (i2c_write(fd, addr, CTRL1_XL, 0x60) != 0) return -1;
    // 配置陀螺仪:416Hz 采样率,±250 dps 量程
    if (i2c_write(fd, addr, CTRL2_G, 0x60) != 0) return -1;
    return 0;
}

// I2C 写操作(无需修改,兼容 LSM6DS3)
static int i2c_write(int fd, uint8_t addr, uint8_t reg, uint8_t val)
{
    uint8_t data[2] = {reg, val};
    ioctl(fd, I2C_TENBIT, 0);  // 7 位地址
    if (ioctl(fd, I2C_SLAVE, addr) < 0) {
        printf("Set slave address failed!\n");
        close(fd);
        return -1;
    }
    ioctl(fd, I2C_RETRIES, 5);
    return (write(fd, data, 2) == 2) ? 0 : -1;
}

// I2C 读操作(无需修改,兼容 LSM6DS3)
static int i2c_read(int fd, uint8_t addr, uint8_t reg, uint8_t *val)
{
    ioctl(fd, I2C_TENBIT, 0);
    if (ioctl(fd, I2C_SLAVE, addr) < 0) {
        printf("Set slave address failed!\n");
        close(fd);
        return -1;
    }
    ioctl(fd, I2C_RETRIES, 5);
    if (write(fd, &reg, 1) != 1) return -1;
    return (read(fd, val, 1) == 1) ? 0 : -1;
}

// LSM6DS3 数据读取(低字节在前,高字节在后)
static short GetData_LSM6DS3(int fd, uint8_t addr, unsigned char REG_L)
{
    uint8_t L, H;
    if (i2c_read(fd, addr, REG_L, &L) != 0) return 0;
    usleep(1000);
    if (i2c_read(fd, addr, REG_L + 1, &H) != 0) return 0;
    return (short)((H << 8) | L);
}

总结

核心修改点是 3 个关键差异

  1. I2C 从机地址(0x68 → 0x6B);

  2. 寄存器地址(替换为 LSM6DS3 的配置 / 数据寄存器);

  3. 数据读取顺序(低字节在前,高字节在后)。

按上述修改后,编译运行即可读取 GY-LSM6DS3 的 6 轴原始数据。若需要将原始数据转换为实际物理量(如加速度单位 g、陀螺仪单位 dps),可根据量程计算(例如 ±2g 量程时,原始值 ÷ 16384 = 实际 g 值)。

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐