Конфликт между шилдом 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);
}
@natalie, 👍0
Обсуждение1 ответ
Судя по предоставленной вами информации, вполне вероятно, что ваша проблема связана с тем, как ваша SD-карта и акселерометр подключаются через шину SPI.
Проблема в том, что многие устройства последовательного периферийного интерфейса (SPI), особенно более дешевые, неправильно переводят вывод MISO в высокое сопротивление, когда SS подключен к высокому уровню. «Информация»/руководство по Micro SD Card Breakout Плата (это устройство, которое, как я полагаю, вы имеете в виду, когда говорите SD-карта Adafruit microshield) имеет следующую схему:
Который, похоже, не включает буфер с тремя состояниями.
У меня была почти такая же ситуация с одним из моих проектов. В конце концов я решил эту проблему, поместив буфер с тремя состояниями (в этом случае я использовал 74HC125) 4-канальный буфер с тремя состояниями) в линии MISO для каждого из устройств SPI.
Эта страница Улучшенное проектирование шины SPI за 3 шага наконец-то помогла мне встать на правильный путь. Я думаю, что объяснения достаточно ясны (по крайней мере, по сравнению со многими другими сайтами, которые я нашел!).
- ЭБР. Можно ли создать несколько таблиц с помощью db.create()?
- Можно ли использовать Arduino для чтения и записи данных с помощью SD-карты?
- Мой Arduino Datalogging Shield не может прочитать SD-карту
- Как увеличить скорость записи на SD-карту в Ардуино
- SD-карта не инициализируется
- Что означают эти контакты? Куда мне их подключить?
- Понимание того, почему следует избегать «String» и альтернативных решений
- Arduino: как записать значение переменной в текстовый файл и изменить его на SD-карте?
const intchipSelect = HIGH;
HIGH равен 1. вы не можете использовать для этого контакт 1, @Jurajвы не до конца описали, что происходит..... что будет, если подключить оба модуля и запустить два отдельных кода?, @jsotola
Оба устройства подключаются через шину SPI?, @sempaiscuba