You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Teensy 2.0通过I2C读取BNO055异常,仅返回TWDR最后写入值

问题根因与修复方案

核心错误点

  • I2C读写标记定义完全颠倒:I2C协议规定地址最低位为1表示读操作,0表示写操作,你当前的宏定义将READ设为0、WRITE设为1,导致发送读命令实际是发送写指令,设备不会返回数据,TWDR自然保留最后一次写入的值。
  • I2C初始化错误开启了中断使能:你在init_sensor里设置了TWIE位,但没有实现对应的I2C中断服务函数,一旦I2C事件触发就会导致程序跑飞,无法正常执行通信逻辑。
  • 单字节读取流程不符合BNO055要求:写完寄存器地址后直接发送STOP信号,部分I2C设备会清除之前的寄存器地址缓存,导致后续读操作寻址错误。
  • 单字节读取时未返回NACK信号:只读1字节时需要在读取前清除应答位TWEA,避免设备持续发送数据导致总线异常。

修复后的代码修改点

1. 修正读写宏定义

// 原错误定义
// #define READ 0
// #define WRITE 1
// 修正后
#define READ 1
#define WRITE 0

2. 修正I2C初始化配置,关闭不必要的中断使能

void init_sensor(void) {
    // 配置I2C
    DDRD &= ~((1 << 0) | (1 << 1));
    PORTD |= ((1 << 0) | (1 << 1));

    TWBR = ((( F_CPU / SCL_CLOCK ) - 16) / 2);
    TWSR = 0;
    // 去掉TWIE位,关闭I2C中断,使用轮询模式
    TWCR = ((1 << TWEN) | (1 << TWEA));
}

3. 修正单字节读取函数,去掉多余的STOP,添加NACK配置

uint8_t read_address(uint8_t addr) {
    start();
    send_address(WRITE);
    write(addr);
    // 删掉原代码这里的stop(),用重复起始信号保持设备的寄存器地址缓存
    start();
    send_address(READ);
    // 只读1字节,先清除TWEA,设备收到NACK就会停止发送
    TWCR = (1 << TWINT) | (1 << TWEN);
    while(!(TWCR & (1 << TWINT)));
    const uint8_t value = TWDR;
    stop();
    // 恢复TWEA方便后续多字节读取操作
    TWCR |= (1 << TWEA);
    return value;
}

验证方法

修复后读取BNO055的芯片ID寄存器0x00,返回值应该是0xA0,如果能读到该值说明通信正常。

内容的提问来源于stack exchange,提问作者CaseyB

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 12:36:04