Конфликт между шилдом SD-карты и акселерометром

Я пытаюсь заставить SD-карту (SD-карту Adafruit microshield) и акселерометр MPU6050 работать одновременно. Когда я подключаю SD-карту к Arduino (без акселерометра), все в порядке; Когда подключаю акселерометр к Arduino (без SD-карты), тоже все ок. Коды акселерометра и SD-карты по отдельности работают нормально. Но когда я подключаю шилд к Arduino, а затем подключаю акселерометр и пытаюсь запустить комбинированный код, то это проблематично. Ниже я включил код, над которым сейчас работаю. Буду очень признателен, если вы поможете мне найти причину проблемы и способы ее решения.

#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include "RTClib.h"
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
//#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
// #include "Wire.h"
//#endif


// Простой регистратор данных для аналоговых выводов Arduino

// сколько миллисекунд между получением данных и их записью. 1000 мс — это раз в секунду
#define LOG_INTERVAL  1000 // фрезерует между записями (уменьшите, чтобы получить больше/быстрее данных)

// сколько миллисекунд перед постоянной записью зарегистрированных данных на диск
// устанавливаем значение LOG_INTERVAL для записи каждый раз (самое безопасное)
// установите значение 10*LOG_INTERVAL, чтобы записывать все данные каждые 10 операций чтения данных, вы можете потерять до
// последние 10 чтений, если питание потеряно, но оно потребляет меньше энергии и работает намного быстрее!
#define SYNC_INTERVAL 1000 // фрезерует между вызовами flash() — для записи данных на карту
uint32_t syncTime = 0; // время последней синхронизации()

#define ECHO_TO_SERIAL   1 // эхо-данные в последовательный порт
#define WAIT_TO_START    0 // Ожидание последовательного ввода в setup()

// цифровые контакты, которые подключаются к светодиодам
#define redLEDpin 4
#define greenLEDpin 3

// Аналоговые контакты, которые подключаются к датчикам
//(не уверен, какие контакты акселерометра являются аналоговыми, которые подключаются к датчику. Вставьте сюда!!)


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


RTC_DS1307 RTC; // определяем объект «Часы реального времени»

// для защиты регистрации данных мы используем цифровой вывод 10 для линии SD CS
const int chipSelect = HIGH;

// файл журнала
File logfile;

void error(char *str)
{
  Serial.print("error: ");
  Serial.println(str);

  // красный светодиод указывает на ошибку
  digitalWrite(redLEDpin, HIGH);

  while(1);
}

void setup(void)
{
  Serial.begin(9600);
  Serial.println();

  // используем светодиоды отладки
  pinMode(redLEDpin, OUTPUT);
  pinMode(greenLEDpin, OUTPUT);

#if WAIT_TO_START
  Serial.println("Type any character to start");
  while (!Serial.available());
#endif //WAIT_TO_START

  // инициализируем SD-карту
  Serial.print("Initializing SD card...");
  // убеждаемся, что для вывода выбора чипа по умолчанию установлено значение
  // вывод, даже если вы его не используете:
  pinMode(10, OUTPUT);

  // проверяем, присутствует ли карта и может ли она быть инициализирована:
  if (!SD.begin(chipSelect)) {
    error("Card failed, or not present");
  }
  Serial.println("card initialized.");

  // создаем новый файл
  char filename[] = "LOGGER00.CSV";
  for (uint8_t i = 0; i < 100; i++) {
    filename[6] = i/10 + '0';
    filename[7] = i%10 + '0';
    if (! SD.exists(filename)) {
      // открываем новый файл, только если он не существует
      logfile = SD.open(filename, FILE_WRITE); 
      break;  // выходим из цикла!
    }
  }

  if (! logfile) {
    error("couldnt create file");
  }

  Serial.print("Logging to: ");
  Serial.println(filename);

  // подключаемся к RTC
  Wire.begin();  
  if (!RTC.begin()) {
    logfile.println("RTC failed");
#if ECHO_TO_SERIAL
    Serial.println("RTC failed");
#endif  //ECHO_TO_SERIAL
  }


  logfile.println("Hello,World,!!!");    
#if ECHO_TO_SERIAL
  Serial.println("Hello,World,!!!");
#endif //ECHO_TO_SERIAL

  // Если вы хотите установить для параметра aref значение, отличное от 5 В
  analogReference(EXTERNAL);
}


// Управление/переменные состояния MPU
bool dmpReady = false;  // устанавливаем true, если инициализация DMP прошла успешно
uint8_t mpuIntStatus;   // содержит фактический байт состояния прерывания от MPU
uint8_t devStatus;      // возвращаем статус после каждой операции с устройством (0 = успех, !0 = ошибка)
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];         // [psi, theta, phi] Контейнер углов Эйлера
float ypr[3];           // [рыскание, тангаж, крен] контейнер рыскания/тангажа/крена и вектор силы тяжести

