Совместимость кода с Arduino Due

В настоящее время я работаю над проектом квадрокоптера, который включает модуль акселерометра + гироскопа MPU6050, ультразвуковой датчик и управление электронными регуляторами скорости. Я использовал существующие примеры и модифицировал их, чтобы получить необработанные значения ускорения, углов тангажа и крена, а также расстояния от ультразвука. Вот длинный код.

#include "I2Cdev.h"

#include "MPU6050_6Axis_MotionApps20.h"
//#include "MPU6050.h" // не требуется, если используется включаемый файл MotionApps


#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
#include "Wire.h"
#endif

MPU6050 mpu;
//MPU6050 mpu(0x69); // <-- используем для высокого уровня AD0

const int trigPin = 9;
const int echoPin = 10;

#define INTERRUPT_PIN 2  // использовать контакт 2 на Arduino Uno & большинство плат
#define LED_PIN 13 // (Arduino — 13, Teensy — 11, Teensy++ — 6)
bool blinkState = false;

// переменные управления/состояния MPU
bool dmpReady = false;  // устанавливаем true, если инициализация DMP прошла успешно
uint8_t mpuIntStatus;   // содержит фактический байт состояния прерывания от MPU
uint8_t devStatus;
uint16_t packetSize;    // ожидаемый размер пакета DMP (по умолчанию 42 байта)
uint16_t fifoCount;     // подсчет всех байтов в настоящее время в FIFO
uint8_t fifoBuffer[64]; // Буфер хранения FIFO

// переменные ориентации/движения
Quaternion q;           // [w, x, y, z] контейнер кватернионов
VectorInt16 aa;         // [x, y, z] измерения датчика ускорения
VectorInt16 aaReal;     // [x, y, z]измерения датчика ускорения без гравитации
VectorInt16 aaWorld;    // [x, y, z] измерения датчика ускорения мирового кадра
VectorFloat gravity;    // [x, y, z] вектор гравитации
float euler[3];         // [пси, тета, фи] Контейнер угла Эйлера
float ypr[3], yprd[3] = {0, 0, 0};

// структура пакета для демонстрации чайника InvenSense
uint8_t teapotPacket[14] = {'$', 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x00, '\r', '\n' };

// =============================================== ================
// === ПРОЦЕДУРА ОБНАРУЖЕНИЯ ПРЕРЫВАНИЯ ===
// =============================================== ================
volatile bool mpuInterrupt = false;
void dmpDataReady() {
  mpuInterrupt = true;
}

