Arduino использует задержку в I2C ReceiveEvent

Я читал ранее, что вы не можете использовать delay() внутри I2C ReceiveEvent, потому что это ISR.

Я хочу сделать следующее: у меня есть сервопривод, подключенный к моему Arduino Uno, и я хочу управлять им с помощью сигнала I2C. Поскольку я хочу уменьшить скорость сервопривода, я ранее использовал цикл с задержкой внутри, который не работает, когда функция вызывается I2C.

Это код, который я использовал раньше для управления сервоприводом:

void setServo(bool status) {
    if (status) {
        for (int pos = 0; pos <= 180; pos += 5) {
            servo.write(pos);
            delay(50);
            Serial.println(pos);
        }
    } else {
        for (int pos = 180; pos >= 0; pos -= 5) {
            wasteFlapServo.write(pos);
            delay(50);
            Serial.println(pos);
        }
    }
    servoStatus = status;
}

Как я могу контролировать скорость сервопривода, даже если я использую I2C? Спасибо за помощь.

С уважением, Мориц

, 👍0

Обсуждение

Вы используете 2 сервопривода: «servo» и «wasteFlapServo»?, @DataFiddler

Обратите внимание, что Serial.println() - еще один запрет во время ISR. Любая последовательная связь довольно медленная., @Duncan C

@DuncanC Хотя проблема больше в том, что последовательные данные не отправляются. print() просто помещает данные в буфер. Фактическая передача осуществляется через ISR, которые помещают каждый байт в аппаратный регистр UART. Таким образом, все данные будут заполнять буфер, пока он не будет заполнен. И данные будут отправляться только при выходе из ISR. Я думаю, что в более новых версиях ядра print также не будет блокироваться при полном буфере, когда внутри ISR, хотя я не помню, где я это читал., @chrisl


1 ответ


2

Общий способ выполнения длинных действий, запускаемых ISR, заключается в простой установке флага внутри ISR, который затем проверяется внутри void loop().

volatile byte flag = 0;

void loop(){
    if(flag){
        // Делаем что-нибудь здесь
        flag = 0; // сброс флага
    }
}

void receiveEvent(int howMany){
    flag = 1; // устанавливаем флаг, если были получены корректные данные
}

Имейте в виду следующее:

  • Функция loop() должна работать быстро, чтобы реагировать быстро. Длительные задержки делают другие длительные вещи, которые влияют на отзывчивость кода. В зависимости от ваших планов относительно точного поведения использование delay() обычно не очень хорошо. Вам следует изучить пример BlinkWithoutDelay, который поставляется вместе с Arduino IDE. Этому стоит научиться довольно рано.

  • flag должен быть объявлен volatile, чтобы сообщить компилятору, что он может измениться в любое время. В противном случае переменная может быть оптимизирована.

  • flag здесь имеет тип byte. Важно, что этот флаг имеет только один байт, потому что чтение и запись одного байта не может быть прервана. Многобайтовые переменные читаются или записываются в несколько циклов COU, поэтому вполне возможно, что прерывание записывает части переменной, пока вы читаете ее в основном коде. Это приведет к искажению данных. Используя один байт, вы избегаете использования критических секций для сохранения чтения в основном коде. Для флага это намного проще.

,