简体   繁体   English

通过寄存器 avr c Atmega328p 正确配置 I2C

[英]Correct configuration I2C via registers avr c Atmega328p

To expand my understanding and current knowledge of programming the Atmega series I've started my own little project.为了扩展我对 Atmega 系列编程的理解和当前知识,我开始了自己的小项目。 I've decided to program the MPU6050 I2C sensor by directly addressing it's registers.我决定通过直接寻址它的寄存器来对 MPU6050 I2C 传感器进行编程。 To test the sensor's working and connections I used this pre-written program first (using the Wire library provided by the Arduino platform):为了测试传感器的工作和连接,我首先使用了这个预先编写的程序(使用 Arduino 平台提供的 Wire 库):

#include<Wire.h>
const int MPU=0x68; 
int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;

void setup(){
  Wire.begin();
  Wire.beginTransmission(MPU);
  Wire.write(0x6B); 
  Wire.write(0);    
  Wire.endTransmission(true);
  Serial.begin(9600);
}
void loop(){
  Wire.beginTransmission(MPU);
  Wire.write(0x3B);  
  Wire.endTransmission(false);
  Wire.requestFrom(MPU,12,true);  
  AcX=Wire.read()<<8|Wire.read();    
  AcY=Wire.read()<<8|Wire.read();  
  AcZ=Wire.read()<<8|Wire.read();  
  GyX=Wire.read()<<8|Wire.read();  
  GyY=Wire.read()<<8|Wire.read();  
  GyZ=Wire.read()<<8|Wire.read();  

  Serial.print("Accelerometer: ");
  Serial.print("X = "); Serial.print(AcX);
  Serial.print(" | Y = "); Serial.print(AcY);
  Serial.print(" | Z = "); Serial.println(AcZ); 

  Serial.print("Gyroscope: ");
  Serial.print("X = "); Serial.print(GyX);
  Serial.print(" | Y = "); Serial.print(GyY);
  Serial.print(" | Z = "); Serial.println(GyZ);
  Serial.println(" ");
  delay(333);
}

The serial monitor displays correct readings from the sensor, stating the sensor is connected properly and working as it should.串行监视器显示传感器的正确读数,说明传感器连接正确并正常工作。 Simultaneously I've connected my bus pirate which sniffs the I2C bus and displays the hex values which are sent between the sensor and atmega chip (Atmega328p-pu).同时我连接了我的总线盗版,它嗅探 I2C 总线并显示在传感器和 atmega 芯片(Atmega328p-pu)之间发送的十六进制值。 Although I can't translate them to their exact meaning, the communication looks normal.虽然我无法将它们翻译成它们的确切含义,但交流看起来很正常。

After I've uploaded my own code, created using the datasheet of both the sensor and the Atmega328p, the I2C bus displays almost nothing.在我上传了自己的代码后,使用传感器和 Atmega328p 的数据表创建,I2C 总线几乎没有显示任何内容。 This is my code:这是我的代码:

#include <Arduino.h>

#define DEBUG_LED_PIN PB5
#define DEBUG_LED_BANK DDRB

#define MPU6050_ADDR_WRITE 0x68
#define MPU6050_ADDR_READ 0x69


const uint8_t led_on = 0x01;
const uint8_t led_off = 0x00;

const uint8_t twi_write = 0x00;
const uint8_t twi_read = 0x01;

const uint8_t twi_start_transmitted = 0x08;
const uint8_t twi_repeated_start_transmitted = 0x10;
const uint8_t twi_ack_received_addr = 0x18;
const uint8_t twi_not_ack_received_addr = 0x20;
const uint8_t twi_ack_received_data = 0x28;
const uint8_t twi_not_ack_received_data = 0x30;
const uint8_t twi_arbitration_lost = 0x38;


void setup_debug_led();
void setup_twi();
void setup_mpu6050();

void control_debug_led(uint8_t status);

void send_data_to_device(uint8_t device_address, uint8_t* data, uint8_t data_length);
void send_start_signal();
void send_device_address(uint8_t device_address);
void send_data(uint8_t data);
void send_stop_signal();

void send_test_data_complete_sequence();


void setup() {
  setup_debug_led();
  control_debug_led(led_off);

  setup_twi(); // Setup i2c protocol
}

void loop() {
  // Test stuff
  send_test_data_complete_sequence();
}

void setup_twi()
{
  TWSR = 0;
  TWCR = 0;

  TWBR = 0x0C; // Set SCL to 400kHz
}

