返回首页

I2C 传感器学习笔记

概述

I2C (Inter-Integrated Circuit) 是常用的低速串行通信协议,广泛用于连接传感器、EEPROM、RTC 等外设。本示例展示如何使用 Zephyr Sensor API 读取 I2C 传感器数据。


1. I2C 基础概念

1.1 I2C 协议特点

特性说明
引脚SDA (数据)、SCL (时钟)
速率标准 100kHz、快速 400kHz、高速 3.4MHz
地址7位或10位地址
主从一主多从,主设备发起通信
电气开漏输出,需要上拉电阻

1.2 I2C 时序

    SDA:  ───┐     ┌───────┐     ┌───
             └─────┘       └─────┘
    SCL:  ───┐     ┌───────┐     ┌───
             └─────┘       └─────┘
    
    起始条件:SDA 从高到低,SCL 为高
    数据位:SDA 在 SCL 低时改变
    停止条件:SDA 从低到高,SCL 为高

2. 设备树配置

2.1 I2C 总线配置

&i2c0 {
    status = "okay";
    clock-frequency = <I2C_BITRATE_STANDARD>;  // 100kHz
    
    bme280@76 {
        compatible = "bosch,bme280";
        reg = <0x76>;  // I2C 设备地址
        status = "okay";
    };
};

2.2 传感器别名配置

/ {
    aliases {
        accel0 = &lis2dh12;
    };
    
    lis2dh12: lis2dh12@18 {
        compatible = "st,lis2dh";
        reg = <0x18>;
        status = "okay";
    };
};

3. Sensor API 核心操作

3.1 获取传感器设备

// 从设备树获取
const struct device *dev = DEVICE_DT_GET_ANY(bosch_bme280);

// 从别名获取
const struct device *const sensors[] = {
    DEVICE_DT_GET(DT_ALIAS(accel0)),
};

// 检查设备就绪
if (!device_is_ready(dev)) {
    printk("Device not ready\n");
}

3.2 同步读取(传统方式)

struct sensor_value temp, press, humidity;

// 1. 采集数据
int ret = sensor_sample_fetch(dev);
if (ret < 0) {
    printk("Fetch failed: %d\n", ret);
}

// 2. 获取具体通道数据
ret = sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP, &temp);
ret = sensor_channel_get(dev, SENSOR_CHAN_PRESS, &press);
ret = sensor_channel_get(dev, SENSOR_CHAN_HUMIDITY, &humidity);

3.3 传感器通道类型

// 加速度
SENSOR_CHAN_ACCEL_X
SENSOR_CHAN_ACCEL_Y
SENSOR_CHAN_ACCEL_Z
SENSOR_CHAN_ACCEL_XYZ

// 陀螺仪
SENSOR_CHAN_GYRO_X
SENSOR_CHAN_GYRO_Y
SENSOR_CHAN_GYRO_Z

// 环境传感器
SENSOR_CHAN_AMBIENT_TEMP
SENSOR_CHAN_HUMIDITY
SENSOR_CHAN_PRESS
SENSOR_CHAN_GAS_RES

// 磁力计
SENSOR_CHAN_MAGN_X
SENSOR_CHAN_MAGN_Y
SENSOR_CHAN_MAGN_Z

// 光照/颜色
SENSOR_CHAN_LIGHT
SENSOR_CHAN_RED
SENSOR_CHAN_GREEN
SENSOR_CHAN_BLUE

3.4 传感器值转换

// sensor_value 结构
struct sensor_value {
    int32_t val1;  // 整数部分
    int32_t val2;  // 小数部分 (百万分之一)
};

// 转换为 double
double val = sensor_value_to_double(&temp);
// 例如:val1 = 25, val2 = 500000 → 25.5

// 转换为整数(毫单位)
int32_t millivolts = sensor_value_to_milli(&voltage);

4. 传感器属性设置

4.1 设置采样频率

struct sensor_value odr;
odr.val1 = 100;  // 100 Hz
odr.val2 = 0;

int ret = sensor_attr_set(
    dev,                            // 设备
    SENSOR_CHAN_ACCEL_XYZ,          // 通道
    SENSOR_ATTR_SAMPLING_FREQUENCY, // 属性
    &odr                            // 值
);

4.2 获取采样频率

struct sensor_value odr;
sensor_attr_get(dev, SENSOR_CHAN_ACCEL_XYZ, SENSOR_ATTR_SAMPLING_FREQUENCY, &odr);
printk("ODR: %d Hz\n", odr.val1);

4.3 常用属性

// 采样频率
SENSOR_ATTR_SAMPLING_FREQUENCY

// 满量程范围
SENSOR_ATTR_FULL_SCALE

// 数据就绪触发
SENSOR_ATTR_CONFIGURATION

// 偏移量校准
SENSOR_ATTR_OFFSET

// 校准斜率
SENSOR_ATTR_SLOPE

// 阈值
SENSOR_ATTR_ALERT_THRESHOLD

5. 异步 Sensor API(新方式)

5.1 定义 IO 设备

SENSOR_DT_READ_IODEV(
    iodev,                              // IO 设备名
    DT_ALIAS(temp0),                    // 设备节点示例
    {SENSOR_CHAN_AMBIENT_TEMP, 0},      // 通道 0
    {SENSOR_CHAN_HUMIDITY, 0},          // 通道 1
    {SENSOR_CHAN_PRESS, 0}              // 通道 2
);

RTIO_DEFINE(ctx, 1, 1);  // RTIO 上下文

5.2 异步读取

