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
相关产品推荐
相关产品推荐

