【问题标题】:Correct configuration I2C via registers avr c Atmega328p通过寄存器 avr c Atmega328p 正确配置 I2C
【发布时间】:2020-04-07 19:38:30
【问题描述】:

为了扩展我对 Atmega 系列编程的理解和当前知识,我开始了自己的小项目。我决定通过直接寻址它的寄存器来对 MPU6050 I2C 传感器进行编程。为了测试传感器的工作和连接,我首先使用了这个预先编写的程序(使用 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);
}

串行监视器显示来自传感器的正确读数,表明传感器已正确连接并正常工作。同时,我连接了我的总线盗版,它嗅探 I2C 总线并显示在传感器和 atmega 芯片(Atmega328p-pu)之间发送的十六进制值。虽然我无法将它们翻译成它们的确切含义,但通信看起来很正常。

在我上传了自己的代码(使用传感器和 Atmega328p 的数据表创建)后,I2C 总线几乎不显示任何内容。这是我的代码:

#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
}

总线盗版在通用总线上显示以下通信数据: [][[][][[[]] 这与我传输到缓冲区的数据完全不同。如果我等待足够长的时间,我会发现一些偶然的 0x0 或 0x00。

我尝试更改从机的地址和写入传感器的第一个数据字节。第 3、7 和 11 步的异常永远不会被触发。

此代码中未触及引脚 A4 和 A5(SCL 和 SDA)。我尝试将它们配置为启用上拉电阻的输入,但这也没有给出预期的结果。

传感器连接到 arduino,如下图所示:

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

总线盗版与 SDA 和 SCL 连接并联以拦截其数据。

Atmega328p 的数据表简要总结了完成 I2C 传输所需的所有步骤。我已尝试正确复制这些步骤(第 21.6 节“使用 TWI”,表 21-2)。

我希望你们能发现我的错误或丢失的代码。 提前非常感谢!

【问题讨论】:

  • 我们无法有效帮助解决此类问题。如果涉及硬件,我们无法确定问题出在哪里。
  • 确实涉及到一些硬件,但有故障的硬件不会造成问题,因为“正常”的 arduino Wire.h 代码就像一个魅力。这使它更像是一个代码配置问题。

标签: c arduino i2c mpu6050


【解决方案1】:

您的setup_twi() 函数中似乎没有启用TWI

您需要按照数据表中的说明设置TWCR 寄存器中的TWEN 位来打开它:

TWEN 位启用 TWI 操作并激活 TWI 接口。将 TWEN 写入 1 时,TWI 将控制连接到 SCL 和 SDA 引脚的 I/O 引脚,从而启用压摆率限制器和尖峰滤波器。如果该位被写入零,则 TWI 将关闭,所有 TWI 传输都将终止,无论任何正在进行的操作。

【讨论】:

  • 没错。我之前尝试过,将TWCR = (1 &lt;&lt; TWEN); 添加到设置函数似乎没有什么不同。刚刚测试它以确保它不工作。
  • 你的 setup 函数中的 debug_led 函数是做什么的?
  • debug_led 指的是 PB5(引脚 13 LED)。仅用于 PB5 的简单开/关功能。
【解决方案2】:

我决定使用另一个配置为 I2C 从机的 Arduino 来模拟传感器。显然,表 21-2“汇编代码示例”中的 Arduino 数据表示例代码不正确。执行TWCR=(1&lt;&lt;TWINT)|(1&lt;&lt;TWEN)|(1&lt;&lt;TWSTO); 会中断代码执行。发送 STOP 信号时不得复位 TWINT。将此行更改为 TWCR=(1&lt;&lt;TWEN)|(1&lt;&lt;TWSTO); 就可以了。

【讨论】:

    猜你喜欢
    • 2021-10-23
    • 1970-01-01
    • 2019-06-24
    • 1970-01-01
    • 1970-01-01
    • 2022-08-13
    • 1970-01-01
    • 2012-12-19
    • 1970-01-01
    相关资源
    最近更新 更多