Простое последовательное сообщение непоследовательно (иногда перемешано)

У меня есть следующий код, он работает (в большинстве случаев), но он не на 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, я напечатал в настройках - примечание: прерывания не срабатывают, все та же проблема:

, 👍0

Обсуждение

добавьте немного печати в 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


5 ответов


Лучший ответ:

0

Очень разочаровывает время, пытаясь диагностировать это.

Выражаем благодарность компании Controllino за их поддержку при выяснении того, была ли проблема связана с аппаратным обеспечением/кодом (это не было!).

Проблема оказалась в получателе этих сообщений. Производительность ПК, к которому был подключен Controllino, была очень плохой. Я заметил то, что вы видели на снимках экрана, которыми я делюсь каждый раз, когда открываю новое приложение или перетаскиваю окно.

Монитор последовательного порта в Arduino IDE блокировался на несколько секунд и поэтому пропускал последовательные сообщения (и должен был наверстать упущенное).

,

4

Я предполагаю, что прерывание происходит во время чтения или записи переменных счетчика.

Поскольку переменные больше, чем исходный размер регистров в ЦП, для работы с ними требуется несколько инструкций, и прямо в середине этой работы может произойти прерывание, которое изменяет значения, над которыми он работает. Это может привести к беспорядку (хотя я не ожидал такого беспорядка, 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


0

Это не ответ, а демонстрация того, как сделать ваш код более чистым.

Используйте критические секции, предложенные Маженко.

Вы можете переписать

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


0

В более новых версиях пакетов 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 для сборки инструментов.

,

0

Это не решение; это скорее обходной путь.

Чтобы избежать повторяющихся букв, вы можете использовать код, чтобы убедиться, что каждый буквенный символ не совпадает с предыдущим, и если это так, удалите текущий символ перед печатью.

Затем, чтобы справиться с удвоением, если в строке четное количество символов, разделите строку пополам, а если обе половины равны, удалите одну из них из строки.

Это не совсем то, что вы хотели, но это скорее наихудший сценарий.

,