// =============================================== ================
// === НАЧАЛЬНАЯ НАСТРОЙКА ===
// =============================================== ================
int16_t yo = 0, po = 0, ro = 0;
int lv = 1, flag = 0;
const int MPU_addr = 0x68; // I2C-адрес MPU-6050
int16_t AcX, AcY, AcZ, Tmp, GyX, GyY, GyZ;
int32_t AcXo = 0, AcYo = 0, AcZo = 0, prev, curr;
int32_t AcXd = 0, AcYd = 0, AcZd = 0;
int i = 1, flaggl = 0;
int  glc = 0, lv2 = 1;
long duration;
int distance, distanced;
void setup() {

#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
  Wire.begin();
  Wire.setClock(400000);
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
  Fastwire::setup(400, true);
#endif
  Serial.begin(115200);
  // инициализируем устройство
  Serial.println(F("Initializing I2C devices..."));
  mpu.initialize();
  pinMode(INTERRUPT_PIN, INPUT);
  // проверяем соединение
  Serial.println(F("Testing device connections..."));
  Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
  // ждем готовности
  Serial.println(F("\nSend any character to begin DMP programming and demo:
                   "));
  while (Serial.available() && Serial.read()); // пустой буфер
  while (!Serial.available());                 // ждем данных
  while (Serial.available() && Serial.read()); // снова пустой буфер
  // загрузить и настроить DMP
  Serial.println(F("Initializing DMP..."));
  devStatus = mpu.dmpInitialize();
  // укажите здесь собственные смещения гироскопа, масштабированные для минимальной чувствительности
  mpu.setXGyroOffset(220);
  mpu.setYGyroOffset(76);
  mpu.setZGyroOffset(-85);
  mpu.setZAccelOffset(1788); // 1688 по умолчанию для моего тестового чипа
  // убедиться, что это сработало (возвращает 0, если это так)
  if (devStatus == 0) {
    // включаем DMP, теперь, когда он готов
    Serial.println(F("Enabling DMP..."));
    mpu.setDMPEnabled(true);

    // включить обнаружение прерываний Arduino
    Serial.println(F("Enabling interrupt detection (Arduino external
                     interrupt 0)..."));
    attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);
    mpuIntStatus = mpu.getIntStatus();

    // устанавливаем наш флаг готовности DMP, чтобы основная функция loop() знала, что ее можно использовать
    Serial.println(F("DMP ready! Waiting for first interrupt..."));
    dmpReady = true;

    // получаем ожидаемый размер пакета DMP для последующего сравнения
    packetSize = mpu.dmpGetFIFOPacketSize();
  } else {
    // ОШИБКА!
    // 1 = первоначальная загрузка памяти не удалась
    // 2 = не удалось обновить конфигурацию DMP
    // (если он сломается, обычно код будет 1)
    Serial.print(F("DMP Initialization failed (code "));
    Serial.print(devStatus);
    Serial.println(F(")"));
  }

  // настроить светодиод для вывода
  pinMode(LED_PIN, OUTPUT);
  glc = 0;
  distance = distanced = 0;
}

// =============================================== ================
// === ГЛАВНЫЙ ПРОГРАММНЫЙ ЦИКЛ ===
// =============================================== ================
void loop() {
  // если программирование не удалось, не пытайтесь ничего сделать
  if (!dmpReady) return;

  // ждем прерывания MPU или доступных дополнительных пакетов
  while (!mpuInterrupt && fifoCount < packetSize) {
  }
  mpuInterrupt = false;
  mpuIntStatus = mpu.getIntStatus();
  // получаем текущий счетчик FIFO
  fifoCount = mpu.getFIFOCount();
  // проверка на переполнение (это никогда не должно происходить, если только наш код не слишком неэффективен)
  if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
    // сбрасываем, чтобы мы могли продолжить работу без ошибок
    mpu.resetFIFO();
    Serial.println(F("FIFO overflow!"));
    // в противном случае проверьте прерывание готовности данных DMP (это должно происходить часто)
  } else if (mpuIntStatus & 0x02) {
    // ожидание правильной доступной длины данных, должно быть ОЧЕНЬ короткое ожидание
    while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
    // читаем пакет из FIFO
    mpu.getFIFOBytes(fifoBuffer, packetSize);
    // отслеживаем количество FIFO здесь, если есть > 1 пакет в наличии
    // (это позволяет нам сразу читать больше, не дожидаясь прерывания)
    fifoCount -= packetSize;
    //#ifdef OUTPUT_READABLE_YAWPITCHROLL
    // отображаем углы Эйлера в градусах
    mpu.dmpGetQuaternion(&q, fifoBuffer);
    mpu.dmpGetGravity(&gravity, &q);
    mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
    if (millis() > 30000 && flag == 0) {
      if ( flag == 0 && lv  < 21) {
        yo += ypr[0] * 180 / M_PI;
        po += ypr[1] * 180 / M_PI;
        ro += ypr[2] * 180 / M_PI;
        ++lv;
      } else if (flag == 0) {
        flag = 1;
        yo /= 20;
        po /= 20;
        ro /= 20;
      }
    }
    //#endif
    // мигать светодиодом для индикации активности
    blinkState = !blinkState;
    digitalWrite(LED_PIN, blinkState);
    Wire.beginTransmission(MPU_addr);
    Wire.write(0x3B);  // начиная с регистра 0x3B (ACCEL_XOUT_H)
    Wire.endTransmission(false);
    Wire.requestFrom(MPU_addr, 14, true); // запрашиваем всего 14 регистров
    AcX = Wire.read() << 8 | Wire.read(); // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
    AcY = Wire.read() << 8 | Wire.read(); // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
    AcZ = Wire.read() << 8 | Wire.read(); // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
    Tmp = Wire.read() << 8 | Wire.read(); // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
    GyX = Wire.read() << 8 | Wire.read(); // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
    GyY = Wire.read() << 8 | Wire.read(); // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
    GyZ = Wire.read() << 8 | Wire.read(); // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)
    mpu.resetFIFO();
    if (i < 21 && !flaggl) {
      AcXo += AcX;
      AcYo += AcY;
      AcZo += AcZ;
      ++i;
      Serial.println();
      Serial.println("***************************************************************************");
    } else if (!flaggl) {
      flaggl = 1;
      AcXo  = AcXo / 20;
      AcYo = AcYo / 20;
      AcZo = AcZo / 20;
    }
    curr = (AcZ - AcZo / 100);
    if (prev == curr )
      ++glc;
    else {
      glc = 0;
      prev = curr;
    }
    if (glc == 10) {
      Serial.println("lock broken!!!");
      glc = 0;
      loop();
    }
    if (flag == 1 && flaggl == 1) {
      if (lv2 < 21) {
        delayMicroseconds(2);
        digitalWrite(trigPin, HIGH);
        delayMicroseconds(10);
        digitalWrite(trigPin, LOW);
        duration = pulseIn(echoPin, HIGH);
        distance = duration * 0.034 / 2;
        AcXd += (AcX - AcXo) / 100;
        AcYd += (AcY - AcYo) / 100;
        AcZd += (AcZ - AcZo) / 100;
        yprd[0] += ypr[0] * 180 / M_PI - yo;
        yprd[1] += ypr[1] * 180 / M_PI - po;
        yprd[2] += ypr[2] * 180 / M_PI - ro;
        distanced += distance;
        ++lv2;
      } else {
        AcXd /= 20;
        AcYd /= 20;
        AcZd /= 20;
        yprd[0] /= 20;
        yprd[1] /= 20;
        yprd[2] /= 20;
        distanced /= 20;
        lv2 = 1;
        Serial.print("AcX = ");
        Serial.print(AcXd);
        Serial.print(" | AcY = ");
        Serial.print(AcYd);
        Serial.print(" | AcZ = ");
        Serial.print(AcZd);
        Serial.print(" | yaw = ");
        Serial.print(yprd[0]);
        Serial.print(" | pitch = ");
        Serial.print(yprd[1]);
        Serial.print(" | roll = ");
        Serial.print(yprd[2]);
        Serial.print(" | distance = ");
        Serial.println(distanced);
        AcXd  = 0;
        AcYd = 0;
        AcZd = 0;
        yprd[0] = 0;
        yprd[1] = 0;
        yprd[2] = 0;
        distanced = 0;
      }
    }
  }
}

Код включает расчет смещения и отображает усредненные мгновенные значения. После преодоления проблем с переполнением буфера FIFO и некоторых других типичных проблем мне удалось запустить код, но через некоторое время Arduino зависает (код тестировался как на Uno, так и на Nano).

Поэтому кажется, что, возможно, мне нужна более высокая тактовая частота. Итак, я просмотрел другие платы Arduino, и Due показался мне решением, так как его тактовая частота составляет 84 МГц по сравнению с 16 МГц у Uno. Поэтому у меня вопрос, будут ли библиотеки и функции, используемые в этом коде, совместимы с Due? Кроме того, может ли работа Due на 3,3 В по сравнению с 5 В на Uno создавать какие-либо проблемы при взаимодействии с другими упомянутыми датчиками?

, 👍0

Обсуждение

Вероятно, вам следует потратить некоторое время на изучение *существующих* конструкций самолетов с открытым исходным кодом после ATmega. Чип от DUE, вероятно, можно заставить работать; во многих существующих проектах используются сопоставимые микроконтроллеры STM32, GigaDevices и Nuvoton ARM Cortex ... а также некоторые другие микроконтроллеры и особенно микроконтроллеры / радиочипы, которые в основном не задокументированы на западе., @Chris Stratton

Arduino Due является мертвым продуктом с момента его рождения. Он слишком мощный, чтобы его можно было использовать только для мигания светодиодами. Да, вы можете заставить его работать для вашего проекта, но будьте внимательны — не ждите большого сочувствия от Arduino., @Jan Hus


2 ответа


Лучший ответ:

0

Поэтому мой вопрос заключается в том, будут ли библиотеки и функции, используемые в этом коде, совместимыми с Due?

Библиотека I2Cdev предназначена для абстрагирования аппаратных функций I2C в более удобный интерфейс для каждого чипа. Поскольку Arduino Due имеет аппаратный TWI (который в основном является I2C), обычная библиотека Wire из Arduino IDE должна работать с ним.

Кроме того, может ли работа Due при напряжении 3,3 В по сравнению с 5 В Uno создавать проблемы при взаимодействии его с другими упомянутыми датчиками?

Вы не можете подать 5 В от вашего датчика на контакт 3,3 В на Arduino Due, так как это разрушит оборудование контактов. Я не знаю, как именно вы используете MPU6050, но это от Adafruit работает на 3,3 В. , что было бы идеально. Если ваша плата MPU6050 работает только с 5 В, возможно, вы можете сдвинуть уровень линий (но учтите, что для I2C потребуется двухсторонний сдвиг уровня, поскольку SDA используется как выход и вход).

Также эта статья может помочь вам, где они портируются MultiWii на Arduino Due.

,

0

Я работаю с платами ArduinoDue и ArduinoMega2560 и обнаружил, что программы, компилируемые с платой Mega2560, не обязательно компилируются с платой Due. Это связано с тем, что существует ограниченное количество доступных библиотек, совместимых с платой Due. Попробуйте скомпилировать (проверить) свой код с установленными параметрами Due, вам не нужно покупать его, чтобы попытаться проверить его.

,