Linux 平台下 I2C 传感器数据读取程序的代码梳理及 LSM6DS3 传感器的适配修改
提供 I2C 总线操作接口(ioctlreadwrite),stdint.h提供标准整数类型。寄存器宏定义:MPU6050 的配置寄存器(采样率、电源管理、量程)和数据寄存器(加速度 X/Y/Z、陀螺仪 X/Y/Z 的高低字节)。是 MPU6050 的默认 I2C 地址(未接 AD0 引脚时)。核心修改点是3 个关键差异I2C 从机地址(0x68 → 0x6B);寄存器地址(替换为 LSM6DS3
#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 地址是 0x6B(i2cdetect 输出的 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. 第五步:修改数据拼接函数(GetData → GetData_LSM6DS3)
LSM6DS3 数据是「低字节(L)在前,高字节(H)在后」,与 MPU6050 相反,需调整拼接顺序:
// 适配 LSM6DS3:先读低字节,再读高字节,拼接为 16 位数据
static short GetData\_LSM6DS3(int fd, uint8\_t addr, unsigned char REG\_L)
{
  uint8\_t L, H; // 用 uint8\_t 更规范(避免符号问题)
  // 读低字节(REG\_L 是低字节地址)
  if (i2c\_read(fd, addr, REG\_L, \&L) != 0) {
  printf("Read L byte failed!\n");
  return 0;
  }
  usleep(1000); // 微小延时,确保传感器准备好高字节数据
  // 读高字节(高字节地址 = 低字节地址 + 1)
  if (i2c\_read(fd, addr, REG\_L + 1, \&H) != 0) {
  printf("Read H byte failed!\n");
  return 0;
  }
  // 拼接:高字节左移 8 位 + 低字节(注意是有符号扩展)
  return (short)((H << 8) | L);
}
三、其他注意事项
- I2C 设备文件路径:运行程序时,需指定你的 I2C 总线设备文件
/dev/i2c-3(因为i2cdetect -a 3检测到传感器),命令如下:
sudo ./your\_program /dev/i2c-3 # 必须加 sudo(I2C 操作需要 root 权限)
- 编译命令:编译时无需额外链接库(依赖的是系统内核提供的 I2C 接口),直接用 gcc:
gcc lsm6ds3\_read.c -o lsm6ds3\_read
- 调试技巧:
- 若读取失败(返回 0 或固定值),先检查
i2c_write/i2c_read的返回值(是否为 0,0 表示成功),添加错误打印:
// 例如在 lsm6ds3\_init 中添加错误检查
if (i2c\_write(fd, addr, CTRL3\_C, 0x00) != 0) {
  printf("CTRL3\_C write failed!\n");
  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, ®, 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 个关键差异:
-
I2C 从机地址(0x68 → 0x6B);
-
寄存器地址(替换为 LSM6DS3 的配置 / 数据寄存器);
-
数据读取顺序(低字节在前,高字节在后)。
按上述修改后,编译运行即可读取 GY-LSM6DS3 的 6 轴原始数据。若需要将原始数据转换为实际物理量(如加速度单位 g、陀螺仪单位 dps),可根据量程计算(例如 ±2g 量程时,原始值 ÷ 16384 = 实际 g 值)。
更多推荐



所有评论(0)