Как сгенерировать аппаратное прерывание в mpu6050 для пробуждения Arduino из режима SLEEP_MODE_PWR_DOWN?

Я использую Arduino UNO и сохраняю режим SLEEP_MODE_PWR_DOWN & хотите, чтобы он просыпался с помощью аппаратного контакта INT MPU6050 (который должен посылать сигнал, когда MPU находится в движении). Я использовал статью https://lukelectro. wordpress.com/2016/08/11/how-to-enable-motion-detection-interrupt-on-mpu6050/

    #include <Wire.h>
    //Аналоговый порт 4 (A4) = SDA (последовательные данные)
    // Аналоговый порт 5 (A5) = SCL (последовательные часы)
    #define SIGNAL_PATH_RESET  0x68
    #define I2C_SLV0_ADDR      0x37
    #define ACCEL_CONFIG       0x1C 
    #define MOT_THR            0x1F  // Биты порога обнаружения движения [7:0]
    #define MOT_DUR            0x20  // Порог счетчика длительности для генерации прерывания движения, частота 1 кГц, LSB = 1 мс
    #define MOT_DETECT_CTRL    0x69
    #define INT_ENABLE         0x38
    #define WHO_AM_I_MPU6050   0x75 // Должен вернуть 0x68
    #define INT_STATUS 0x3A
    // когда ничего не подключено к AD0, чем адрес 0x68
    #define ADO 0
    #if ADO
    #define MPU6050_ADDRESS 0x69  // Адрес устройства, когда ADO = 1
    #else
    #define MPU6050_ADDRESS 0x68  // Адрес устройства, когда ADO = 0
    #endif

    /*    Example for using write byte
          Configure the accelerometer for self-test
          writeByte(MPU6050_ADDRESS, ACCEL_CONFIG, 0xF0); // Включить самотестирование по всем трем осям и установить диапазон акселерометра на +/- 8g */
    void writeByte(uint8_t address, uint8_t subAddress, uint8_t data)
    {
      Wire.begin();
      Wire.beginTransmission(address);  // Инициализировать буфер Tx
      Wire.write(subAddress);           // Помещаем адрес подчиненного регистра в буфер Tx
      Wire.write(data);                 // Помещаем данные в буфер Tx
      Wire.endTransmission();           // Отправляем буфер Tx
    // Serial.println("mnnj");

    }

    //пример использования readbytev ---- readByte(MPU6050_ADDRESS, GYRO_CONFIG);
    uint8

_t readByte(uint8_t address, uint8_t subAddress)
{
  uint8_t data;                            // `data` будет хранить данные регистра
  Wire.beginTransmission(address);         // Инициализировать буфер Tx
  Wire.write(subAddress);                  // Помещаем адрес подчиненного регистра в буфер Tx
  Wire.endTransmission(false);             // Отправляем буфер Tx, но отправляем перезагрузку, чтобы поддерживать соединение
  Wire.requestFrom(address, (uint8_t) 1);  // Читаем один байт из адреса подчиненного регистра
  data = Wire.read();                      // Заполняем буфер Rx результатом
  return data;                             // Возвращаем данные, считанные из ведомого регистра
}


