我正试图从Teensy 2.0连接BNO055分线板,但我从BNO055读取时遇到麻烦。它有不同的寄存器,你可以从芯片读取数据。首先,我只是试着读取一些内部部件的id。不管我怎么做,我似乎只得到最后一个我放入TWDR
的值。试着读一遍似乎并没有填充它。这是我在main中的内容:
void main(void) {
CPU_PRESCALE(CPU_16MHz);
init_sensor();
char buffer[50];
sprintf(buffer, "Chip ID: %Xn", read_address(0x00));
while(1) {
_delay_ms(20);
}
}
这是我的bno055:
#include "BNO055.h"
#include <avr/io.h>
// The breakout board pulls COM3_state low internally
#define DEVICE_ADDR 0x29
#define READ 0
#define WRITE 1
#define F_CPU 16000000UL
#define SCL_CLOCK 400000L
inline void start(void) {
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
while ( !(TWCR & (1<<TWINT)));
}
inline void stop(void) {
TWCR = (1 << TWINT) | (1 << TWSTO) | (1 << TWEN);
while(TWCR & (1 << TWSTO));
}
void send_address(uint8_t w) {
TWDR = (DEVICE_ADDR<<1) | w ;
TWCR = (1 << TWINT) | (1<<TWEN);
while(!(TWCR & (1 << TWINT)));
}
uint8_t read(void) {
TWCR = (1 << TWINT) | (1 << TWEN);
while(!(TWCR & (1 << TWINT)));
return TWDR;
}
void write(uint8_t data) {
TWDR = data;
TWCR = (1 << TWINT) | (1<<TWEN);
while(!(TWCR & (1 << TWINT)));
}
uint8_t read_address(uint8_t addr) {
start();
send_address(WRITE);
write(addr);
stop();
start();
send_address(READ);
const uint8_t value = read();
stop();
return value;
}
void write_address(uint8_t addr, uint8_t value) {
start();
send_address(WRITE);
write(addr);
write(value);
stop();
}
uint8_t status(void) {
return TWSR & 0xF8;
}
void init_sensor(void) {
// Setup I2C
DDRD &= ~((1 << 0) | (1 << 1));
PORTD |= ((1 << 0) | (1 << 1));
TWBR = ((( F_CPU / SCL_CLOCK ) - 16) / 2);
TWSR = 0;
TWCR = ((1 << TWEN) | (1 << TWIE) | (1 << TWEA));
}
这个答案是在用这个答案在行更新问题中的代码之前编写的。如果不知道这些,你就会对这个问题的答案感到困惑。
你遇到的基本问题是你在不属于它们的地方发布I2C启动条件和I2C停止条件。START和STOP条件在整个I2C事务的开始和结束处运行,而不是在每个单独的字节周围运行。
事务的数据部分总是完全由要读的数据或完全由要写的数据组成。因此,对典型I2C设备的寄存器执行读取需要两个I2C事务。一个涉及写入寄存器地址,下一个涉及读取寄存器的数据,如下所示:
-
[I2C-START]
[WRITE(I2C-DEVICE-WRITING-ADDRESS)]
[WRITE(REGISTER-ADDRESS)]
[I2C-STOP]
写事务是我们通知I2C设备我们想要读取数据的寄存器。 -
[I2C-START]
[WRITE(I2C-DEVICE-READING-ADDESS)]
[READ(REGISTER-DATA)]
[I2C-STOP]
传输数据本身的读事务。
可能第一个的最后一站可以/应该被省略,以创建I2C"重复启动";(你可能不需要)。为了避免混淆,我将采用上述方案。
你现在拥有的,看起来更像这样:
[I2C-START]
[WRITE(I2C-DEVICE-WRITING-ADDRESS)]
[I2C-STOP]
写事务为空[I2C-START]
[WRITE(REGISTER-ADDRESS)]
[I2C-STOP]
现在一个尝试使用设备寄存器地址作为I2C总线地址的事务[I2C-START]
[WRITE(I2C-DEVICE-READING-ADDRESS)]
[I2C-STOP]
你已经开始了一个读事务,但没有读取任何数据,如果你发出了一个read。[I2C-START]
[READ(?)]
[I2C-STOP]
当你应该写I2C地址(无论是读还是写事务)时,尝试读取
后一个可能是你遇到最大麻烦的地方,也是为什么你看到一个不变的TWDR
;作为一个I2C主机,你永远不会像那样在开始条件之后立即读取一个字节,因为总是假设在那里写入一个包含地址位的字节。
在你的代码中,你会想要这样的东西:
uint8_t read_address(uint8_t addr) {
// The first transaction
// that tell the I2C device
// what register-address we want to read
start();
send_address(WRITE);
write(addr);
stop();
// The read transaction to get the data
// at the register address
// given in the prior transaction.
start();
send_address(READ);
const uint8_t r = read();
stop();
return r;
}
…其中send_address()
、write()
和read()
都不包含对start()
或stop()
的调用。
start()
和stop()
移动到正确的位置确实使事情从非功能变为功能。所以,当我说"诸如"以上代码,我的意思是非常像。=)可能还有其他错误,但这是主要问题。