Корректная конфигурация I2 C через регистры avr c Atmega328p - PullRequest
0 голосов
/ 07 апреля 2020

Чтобы расширить мои знания и знания в области программирования серии Atmega, я начал свой собственный маленький проект. Я решил запрограммировать датчик MPU6050 I2 C, напрямую обращаясь к его регистрам. Для проверки работоспособности датчика и подключений я сначала использовал эту предварительно написанную программу (используя библиотеку Wire, предоставляемую платформой Arduino):

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

Последовательный монитор отображает правильные показания датчика, указывая, что датчик подключен правильно и работает как надо. Одновременно я подключил своего пиратского автобуса, который анализирует шину I2 C и отображает шестнадцатеричные значения, которые передаются между датчиком и чипом atmega (Atmega328p-pu). Хотя я не могу перевести их в их точное значение, связь выглядит нормально.

После того, как я загрузил свой собственный код, созданный с использованием таблицы данных датчика и Atmega328p, шины I2 C почти ничего не отображает. Это мой код:

#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 содержит небольшую сводку всех шагов, необходимых для выполнения передачи I2 C. Я попытался правильно скопировать эти шаги (раздел 21.6 «Использование TWI», таблица 21-2).

Я надеялся, что вы, ребята, заметите мою ошибку или отсутствующий код. Большое спасибо заранее!

Ответы [ 2 ]

0 голосов
/ 08 апреля 2020

Я решил смоделировать датчик, используя другой Arduino, настроенный как слэйв I2 C. И, очевидно, код примера таблицы данных Arduino в таблице 21-2 «Пример кода сборки» неверен. Выполнение TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWSTO); прерывает выполнение кода. TWINT нельзя сбрасывать при передаче сигнала STOP. Изменение этой строки на TWCR=(1<<TWEN)|(1<<TWSTO); сделало свое дело.

0 голосов
/ 08 апреля 2020

Похоже, вы не включили TWI в своей функции setup_twi().

Вам необходимо включить его, установив бит TWEN в регистре TWCR, как написано в таблице данных. :

Бит TWEN включает работу TWI и активирует интерфейс TWI. Когда TWEN записывается на один, TWI получает контроль над контактами ввода / вывода, подключенными к контактам SCL и SDA, включая ограничители скорости нарастания и фильтры пиков. Если этот бит записан в ноль, TWI отключается и все передачи TWI прекращаются, независимо от продолжающейся операции.

...