void setup() 
{
   /*
    * #define SIGNAL_PATH_RESET  0x68
      #define I2C_SLV0_ADDR      0x37
      #define ACCEL_CONFIG       0x1C 
      #define MOT_THR            0x1F  // Биты порога обнаружения движения [7:0]
      #define MOT_DUR            0x20  // Порог счетчика длительности для генерации прерывания движения, частота 1 кГц, LSB = 1 мс
      #define MOT_DETECT_CTRL    0x69
      #define INT_ENABLE         0x38
      #define WHO_AM_I_MPU6050 0x75 // Должен вернуть 0x68
      #define INT_STATUS 0x3A*/
    Serial.begin(9600);
    writeByte( MPU6050_ADDRESS, SIGNAL_PATH_RESET, 0x07);//Сбросьте все внутренние пути прохождения сигнала в MPU-6050, записав 0x07 в регистр 0x68;
    writeByte( MPU6050_ADDRESS, I2C_SLV0_ADDR, 0x20);// запись в регистр 0x37 для выбора способа использования вывода прерывания. Для активного высокого уровня двухтактного сигнала, который сохраняется до тех пор, пока не будет прочитан регистр (десятичный) 58, запишите 0x20.
    writeByte( MPU6050_ADDRESS, ACCEL_CONFIG, 0x01);//Запись регистра 28 (==0x1C) для установки цифрового фильтра верхних частот, биты 3:0. Например, установите его на 0x01 для 5 Гц. (Эти 3 бита выделены серым цветом в таблице данных, но они используются! Если оставить их равными 0, фильтр всегда выводит 0.)
    writeByte( MPU6050_ADDRESS, MOT_THR, 0x14);  // Запишите желаемый порог движения в регистр 0x1F (например, запишите десятичное число 20).
    writeByte( MPU6050_ADDRESS, MOT_DUR, 0x01 );  // Установить длительность обнаружения движения на 1 мс; LSB составляет 1 мс при частоте 1 кГц.
    writeByte( MPU6050_ADDRESS, MOT_DETECT_CTRL, 0x15); // чтобы зарегистрировать 0x69, запишите декремент обнаружения движения и несколько других настроек (например, напишите 0x15, чтобы установить декременты свободного падения и движения на 1 и задержку запуска акселерометра до 5 мс, добавив 1 мс. )
    writeByte( MPU6050_ADDRESS, INT_ENABLE, 0x40 ); // запись в регистр 0x38, бит 6 (0x40), чтобы включить прерывание обнаружения движения. */

    pinMode(13, OUTPUT);      // устанавливает вывод 13 цифрового светодиода в качестве выхода
    pinMode(7, INPUT);        // устанавливает цифровой вывод 7 в качестве входа

}
    uint16_t readdata;
void loop()
{
      Serial.println("scdf");

      if(digitalRead(7)==1)
      {
        while(1)
        {
          digitalWrite(13, 1);
          delay(100);
        }
      }

  // readdata = readByte(MPU6050_ADDRESS,WHO_AM_I_MPU6050);
  // Serial.println(readdata);
}

, 👍8

Обсуждение

Используете ли вы библиотеку для настройки MPU6050 или взаимодействуете с ним вручную с помощью библиотеки Wire?, @Majenko

Привет @Majenko..! Я отредактировал вопрос и добавил код ..! в этом я просто использую библиотеку проводов, но в этом выводе INT mpu не генерируется триггер при движении, как это должно быть в соответствии со статьей., @bandejiya


1 ответ


8

Что ж, этот код сработал. В этом коде вывод INT MPU имеет активный низкий уровень, поэтому генерирует заземление при движении, которое затем подключается к контакту 2 INT0 или arduino UNO, который генерирует прерывание и пробуждает MCU

#include <avr/sleep.h>

#include <Wire.h>
 //Аналоговый порт 4 (A4) = SDA (последовательные данные)
// Аналоговый порт 5 (A5) = SCL (последовательные часы)
#define SIGNAL_PATH_RESET 0x68
#define I2C_SLV0_ADDR 0x37
#define ACCEL_CONFIG 0x1C
#define MOT_THR 0x1F // Биты порога обнаружения движения [7:0]
#define MOT_DUR 0x20 // Порог счетчика длительности для генерации прерывания движения, частота 1 кГц, LSB = 1 мс
#define MOT_DETECT_CTRL 0x69
#define INT_ENABLE 0x38
#define WHO_AM_I_MPU6050 0x75 // Должен вернуть 0x68
#define INT_STATUS 0x3A
// когда ничего не подключено к AD0, чем адрес 0x68
#define ADO 0
#if ADO
#define MPU6050_ADDRESS 0x69 // Адрес устройства, когда ADO = 1
#else
#define MPU6050_ADDRESS 0x68 // Адрес устройства, когда ADO = 0
#endif