uint8_t buf[128];
int rc = sensor_read(&iodev, &ctx, buf, 128);

// 获取解码器
const struct sensor_decoder_api *decoder;
sensor_get_decoder(dev, &decoder);

// 解码数据
struct sensor_q31_data temp_data;
decoder->decode(buf,
    (struct sensor_chan_spec) {SENSOR_CHAN_AMBIENT_TEMP, 0},
    &temp_fit, 1, &temp_data);

printk("Temp: %d.%d\n",
    temp_data.readings[0].temperature >> temp_data.shift,
    temp_data.readings[0].temperature & ((1 << temp_data.shift) - 1));

6. I2C 底层 API(直接使用)

6.1 写操作

// I2C 写一个字节
uint8_t reg = 0xF4;  // 寄存器地址
uint8_t data = 0x27; // 数据

int ret = i2c_write(
    dev,        // I2C 设备
    &data,      // 数据
    1,          // 长度
    0x76        // 从机地址
);

6.2 读操作

// I2C 读操作(先写寄存器地址,再读数据)
uint8_t reg = 0xF7;  // 要读的寄存器
uint8_t buf[6];      // 读缓冲区

int ret = i2c_write_read(
    dev,
    0x76,           // 从机地址
    &reg, 1,        // 写:寄存器地址
    buf, 6          // 读:6 字节数据
);

6.3 扫描总线

// 扫描 I2C 总线,查找设备
for (uint8_t addr = 0x08; addr < 0x77; addr++) {
    uint8_t dummy;
    if (i2c_read(i2c_dev, &dummy, 0, addr) == 0) {
        printk("Found device at 0x%02X\n", addr);
    }
}

7. nRF54L15 I2C 配置

7.1 设备树配置

&i2c20 {
    status = "okay";
    pinctrl-0 = <&i2c20_default>;
    pinctrl-1 = <&i2c20_sleep>;
    pinctrl-names = "default", "sleep";
    
    my_sensor@48 {
        compatible = "my,vendor,sensor";
        reg = <0x48>;
        status = "okay";
    };
};

7.2 引脚配置

&pinctrl {
    i2c20_default: i2c20_default {
        group1 {
            psels = <NRF_PSEL(TWIM_SDA, 1, 10)>,  // P1.10
                    <NRF_PSEL(TWIM_SCL, 1, 11)>;  // P1.11
        };
    };
};

8. 实际应用示例

8.1 BME280 传感器读取

const struct device *dev = DEVICE_DT_GET_ANY(bosch_bme280);

while (1) {
    sensor_sample_fetch(dev);
    
    struct sensor_value temp, press, hum;
    sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP, &temp);
    sensor_channel_get(dev, SENSOR_CHAN_PRESS, &press);
    sensor_channel_get(dev, SENSOR_CHAN_HUMIDITY, &hum);
    
    printk("T: %.2f°C, P: %.2fhPa, H: %.2f%%\n",
           sensor_value_to_double(&temp),
           sensor_value_to_double(&press) / 100,  // hPa
           sensor_value_to_double(&hum));
    
    k_sleep(K_MSEC(1000));
}

8.2 加速度计数据读取

static const enum sensor_channel channels[] = {
    SENSOR_CHAN_ACCEL_X,
    SENSOR_CHAN_ACCEL_Y,
    SENSOR_CHAN_ACCEL_Z,
};

int print_accel(const struct device *dev)
{
    struct sensor_value accel[3];
    
    sensor_sample_fetch(dev);
    
    for (int i = 0; i < 3; i++) {
        sensor_channel_get(dev, channels[i], &accel[i]);
    }
    
    printk("Accel: X=%.3f Y=%.3f Z=%.3f m/s^2\n",
           sensor_value_to_double(&accel[0]),
           sensor_value_to_double(&accel[1]),
           sensor_value_to_double(&accel[2]));
}

9. 配置选项 (prj.conf)

CONFIG_I2C=y              # 启用 I2C 驱动
CONFIG_SENSOR=y           # 启用 Sensor 子系统
CONFIG_SENSOR_ASYNC_API=y # 启用异步 API(可选)

# 具体传感器驱动
CONFIG_BME280=y           # BME280 驱动
CONFIG_LIS2DH=y           # LIS2DH 加速度计驱动
CONFIG_CBPRINTF_FP_SUPPORT=y  # 浮点打印支持

10. 关键问题总结

问题答案
I2C 地址在哪设?设备树 reg 属性
如何读取传感器?sensor_sample_fetch() + sensor_channel_get()
采样频率怎么改?sensor_attr_set(SENSOR_ATTR_SAMPLING_FREQUENCY)
I2C 速率怎么设?设备树 clock-frequency
数据格式是什么?struct sensor_value (整数+小数)

11. 与其他示例对比

示例通信方式API 层级
I2C 传感器I2CSensor API(高层)
SPI spi_flashSPIFlash API(中层)
UART echo_botUART驱动 API(低层)

12. 学习心得

  1. Sensor API:提供统一接口,隐藏硬件差异
  2. 设备树配置:I2C 地址、引脚都在 DTS 中配置
  3. 同步 vs 异步:新 API 支持异步读取,适合高吞吐场景
  4. I2C 调试:先用 i2c_write_read() 底层 API 确认通信正常

总结:三大外设学习笔记全部完成!

示例学习笔记文件
PWM LEDnotes/PWM_LED_Learning_Notes.md
ADC 采样notes/ADC_Learning_Notes.md
I2C 传感器notes/I2C_Sensor_Learning_Notes.md

小白 🤖 2026-03-14