Совместимость кода с 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 создавать какие-либо проблемы при взаимодействии с другими упомянутыми датчиками?
@virmis_007, 👍0
Обсуждение2 ответа
Лучший ответ:
Поэтому мой вопрос заключается в том, будут ли библиотеки и функции, используемые в этом коде, совместимыми с 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.
Я работаю с платами ArduinoDue и ArduinoMega2560 и обнаружил, что программы, компилируемые с платой Mega2560, не обязательно компилируются с платой Due. Это связано с тем, что существует ограниченное количество доступных библиотек, совместимых с платой Due. Попробуйте скомпилировать (проверить) свой код с установленными параметрами Due, вам не нужно покупать его, чтобы попытаться проверить его.
- Arduino Uno: avrdude: stk500_recv(): программатор не отвечает
- Программирование Arduino на Паскале?
- Операторы If и while не работают
- Проблема повторения
- avrdude ser_open() can't set com-state
- Как отправить команду AT на sim800l с помощью SoftwareSerial
- Как очистить буфер FIFO на MPU6050?
- Как сгенерировать аппаратное прерывание в mpu6050 для пробуждения Arduino из режима SLEEP_MODE_PWR_DOWN?
Вероятно, вам следует потратить некоторое время на изучение *существующих* конструкций самолетов с открытым исходным кодом после ATmega. Чип от DUE, вероятно, можно заставить работать; во многих существующих проектах используются сопоставимые микроконтроллеры STM32, GigaDevices и Nuvoton ARM Cortex ... а также некоторые другие микроконтроллеры и особенно микроконтроллеры / радиочипы, которые в основном не задокументированы на западе., @Chris Stratton
Arduino Due является мертвым продуктом с момента его рождения. Он слишком мощный, чтобы его можно было использовать только для мигания светодиодами. Да, вы можете заставить его работать для вашего проекта, но будьте внимательны — не ждите большого сочувствия от Arduino., @Jan Hus