int wakePin = 2; // пин, используемый для пробуждения
int led = 13;
int flag = 0;

void wakeUpNow() { // ПРОГРАММА ПРОДОЛЖАЕТСЯ ПОСЛЕ ПРОБУЖДЕНИЯ (т.е. после получения прерывания)
  // выполнить здесь код после пробуждения перед возвратом в функцию loop()
  // таймеры и код с использованием таймеров (serial.print и т. д.) здесь не работают.
  // нам действительно не нужно выполнять здесь какие-либо специальные функции, так как мы
  // просто хотим, чтобы вещь проснулась

  delay(500);
  Serial.println("WOKEN UP !!!!!!!!!!");
  delay(500);
  int count = 10;
  while (count != 0) {
    delay(1000);
    count--;
    Serial.println(count);
    delay(1000);
  }
  // в целях предосторожности, пока мы занимаемся другими делами
  detachInterrupt(0);

}

/*    Example for using write byte
      Configure the accelerometer for self-test
      writeByte(MPU6050_ADDRESS, ACCEL_CONFIG, 0xF0); // Включить самотестирование по всем трем осям и установить диапазон акселерометра на +/- 8g */
void writeByte(uint8_t address, uint8_t subAddress, uint8_t data) {
  Wire.begin();
  Wire.beginTransmission(address); // Инициализировать буфер Tx
  Wire.write(subAddress); // Помещаем адрес подчиненного регистра в буфер Tx
  Wire.write(data); // Помещаем данные в буфер Tx
  Wire.endTransmission(); // Отправляем буфер Tx
  // Serial.println("mnnj");

}

//пример использования readbytev ---- readByte(MPU6050_ADDRESS, GYRO_CONFIG);
uint8_t readByte(uint8_t address, uint8_t subAddress) {
  uint8_t data; // `data` будет хранить данные регистра
  Wire.beginTransmission(address); // Инициализировать буфер Tx
  Wire.write(subAddress); // Помещаем адрес подчиненного регистра в буфер Tx
  Wire.endTransmission(false); // Отправляем буфер Tx, но отправляем перезагрузку, чтобы поддерживать соединение
  Wire.requestFrom(address, (uint8_t) 1); // Читаем один байт из адреса подчиненного регистра
  data = Wire.read(); // Заполняем буфер Rx результатом
  return data; // Возвращаем данные, считанные из ведомого регистра
}