void send_test_data_complete_sequence()
{
  // 1.
  TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN); // Write start condition

  // 2.
  while(!(TWCR & (1 << TWINT))); // Wait for TWINT flag set. Indicates START has been transmitted

  // 3.
  if((TWSR & 0xF8) != twi_start_transmitted) // Check value TWI status register, if equals 0x08 continue
  {
    // Handle exception call
  }

  // 4.
  TWDR = 0b11010000; // Load slave address including r/w bit

  // 5.
  TWCR = (1 << TWINT) | (1 << TWEN); // Clear TWINT bit to start tranmission

  // 6.
  while(!(TWCR & (1 << TWINT))); // Wait for TWINT flag set. Inidicates SLA+W has been transmitted

  // 7.
  if((TWSR & 0xF8) != twi_ack_received_addr) // Check for 0x18 (SLA+W ACK has been received)
  {
    // Handle exception call
  }

  // 8.
  TWDR = 0x6B; // Load data into TWDR

  // 9.
  TWCR = (1 << TWINT) | (1 << TWEN); // Clear TWINT bit to start data tranmission

  // 10.
  while(!(TWCR & (1 << TWINT))); // Wait for TWINT flag set. Inidicates data has been transmitted

  // 11.
  if((TWSR & 0xF8) != twi_ack_received_data) // Check for 0x28 (data ACK has been received)
  {
    // Handle exception
  }

  // 12.
  TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO); // Write stop condition
}

The bus pirate shows the following communication data on the general bus: [][[][][[[]] It's nothing like the data I transfer into the buffer.总线盗版在通用总线上显示如下通信数据: [][[][][[[]]这和我传输到缓冲区的数据完全不同。 And if I wait long enough I'll spot some incidental 0x0 or 0x00's.如果我等待足够长的时间,我会发现一些偶然的 0x0 或 0x00。

I've tried changing the slave's address and the first data byte which is written to the sensor.我尝试更改从设备的地址和写入传感器的第一个数据字节。 The exceptions at steps 3, 7 and 11 are never triggered.步骤 3、7 和 11 的异常永远不会被触发。

The pins A4 and A5 (SCL and SDA) are not touched in this code.此代码中未触及引脚 A4 和 A5(SCL 和 SDA)。 I've tried configuring them as input with pull-up resistors enabled, which didn't give the desired results either.我尝试将它们配置为启用上拉电阻的输入,但这也没有给出预期的结果。

The sensor is connected to the arduino as stated in the scheme below:传感器连接到 arduino,如下图所示:

(Arduino)   (MPU6050)
A4 (SDA) -> SDA
A5 (SCL) -> SCL
VCC      -> VCC
GND      -> GND
GND      -> AD0

The bus pirate is connected in parallel with the SDA and SCL connections to intercept its data.总线盗版与 SDA 和 SCL 连接并行连接以拦截其数据。

The Atmega328p's datasheet has a minor summary of all the steps needed to be taken to complete an I2C transfer. Atmega328p 的数据表简要总结了完成 I2C 传输所需的所有步骤。 I've tried to correctly copy these steps (Section 21.6 'Using the TWI', table 21-2).我尝试正确复制这些步骤(第 21.6 节“使用 TWI”,表 21-2)。

I was hoping you guys could spot my mistake or missing code.我希望你们能发现我的错误或丢失的代码。 Thanks so much in advance!提前非常感谢!

It looks like you have not enabled TWI in your setup_twi() function.看起来您尚未在setup_twi() function 中启用TWI

You need to switch it on by setting the TWEN bit in the TWCR register as written in the datasheet:您需要通过设置TWCR寄存器中的TWEN位来打开它,如数据表中所述:

The TWEN bit enables TWI operation and activates the TWI interface. TWEN 位使能 TWI 操作并激活 TWI 接口。 When TWEN is written to one, the TWI takes control over the I/O pins connected to the SCL and SDA pins, enabling the slew-rate limiters and spike filters.将 TWEN 写入 1 时,TWI 将控制连接到 SCL 和 SDA 引脚的 I/O 引脚,从而启用压摆率限制器和尖峰滤波器。 If this bit is written to zero, the TWI is switched off and all TWI transmissions are terminated, regardless of any ongoing operation.如果该位被写入 0,则 TWI 被关闭并且所有 TWI 传输都被终止,而不管任何正在进行的操作。

I decided to simulate the sensor using another Arduino configured as a I2C slave.我决定使用另一个配置为 I2C 从机的 Arduino 来模拟传感器。 And apparently the Arduino datasheet example code in table 21-2 'Assembly code example' is incorrect.显然,表 21-2“汇编代码示例”中的 Arduino 数据表示例代码不正确。 Executing TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWSTO);执行TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWSTO); breaks the code execution.中断代码执行。 The TWINT must not be reset when transmitting the STOP signal.发送 STOP 信号时不得复位 TWINT。 Changing this line to TWCR=(1<<TWEN)|(1<<TWSTO);将此行更改为TWCR=(1<<TWEN)|(1<<TWSTO); did the trick.成功了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM