Простое последовательное сообщение непоследовательно (иногда перемешано)
У меня есть следующий код, он работает (в большинстве случаев), но он не на 100% надежен и время от времени будет отправлять двойное сообщение в одной строке или 2 последовательных буквенных символа:
const byte interruptPin0 = CONTROLLINO_IN0;
const byte interruptPin1 = CONTROLLINO_IN1;
int counter1 = 0;
int counter2 = 0;
unsigned long previousMillis = 0;
unsigned long interval = 2000;
int a = 60;
char buffer1[256];
char buffer2[256];
void setup()
{
pinMode(interruptPin0, INPUT_PULLUP);
pinMode(interruptPin1, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPin0), pin1Fired, RISING);
attachInterrupt(digitalPinToInterrupt(interruptPin1), pin2Fired, RISING);
Serial.begin(9600);
}
void loop()
{
unsigned long currentMillis = millis();
if (currentMillis - previousMillis > interval) {
previousMillis = currentMillis;
sprintf(buffer1,"A%d",counter1);
Serial.println(buffer1);
sprintf(buffer2,"D%d",counter2);
Serial.println(buffer2);
counter1 = 0;
counter2 = 0;
}
}
void pin1Fired()
{
counter1 = counter1 + 1;
}
void pin2Fired()
{
counter2 = counter2 + 1;
}
Результат:
Это позже вызывает проблему, когда мой код на другом конце последовательного соединения ожидает [БУКВА][ЧИСЛО].
Что я здесь пропустил? Я относительно новичок в C для Arduino, поэтому, возможно, я выбрал плохой подход?
Я пытался избежать отправки последовательных сообщений из прерывания, я читал, что это рискованно, поэтому я отслеживаю количество прерываний отдельно.
Также можно подтвердить соответствие скорости передачи данных.
ИЗМЕНИТЬ
Чтобы подтвердить сброс MCU, я напечатал в настройках - примечание: прерывания не срабатывают, все та же проблема:
@JᴀʏMᴇᴇ, 👍0
Обсуждение5 ответов
Лучший ответ:
Очень разочаровывает время, пытаясь диагностировать это.
Выражаем благодарность компании Controllino за их поддержку при выяснении того, была ли проблема связана с аппаратным обеспечением/кодом (это не было!).
Проблема оказалась в получателе этих сообщений. Производительность ПК, к которому был подключен Controllino, была очень плохой. Я заметил то, что вы видели на снимках экрана, которыми я делюсь каждый раз, когда открываю новое приложение или перетаскиваю окно.
Монитор последовательного порта в Arduino IDE блокировался на несколько секунд и поэтому пропускал последовательные сообщения (и должен был наверстать упущенное).
Я предполагаю, что прерывание происходит во время чтения или записи переменных счетчика.
Поскольку переменные больше, чем исходный размер регистров в ЦП, для работы с ними требуется несколько инструкций, и прямо в середине этой работы может произойти прерывание, которое изменяет значения, над которыми он работает. Это может привести к беспорядку (хотя я не ожидал такого беспорядка, TBH).
Все обращения к счетчикам, которые не относятся к прерываниям, следует заключать в критическую секцию. Это сегмент кода, где прерывания не разрешены. По очевидным причинам вы хотите, чтобы этот код был как можно короче.
Кроме того, в приведенном вами коде нет необходимости в ваших буферах и sprintfs. Вы можете просто использовать несколько Serial.prints для достижения того же результата с гораздо меньшим объемом кода/памяти.
Вот пример:
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis > interval) {
previousMillis = currentMillis;
// Копируем и сбрасываем значения счетчика в критической секции
noInterrupts();
int nowCounter1 = counter1;
counter1 = 0;
int nowCounter2 = counter2;
counter2 = 0;
interrupts();
Serial.print("A");
Serial.println(nowCounter1);
Serial.print("D");
Serial.println(nowCounter2);
}
}
Кроме того, ваши переменные-счетчики должны быть volatile
, поскольку доступ к ним осуществляется как из прерываний, так и из основного цикла:
volatile int counter1 = 0;
volatile int counter2 = 0;
Спасибо за ваш ответ! Это имеет смысл, я помню, что видел «noInterrupts» и должен был знать, как его использовать. Я скопировал пример цикла в вашем ответе, но это все еще происходит. Только что получил A0A0, @JᴀʏMᴇᴇ
Какую плату вы используете?, @Majenko
Контролино мини, @JᴀʏMᴇᴇ
Я не могу понять, какой USB-чип они там используют. Насколько нам известно, это может быть куча хлама., @Majenko
Это не ответ, а демонстрация того, как сделать ваш код более чистым.
Используйте критические секции, предложенные Маженко.
Вы можете переписать
sprintf(buffer1,"A%d",counter1);
Serial.println(buffer1);
sprintf(buffer2,"D%d",counter2);
Serial.println(buffer2);
как
sprintf(buffer, "A%d\r\nD%d", counter1, counter2);
Serial.println(buffer);
Кроме того, unsigned long int может содержать не более 10 символов (2^32), поэтому, включая "A", "D", дополнительную новую строку и \0, вместо этого будет достаточно одного буфера длиной около 30 символов. из 2 буферов по 256 байт (что занимает 25% SRAM Arduino UNO).
Также вы можете переписать
counter1 = counter1 + 1;
как
counter1++;
который увеличивает переменную на 1.
Кроме того, counter1
и counter2
никогда не должны быть отрицательными, поэтому лучше использовать тип unsigned
.
@EdgarBonet Спасибо за этот комментарий, я соответственно обновил свой ответ., @Michel Keijzers
В более новых версиях пакетов Arduino AVC Boards используется более новая версия компилятора avr-gcc, и иногда возникают странные проблемы.
Попробуйте пакет плат Arduino AVR 1.6.21. Он имеет проверенную версию avr-gcc.
В версиях 1.6.22 и 1.6.23 подтверждены проблемы с avr-gcc.
Начиная с версии 1.8.0 пользователи сообщают о странных проблемах. Некоторые здесь, некоторые на Stack Overflow, некоторые на форуме Arduino.
Пакет плат Controlino использует пакет Arduino AVR для сборки инструментов.
Это не решение; это скорее обходной путь.
Чтобы избежать повторяющихся букв, вы можете использовать код, чтобы убедиться, что каждый буквенный символ не совпадает с предыдущим, и если это так, удалите текущий символ перед печатью.
Затем, чтобы справиться с удвоением, если в строке четное количество символов, разделите строку пополам, а если обе половины равны, удалите одну из них из строки.
Это не совсем то, что вы хотели, но это скорее наихудший сценарий.
- Отправка последовательных данных в прерывании
- Серийное прерывание
- Есть ли способ использовать последовательный порт в качестве источника прерывания?
- Можно ли безопасно использовать последовательный порт в процедуре прерывания SPI?
- Является ли мой скетч «потокобезопасным»?
- Целесообразно ли использовать Serial.write в ISR, когда loop() обычно использует Serial.read?
- Как соединить автономный многоканальный adc с последовательным?
- прерывание с кнопки и ожидание, пока на последовательный порт 1 поступит сообщение
добавьте немного печати в setup(). что-то вроде
Serial.println("START");
, чтобы увидеть, не сбрасывается ли MCU по какой-то причине., @JurajСпасибо @Juraj - см. редактирование., @JᴀʏMᴇᴇ
какую версию пакета плат Arduino AVR вы используете? Более новые версии имеют более новую версию компилятора avr-gcc, и иногда возникают странные проблемы. Попробуйте пакет плат Arduino AVR 1.6.21. Он имеет проверенную версию avr-gcc., @Juraj
Необходимость использовать собственного менеджера платы Controllino @Juraj, @JᴀʏMᴇᴇ
Кажется, что с 1.6.21 компилируется/загружается нормально, я позволю этому запуститься и посмотрю, решило ли это проблему., @JᴀʏMᴇᴇ
Это длилось 2 минуты и не случалось ни разу! Если это сработало @Juraj, вы избавили меня от огромной головной боли!, @JᴀʏMᴇᴇ
Это случилось снова, к сожалению, очень расстраивает. Вы можете видеть по ссылке здесь, я должен использовать их диспетчер плат: https://www.controllino.biz/knowledge-base/board-library-setup-in-arduino-ide/, @JᴀʏMᴇᴇ
1. Попробуйте удалить все, что связано с обработкой прерывания (
pinMode()
,attachInterrupt()
,pin*Fired()
...), оставивcounter*
непостоянным. Проблема сохраняется? 2. Не могли бы вы поделиться скомпилированным файлом .elf, соответствующим минимальной программе, которая показывает проблему? Если это так, обновите исходный код в вопросе, чтобы он соответствовал этой минимальной программе., @Edgar Bonet