void setup() {

  /*
   * #define SIGNAL_PATH_RESET  0x68
     #define I2C_SLV0_ADDR      0x37
     #define ACCEL_CONFIG       0x1C 
     #define MOT_THR            0x1F  // Биты порога обнаружения движения [7:0]
     #define MOT_DUR            0x20  // Порог счетчика длительности для генерации прерывания движения, частота 1 кГц, LSB = 1 мс
     #define MOT_DETECT_CTRL    0x69
     #define INT_ENABLE         0x38
     #define WHO_AM_I_MPU6050 0x75 // Должен вернуть 0x68
     #define INT_STATUS 0x3A*/
  Serial.begin(9600);
  writeByte(MPU6050_ADDRESS, 0x6B, 0x00);
  writeByte(MPU6050_ADDRESS, SIGNAL_PATH_RESET, 0x07); // Сбросьте все внутренние пути прохождения сигнала в MPU-6050, записав 0x07 в регистр 0x68;
  writeByte(MPU6050_ADDRESS, I2C_SLV0_ADDR, 0x20); // запись в регистр 0x37, чтобы выбрать, как использовать вывод прерывания. Для активного высокого уровня двухтактного сигнала, который сохраняется до тех пор, пока не будет прочитан регистр (десятичный) 58, запишите 0x20.
  writeByte(MPU6050_ADDRESS, ACCEL_CONFIG, 0x01); //Запись регистра 28 (==0x1C), чтобы установить цифровой фильтр верхних частот, биты 3:0. Например, установите его на 0x01 для 5 Гц. (Эти 3 бита выделены серым цветом в таблице данных, но они используются! Если оставить их равными 0, фильтр всегда выводит 0.)
  writeByte(MPU6050_ADDRESS, MOT_THR, 10); // Запишите желаемый порог движения в регистр 0x1F (например, запишите десятичное число 20).
  writeByte(MPU6050_ADDRESS, MOT_DUR, 40); // Установить длительность обнаружения движения на 1 мс; LSB составляет 1 мс при частоте 1 кГц.
  writeByte(MPU6050_ADDRESS, MOT_DETECT_CTRL, 0x15); // чтобы зарегистрировать 0x69, запишите декремент обнаружения движения и несколько других настроек (например, напишите 0x15, чтобы установить декременты свободного падения и движения на 1 и задержку запуска акселерометра до 5 мс, добавив 1 мс. )
  writeByte(MPU6050_ADDRESS, INT_ENABLE, 0x40); // запись в регистр 0x38, бит 6 (0x40), чтобы включить прерывание обнаружения движения.
  writeByte(MPU6050_ADDRESS, 0x37, 160); // теперь вывод INT активен низким уровнем

  pinMode(2, INPUT); // устанавливает цифровой вывод 7 в качестве входа

  pinMode(wakePin, INPUT_PULLUP); // wakePin — это номер контакта. 2
  pinMode(led, OUTPUT); // светодиод - это номер контакта. 13
  // attachInterrupt(0, wakeUpNow, LOW); // используем прерывание 0 (вывод 2) и запускаем функцию wakeUpNow, когда вывод 2 получает НИЗКИЙ уровень

}

void sleepNow() {
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // здесь устанавливается спящий режим
  sleep_enable(); // включает бит сна в регистре mcucr
  delay(500);
  Serial.println("About to sleep");
  delay(500);
  attachInterrupt(0, wakeUpNow, LOW); // используем прерывание 0 (вывод 2) и запускаем функцию
  delay(500);
  Serial.println("Interupt attached");
  delay(500);
  sleep_mode(); // здесь устройство фактически усыпляется...!!

  // ПРОГРАММА ПРОДОЛЖАЕТСЯ С ЭТОГО МЕСТА ПОСЛЕ ЗАКРЫТИЯ ПРЕРЫВАНИЯ
  delay(500);
  Serial.println("Continuing main program after interupt");
  delay(500);

  sleep_disable(); // первое, что нужно сделать после пробуждения ото сна: отключить сон...
  delay(500);
  Serial.println("Sleep disabled");
  delay(500);
}
uint16_t readdata;
void loop() {
  if (digitalRead(2) == 0) {
    {

      digitalWrite(13, 1);
      delay(100);
      digitalWrite(13, 0);
      delay(100);
    }
  }
  sleepNow(); // здесь вызывается функция сна
  readdata = readByte(MPU6050_ADDRESS, 0x3A);
  Serial.print(readdata);
  Serial.print(",");
  readdata = readByte(MPU6050_ADDRESS, 0x37);
  Serial.println(readdata);

}
,

Рад видеть, что у вас все получилось. Спасибо, что поделились своим решением., @Gerben

Мне нужно было поместить detachInterrupt() в функцию wakeUpNow(). В остальном отлично работает., @Patrick McGloin

+1 У меня отлично сработало. Нравится, что внешние библиотеки не нужны., @blak3r

У меня не работало, пока я не узнал, что MPU (дополнительно к MCU) должен очистить прерывание, прочитав: readByte (MPU6050_ADDRESS, 0x3A); И это не сработало в самом ISR, поэтому я установил логическое значение «прервано», и вне ISR отреагировал на это вызовом readByte. Также сделайте это после инициализации MPU., @user2081279