volatile bool mpuInterrupt = false;     // указывает, перешел ли вывод прерывания MPU в высокий уровень
void dmpDataReady() {
    mpuInterrupt = true;
}
 #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
        Wire.begin();
        Wire.setClock(400000); // Часы I2C 400 кГц. Прокомментируйте эту строку, если у вас возникли трудности с компиляцией.
    #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
        Fastwire::setup(400, true);
    #endif

    Serial.begin(9600);
    while (!Serial); // ждем перечисления Леонардо, остальные продолжают немедленно
 // инициализируем устройство
    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.print(F("Enabling interrupt detection (Arduino external interrupt "));
        Serial.print(digitalPinToInterrupt(INTERRUPT_PIN));
        Serial.println(F(")..."));
        attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);
        mpuIntStatus = mpu.getIntStatus();

        // устанавливаем флаг готовности DMP, чтобы основная функция цикла() знала, что его можно использовать
        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);
}


void loop(void){

 if (!dmpReady) return;

    // ждем прерывания MPU или доступности дополнительных пакетов
    while (!mpuInterrupt && fifoCount < packetSize) {
        if (mpuInterrupt && fifoCount < packetSize) {
          // пытаемся выйти из бесконечного цикла
          fifoCount = mpu.getFIFOCount();
        }  

  DateTime now;

  // задержка на желаемое время между чтениями
  delay((LOG_INTERVAL -1) - (millis() % LOG_INTERVAL));

  digitalWrite(greenLEDpin, HIGH);

  // записываем миллисекунды с момента запуска
  uint32_t m = millis();
  logfile.print(m);           // миллисекунды с момента запуска
  logfile.print(", ");    
#if ECHO_TO_SERIAL
  Serial.print(m);         // миллисекунды с момента запуска
  Serial.print(", ");  
#endif

  // получаем время
  now = RTC.now();
  // записываем время
  logfile.print(now.unixtime()); // секунды с 01.01.1970
  logfile.print(", ");
  logfile.print('"');
  logfile.print(now.year(), DEC);
  logfile.print("/");
  logfile.print(now.month(), DEC);
  logfile.print("/");
  logfile.print(now.day(), DEC);
  logfile.print(" ");
  logfile.print(now.hour(), DEC);
  logfile.print(":");
  logfile.print(now.minute(), DEC);
  logfile.print(":");
  logfile.print(now.second(), DEC);
  logfile.print('"');
#if ECHO_TO_SERIAL
  Serial.print(now.unixtime()); // секунды с 01.01.1970
  Serial.print(", ");
  Serial.print('"');
  Serial.print(now.year(), DEC);
  Serial.print("/");
  Serial.print(now.month(), DEC);
  Serial.print("/");
  Serial.print(now.day(), DEC);
  Serial.print(" ");
  Serial.print(now.hour(), DEC);
  Serial.print(":");
  Serial.print(now.minute(), DEC);
  Serial.print(":");
  Serial.print(now.second(), DEC);
  Serial.print('"');
#endif //ECHO_TO_SERIAL

  analogRead(photocellPin);
  delay(10); 
  int photocellReading = analogRead(photocellPin);  

  analogRead(tempPin); 
  delay(10);
  int tempReading = analogRead(tempPin);    


  logfile.print(", ");    
  logfile.print(photocellReading);
  logfile.print(", ");    
  logfile.print(temperatureF);
#if ECHO_TO_SERIAL
  Serial.print(", ");   
  Serial.print(photocellReading);
  Serial.print(", ");    
  Serial.print(temperatureF);
#endif //ECHO_TO_SERIAL

}


// сбрасываем флаг прерывания и получаем байт INT_STATUS
    mpuInterrupt = false;
    mpuIntStatus = mpu.getIntStatus();

    // получаем текущий счетчик FIFO
    fifoCount = mpu.getFIFOCount();

    // проверка на переполнение (это никогда не должно происходить, если только наш код не слишком неэффективен)
    if ((mpuIntStatus & _BV(MPU6050_INTERRUPT_FIFO_OFLOW_BIT)) || fifoCount >= 1024) {
        // сброс, чтобы мы могли продолжить работу без ошибок
        mpu.resetFIFO();
        fifoCount = mpu.getFIFOCount();
        Serial.println(F("FIFO overflow!"));

    // в противном случае проверяем прерывание готовности данных DMP (это должно происходить часто)
    } else if (mpuIntStatus & _BV(MPU6050_INTERRUPT_DMP_INT_BIT)) {
        // ожидание правильной доступной длины данных, ожидание должно быть ОЧЕНЬ коротким
        while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();

        // читаем пакет из FIFO
        mpu.getFIFOBytes(fifoBuffer, packetSize);

        // отслеживаем здесь счетчик FIFO, если есть > в наличии 1 пакет
        // (это позволяет нам сразу читать дальше, не дожидаясь прерывания)
        fifoCount -= packetSize;

        #ifdef OUTPUT_READABLE_QUATERNION
            // отображаем значения кватернионов в простой матричной форме: wxyz
            mpu.dmpGetQuaternion(&q, fifoBuffer);
            Serial.print("quat\t");
            Serial.print(q.w);
            Serial.print("\t");
            Serial.print(q.x);
            Serial.print("\t");
            Serial.print(q.y);
            Serial.print("\t");
            Serial.println(q.z);
        #endif

        #ifdef OUTPUT_READABLE_EULER
            // отображаем углы Эйлера в градусах
            mpu.dmpGetQuaternion(&q, fifoBuffer);
            mpu.dmpGetEuler(euler, &q);
            Serial.print("euler\t");
            Serial.print(euler[0] * 180/M_PI);
            Serial.print("\t");
            Serial.print(euler[1] * 180/M_PI);
            Serial.print("\t");
            Serial.println(euler[2] * 180/M_PI);
        #endif

        #ifdef OUTPUT_READABLE_YAWPITCHROLL
            // отображаем углы Эйлера в градусах
            mpu.dmpGetQuaternion(&q, fifoBuffer);
            mpu.dmpGetGravity(&gravity, &q);
            mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
            Serial.print("ypr\t");
            Serial.print(ypr[0] * 180/M_PI);
            Serial.print("\t");
            Serial.print(ypr[1] * 180/M_PI);
            Serial.print("\t");
            Serial.println(ypr[2] * 180/M_PI);
        #endif

        #ifdef OUTPUT_READABLE_REALACCEL
            // отображаем реальное ускорение, скорректированное для устранения гравитации
            mpu.dmpGetQuaternion(&q, fifoBuffer);
            mpu.dmpGetAccel(&aa, fifoBuffer);
            mpu.dmpGetGravity(&gravity, &q);
            mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
            Serial.print("areal\t");
            Serial.print(aaReal.x);
            Serial.print("\t");
            Serial.print(aaReal.y);
            Serial.print("\t");
            Serial.println(aaReal.z);
        #endif

        #ifdef OUTPUT_READABLE_WORLDACCEL
            // отображаем начальное ускорение мировой рамки, скорректированное для устранения гравитации
            // и повернут на основе известной ориентации кватерниона
            mpu.dmpGetQuaternion(&q, fifoBuffer);
            mpu.dmpGetAccel(&aa, fifoBuffer);
            mpu.dmpGetGravity(&gravity, &q);
            mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
            mpu.dmpGetLinearAccelInWorld(&aaWorld, &aaReal, &q);
            Serial.print("aworld\t");
            Serial.print(aaWorld.x);
            Serial.print("\t");
            Serial.print(aaWorld.y);
            Serial.print("\t");
            Serial.println(aaWorld.z);
        #endif


  logfile.println();
#if ECHO_TO_SERIAL
  Serial.println();
#endif // ECHO_TO_SERIAL

  digitalWrite(greenLEDpin, LOW);

  // Теперь записываем данные на диск! Не синхронизируйте слишком часто — требуется 2048 байт ввода-вывода на SD-карту.
  // что требует много энергии и времени
  if ((millis() - syncTime) < SYNC_INTERVAL) return;
  syncTime = millis();

  // мигаем светодиодом, показывая, что идет синхронизация данных с картой & обновление ФАТ!
  digitalWrite(redLEDpin, HIGH);
  logfile.flush();
  digitalWrite(redLEDpin, LOW);

}

, 👍0

Обсуждение

const intchipSelect = HIGH; HIGH равен 1. вы не можете использовать для этого контакт 1, @Juraj

вы не до конца описали, что происходит..... что будет, если подключить оба модуля и запустить два отдельных кода?, @jsotola

Оба устройства подключаются через шину SPI?, @sempaiscuba


1 ответ


2

Судя по предоставленной вами информации, вполне вероятно, что ваша проблема связана с тем, как ваша SD-карта и акселерометр подключаются через шину SPI.

Проблема в том, что многие устройства последовательного периферийного интерфейса (SPI), особенно более дешевые, неправильно переводят вывод MISO в высокое сопротивление, когда SS подключен к высокому уровню. «Информация»/руководство по Micro SD Card Breakout Плата (это устройство, которое, как я полагаю, вы имеете в виду, когда говорите SD-карта Adafruit microshield) имеет следующую схему:

schematic

Который, похоже, не включает буфер с тремя состояниями.

У меня была почти такая же ситуация с одним из моих проектов. В конце концов я решил эту проблему, поместив буфер с тремя состояниями (в этом случае я использовал 74HC125) 4-канальный буфер с тремя состояниями) в линии MISO для каждого из устройств SPI.


Эта страница Улучшенное проектирование шины SPI за 3 шага наконец-то помогла мне встать на правильный путь. Я думаю, что объяснения достаточно ясны (по крайней мере, по сравнению со многими другими сайтами, которые я нашел!).

,