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? Спасибо за помощь.
С уважением, Мориц
@puchm, 👍0
Обсуждение1 ответ
Общий способ выполнения длинных действий, запускаемых 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, поэтому вполне возможно, что прерывание записывает части переменной, пока вы читаете ее в основном коде. Это приведет к искажению данных. Используя один байт, вы избегаете использования критических секций для сохранения чтения в основном коде. Для флага это намного проще.
- Как прервать функцию цикла и перезапустить ее?
- Какой правильный способ запроса устройства I2C из процедуры обслуживания прерывания?
- Проблема с тахометром с использованием AttachInterrupt и сервопривода
- Как управлять несколькими серводвигателями одновременно с помощью протопотока с помощью платы ШИМ I2C PCA9685?
- Проблема стабильности кода прерываний, связанного с датчиком расхода
- проблемы с кодированием
- Почему сопротивление между выводом ввода-вывода и землей падает, когда Arduino не питается
- Определение ISR вызывает цикл загрузки?
Вы используете 2 сервопривода: «servo» и «wasteFlapServo»?, @DataFiddler
Обратите внимание, что
Serial.println()
- еще один запрет во время ISR. Любая последовательная связь довольно медленная., @Duncan C@DuncanC Хотя проблема больше в том, что последовательные данные не отправляются.
print()
просто помещает данные в буфер. Фактическая передача осуществляется через ISR, которые помещают каждый байт в аппаратный регистр UART. Таким образом, все данные будут заполнять буфер, пока он не будет заполнен. И данные будут отправляться только при выходе из ISR. Я думаю, что в более новых версиях ядраprint
также не будет блокироваться при полном буфере, когда внутри ISR, хотя я не помню, где я это читал., @chrisl