Проблема прерывания библиотеки MPU6050 Arduino Jeff Rowberg
Я пытался получить данные из MPU6050, используя библиотеку Arduino, упомянутую здесь.
Следующий код-это отредактированная версия примера кода, приведенного в папке библиотеки, который, кажется, работает нормально.
#define printfloatx(Name,Variable,Spaces,Precision,EndTxt) { Serial.print(F(Name)); {char S[(Spaces + Precision + 3)];Serial.print(F(" ")); Serial.print(dtostrf((float)Variable,Spaces,Precision ,S));}Serial.print(F(EndTxt)); }//Имя,Переменная,Spaces,Precision,EndTxt
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps_V6_12.h"
#include "Wire.h"
MPU6050 accelgyro;
MPU6050 mpu;
I2Cdev a;
/*
IMU Нулевые переменные
*/
int16_t data_acc[3];
int16_t data_Gyro[3];
/*
IMU Переменные инициализации
*/
uint8_t devStatus; // возвращает состояние после каждой операции устройства (0 = успех, !0 = ошибка)
uint8_t mpuIntStatus; // содержит фактический байт состояния прерывания из MPU
bool dmpReady = false; // установить true, если инициализация DMP прошла успешно
uint16_t packetSize; // ожидаемый размер пакета DMP (по умолчанию 42 байта)
/*
Переменные нахождения угла ИДУ
*/
uint16_t fifoCount; // количество всех байтов, находящихся в данный момент в FIFO
uint8_t fifoBuffer[64]; // Буфер хранения FIFO
Quaternion q; // [w, x, y, z] кватернионный контейнер
VectorFloat gravity; // [x, y, z] gravity vector
float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll контейнер и вектор силы тяжести
void Initialize()
{
// инициализировать устройство
Serial.println("Initializing I2C devices...");
accelgyro.initialize();
// проверить соединение
Serial.println("Testing device connections...");
Serial.println(accelgyro.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed");
Serial.println("tuning Each Dot = 100 readings");
accelgyro.CalibrateAccel(10);
accelgyro.CalibrateGyro(10);
a.readWords(0x68, 0x06, 3, (uint16_t *)data_acc);
a.readWords(0x68, 0x13, 3, (uint16_t *)data_Gyro);
} // Инициализация
void setup() {
Wire.begin();
Serial.begin(115200);
Initialize();
Wire.setClock(400000);
while (!Serial);
Serial.println(F("Инициализация устройств I2C..."));
mpu.initialize();
Serial.println(F("Соединения испытательного устройства..."));
Serial.println(mpu.testConnection() ? F("соединение MPU6050 успешно") : F("соединение MPU6050 не удалось"));
Serial.println(F("\nSend any character to begin DMP programming and demo: "));
while (Serial.available() && Serial.read()); // empty buffer
while (!Serial.available()); // wait for data
while (Serial.available() && Serial.read()); // снова пустой буфер
// загрузка и настройка DMP
Serial.println(F("Инициализация DMP..."));
devStatus = mpu.dmpInitialize();
// поставьте здесь свои собственные смещения гироскопа, масштабированные для минимальной чувствительности
mpu.setXGyroOffset(data_Gyro[0]);
mpu.setYGyroOffset(data_Gyro[1]);
mpu.setZGyroOffset(data_Gyro[2]);
mpu.setXAccelOffset(data_acc[0]);
mpu.setYAccelOffset(data_acc[1]);
mpu.setZAccelOffset(data_acc[2]);
if (devStatus == 0) {
// Время калибровки: генерируйте смещения и калибруйте наш MPU6050
mpu.CalibrateAccel(6);
mpu.CalibrateGyro(6);
Serial.println();
mpu.PrintActiveOffsets();
// включите DMP, теперь, когда он готов
Serial.println(F("Включение DMP..."));
mpu.setDMPEnabled(true);
// включить обнаружение прерываний Arduino
Serial.print(F("Включение обнаружения прерываний (внешнее прерывание Arduino "));
mpuIntStatus = mpu.getIntStatus();
Serial.println(F("DMP готов! Жду первого прерывания..."));
dmpReady = true;
packetSize = mpu.dmpGetFIFOPacketSize();
} else {
Serial.print(F("DMP Initialization failed (code "));
Serial.print(devStatus);
Serial.println(F(")"));
}
Serial.println("-------------- done --------------");
} // настройка
void loop()
{
if (!dmpReady) return;
if (mpu.dmpGetCurrentFIFOPacket(fifoBuffer)) {
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.print(ypr[2] * 180 / M_PI);
Serial.println();
}
}
Затем я активировал PIN захвата ввода для использования с прерыванием, и код выглядит следующим образом
#define printfloatx(Name,Variable,Spaces,Precision,EndTxt) { Serial.print(F(Name)); {char S[(Spaces + Precision + 3)];Serial.print(F(" ")); Serial.print(dtostrf((float)Variable,Spaces,Precision ,S));}Serial.print(F(EndTxt)); }//Имя,Переменная,Spaces,Precision,EndTxt
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps_V6_12.h"
#include "Wire.h"
MPU6050 accelgyro;
volatile MPU6050 mpu;
I2Cdev a;
/*
IMU Zero variables
*/
int16_t data_acc[3];
int16_t data_Gyro[3];
/*
IMU initialization variables
*/
uint8_t devStatus; // возвращает состояние после каждой операции устройства (0 = успех, !0 = ошибка)
uint8_t mpuIntStatus; // содержит фактический байт состояния прерывания из MPU
bool dmpReady = false; // установить true, если инициализация DMP прошла успешно
uint16_t packetSize; // ожидаемый размер пакета DMP (по умолчанию 42 байта)
/*
IMU angle finding variables
*/
uint16_t fifoCount; // количество всех байтов, находящихся в данный момент в FIFO
uint8_t fifoBuffer[64]; // Буфер хранения FIFO
Quaternion q; // [w, x, y, z] кватернионный контейнер
VectorFloat gravity; // [x, y, z] gravity vector
volatile float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll контейнер и вектор силы тяжести
void Initialize()
{
// инициализировать устройство
Serial.println("Initializing I2C devices...");
accelgyro.initialize();
// проверить соединение
Serial.println("Testing device connections...");
Serial.println(accelgyro.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed");
Serial.println("tuning Each Dot = 100 readings");
accelgyro.CalibrateAccel(10);
accelgyro.CalibrateGyro(10);
a.readWords(0x68, 0x06, 3, (uint16_t *)data_acc);
a.readWords(0x68, 0x13, 3, (uint16_t *)data_Gyro);
} // Инициализация
void timer4_initialization() {
pinMode(49, INPUT);
TCCR4A = 0; TCCR4B = 0; TCCR4C = 0;
TCCR4B = B11000000;
TIFR4 = _BV(ICF4);
TIMSK4 = B00100000;
}
ISR(TIMER4_CAPT_vect) {
if (!dmpReady) return;
if (mpu.dmpGetCurrentFIFOPacket(fifoBuffer)) {
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
}
}
void setup() {
Wire.begin();
Serial.begin(115200);
Initialize();
Wire.setClock(400000);
while (!Serial);
Serial.println(F("Initializing I2C devices..."));
mpu.initialize();
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()); // empty buffer
while (!Serial.available()); // wait for data
while (Serial.available() && Serial.read()); // снова пустой буфер
// загрузка и настройка DMP
Serial.println(F("Initializing DMP..."));
devStatus = mpu.dmpInitialize();
// поставьте здесь свои собственные смещения гироскопа, масштабированные для минимальной чувствительности
mpu.setXGyroOffset(data_Gyro[0]);
mpu.setYGyroOffset(data_Gyro[1]);
mpu.setZGyroOffset(data_Gyro[2]);
mpu.setXAccelOffset(data_acc[0]);
mpu.setYAccelOffset(data_acc[1]);
mpu.setZAccelOffset(data_acc[2]);
if (devStatus == 0) {
// Время калибровки: генерируйте смещения и калибруйте наш MPU6050
mpu.CalibrateAccel(6);
mpu.CalibrateGyro(6);
Serial.println();
mpu.PrintActiveOffsets();
// включите DMP, теперь, когда он готов
Serial.println(F("Enabling DMP..."));
mpu.setDMPEnabled(true);
// включить обнаружение прерываний Arduino
Serial.print(F("Enabling interrupt detection (Arduino external interrupt "));
mpuIntStatus = mpu.getIntStatus();
Serial.println(F("DMP ready! Waiting for first interrupt..."));
dmpReady = true;
packetSize = mpu.dmpGetFIFOPacketSize();
} else {
Serial.print(F("DMP Initialization failed (code "));
Serial.print(devStatus);
Serial.println(F(")"));
}
Serial.println("-------------- done --------------");
delay(2000);
timer4_initialization();
} // настройка
void loop()
{
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.print(ypr[2] * 180 / M_PI);
Serial.println();
}
Здесь, когда я использую прерывание, я ничего не печатаю, кажется, что программа заморожена где-то в вызове функции interrupt ->.
В чем же заключается ошибка, которую я здесь совершаю?
Как выполнить измерение с MPU6050 с помощью процедур ISR.
@Lawliet, 👍3
Обсуждение2 ответа
Функции mpu.dmpGetCurrentFIFOPacket(fifoBuffer)
и т. Д. Используют I2C для получения данных. Интерфейс I2C на Uno и Mega (которым вы пометили свой вопрос) реализован аппаратно и нуждается в прерываниях для обработки байтов данных (извлечения принятого байта из регистра или записи данных в регистр для отправки).
По умолчанию прерывания отключены внутри ISR. Таким образом, упомянутые функции не могут выполняться корректно. Они ждут прерывания, которое никогда не произойдет, так как функции выполняются, а прерывания отключены.
Можно повторно активировать прерывания в ISR, но это то, что вы должны делать только в нескольких ситуациях и только тогда, когда вы точно знаете, что делаете.
Вместо этого вы должны просто установить переменную volatile flag внутри ISR. В основном коде (в loop()
) вы можете проверить наличие этого флага, выполнить код I2C, когда он установлен, и сбросить флаг после этого. И тогда вы также можете просто печатать, когда вместо каждой итерации цикла появляются новые данные. Так что примерно так:
volatile byte timer_flag = 0;
ISR(TIMER4_CAPT_vect) {
timer_flag = 1;
}
void loop(){
if(timer_flag){
if (!dmpReady) return;
if (mpu.dmpGetCurrentFIFOPacket(fifoBuffer)) {
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
// затем выведите значения здесь, так как мы получили новые данные из MPU
}
timer_flag = 0; // Сброс флага для следующего прерывания таймера
}
}
Я изменил ответ Крисла, чтобы показать, как скопировать данные ISR и сбросить флаг ISR атомарным способом.
volatile bool isr_timer_flag = false;
volatile uint32_t isr_counter = 0; // Добавлено для демонстрации атомарного копирования многобайтного типа данных.
ISR(TIMER4_CAPT_vect)
{
isr_timer_flag = true;
isr_counter++;
}
void loop()
{
//
// Получить копию данных и сбросить флаг атомарно, временно отключив прерывания.
//
// Отключите прерывания, сняв флаг глобального прерывания.
cli();
// Скопируйте данные.
bool timer_flag = isr_timer_flag;
uint32_t counter = isr_counter;
// Сбросить флаг ISR для следующего прерывания таймера.
// Сброс его здесь позволяет избежать снятия любого повторного флага во время следующих операторов if.
isr_timer_flag = false;
// Включить прерывания, установив флаг глобального прерывания.
sei();
// Теперь используйте локальную копию флага ISR.
// Флаг ISR может быть повторно помечен в любое время в течение оставшейся части цикла,
// but a fresh copy will be obtained on the next iteration of the loop.
if (timer_flag)
{
if (!dmpReady) return;
if (mpu.dmpGetCurrentFIFOPacket(fifoBuffer))
{
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
// затем выведите значения здесь, так как мы получили новые данные из MPU
}
}
}
- Точность синхронизации Arduino nano
- Проблема с использованием Arduino Mega Timer2 с прерыванием PinChange
- DS3231 с Arduino Nano для точной синхронизации
- Как очистить буфер FIFO на MPU6050?
- Как сгенерировать аппаратное прерывание в mpu6050 для пробуждения Arduino из режима SLEEP_MODE_PWR_DOWN?
- Arduino непрерывно считывает значение АЦП с помощью прерывания
- 4-битный счетчик вверх и вниз
- Включить и отключить отдельные прерывания
вы хотите сказать, что он даже не печатает " Инициализация устройств I2C...`?, @jsotola