Прерывание Arduino для последовательных данных на цифровом выводе, которое слишком велико для последовательного буфера.

Я использую Arduino Nano в сочетании с GPS-модулем NEO 6 м. Модуль GPS каждую секунду отправляет данные, состоящие из нескольких сотен символов. Поскольку я общаюсь со своим ПК через встроенные последовательные контакты RX и TX Arduino, я не могу использовать их для датчика, не имея возможности отображать на своем ПК что-либо, отправленное с Arduino. Я использую библиотеку SoftwareSerial, чтобы пометить два цифровых контакта как новые RX и TX для связи с датчиком GPS.

Проблема в том, что мне приходится активно прослушивать вывод RX в момент поступления данных, потому что в противном случае данные будут потеряны, поскольку их слишком много, чтобы полностью поместить их в последовательный буфер. Но с другой стороны: я не хочу активно ждать, пока цифровой пин получит данные, чтобы успеть их прочитать. Лучше всего было бы обрабатывать прерывание каждый раз, когда цифровой вывод RX начинает получать данные. Но я читал, что SoftwareSerial не совместим с такого рода прерываниями, поскольку у него есть встроенное прерывание каждый раз при поступлении новых данных для сохранения их в буфере.

Вот что я пробовал:

#include <SoftwareSerial.h>

// Последовательное соединение с модулем GPS
SoftwareSerial ss(4, 3);
String serialString;

void setup() {
  Serial.begin(9600);
  ss.begin(9600);

  //Прерывает, когда распознается начало передачи на RX-контакте
  attachInterrupt(digitalPinToInterrupt(4), readGpsSerialData, CHANGE);
}

void loop() {
  //обработка данных, которая занимает слишком много времени, чтобы достаточно часто проверять, передаются ли данные в данный момент
  Serial.println("In Loop");
  delay(1000);
}

//Читаем все входящие данные после начала передачи
void readGpsSerialData() {
  String serialString = "";
  while (ss.available() > 0) {
    char serialChar = ss.read();
    serialString += serialChar;
  }
  lastGpsString = serialString;
  Serial.print("Data from Interrupt: ");Serial.println(lastGpsString);
}

Я думаю, проблема в том, что мое прерывание активируется не только один раз, когда начинается передача, но каждый раз, когда изменяется цифровое значение RX (то есть много раз при передаче). Значение RDY данных было бы неплохо для этого триггера прерывания, но у модуля GPS его нет.

, 👍0

Обсуждение

Я предполагаю, что задержка в цикле означает какой-то другой сложный код, выполнение которого занимает много времени? Я бы попытался перейти к неблокирующему стилю кодирования, чтобы вы могли проверять последовательные данные с более высокой частотой. Не зная, что это за сложный код, мы не можем ничего посоветовать., @chrisl

Или вы можете увеличить размер буфера, отредактировав библиотеку SoftwareSerial. Хотя я бы сказал, что это не очень хорошее решение, поскольку оно делает ваш код менее переносимым (вам придется использовать эту модифицированную библиотеку везде, где вы хотите скомпилировать код)., @chrisl

Действительно ли запуск кода цикла занимает больше 1 секунды? Возможно, это следует оптимизировать, и тогда это не будет проблемой., @Delta_G

Вы можете использовать аппаратный последовательный порт как для чтения с GPS, так и для записи в ПК. Однако вы не сможете читать с ПК., @Edgar Bonet

Спасибо, timemage — я заблокировал редактирование. Исправлено здесь: Следуя предложению @EdgarBonet, вы можете поменять роли Serial и SoftwareSerial. Тогда у вас будет один канал, предназначенный для ПК, а другой, более быстрый, для GPS. Вам все равно придется сделать свой код неблокирующим, чтобы предотвратить пропуск символов в любом канале, и вам захочется обслуживать каждый канал чаще (только для сбора входных данных, а не обязательно для их обработки) по той же причине., @JRobert

@EdgarBonet Интересный аспект, но не приведет ли это к «прохождению» данных с контакта Arduino RX на порт USB, практически полностью минуя Arduino?, @PMF

@PMF: Нет, ПК не видит данные, поступающие на RX Arduino. Он видит только то, что выходит из TX Arduino, то есть то, что вы Serial.print()., @Edgar Bonet


3 ответа


2

GPS — очень медленная связь, скорость передачи данных составляет 9600 бит/с, а данные доступны только один раз в секунду. Любой микроконтроллер, работающий намного медленнее, чем Arduino Uno, работающий на частоте 16 МГц, должен справиться с этим с небольшим буфером приема. Если и есть проблема, то она связана с реализацией пользовательского кода.

Вы не указали, какую плату Arduino вы используете. Реализация функции attachInterrupt() в Arduino имеет довольно ограниченное количество цифровых контактов, к которым можно прикрепить прерывание. См. документацию. Даже если он может обрабатывать прерывание на выводе 4, CHANGE означает, что он будет срабатывать при каждом изменении бита, что на самом деле не нужно.

Во-вторых, есть несколько правил, которые можно и нельзя делать. для настройки функции обратного вызова прерывания все сводится к тому, чтобы «быть кратким и простым», иметь цикл while.. и последовательную печать в обратном вызове прерывания - это одна из тех вещей, которых следует избегать, поэтому я предлагаю прочитать Прерывания Ника Гаммона.

Существует неправильное понимание фразы «активно слушать». из RX. Serial.available() — это функция, которая внутренне ведет себя как обратный вызов onEvent(), поэтому она неблокируется, просто выполняя проверку с помощью if( Serial.available(), он будет блокироваться, только если поместить его в цикл while. Кроме того, Serial.available() меняется с 0 на 1 только тогда, когда получен полный байт.

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

#include <SoftwareSerial.h>


// Последовательное соединение с модулем GPS
SoftwareSerial ss(4, 3);

void setup() {
  Serial.begin(9600);   // изменяем скорость передачи данных на 115200 для повышения производительности
  ss.begin(9600);
}

void loop() {
  String serialString = "";

  // этот блок кода выполняется только тогда, когда символ полностью получен
  if (ss.available() > 0) {
    char serialChar = ss.read();
    serialString += serialChar;
    // печатаем строку только при получении `\n` (т.е. полную строку GPS)
    if (serialChar == '\n') {
      Serial.println(serialString);
    }
  }

  // здесь достаточно времени, чтобы справиться с остальной частью связи с ПК
}
,

0

Здесь есть два аспекта.

Во-первых, непрерывный поток данных NMEA не так важен. Когда вы будете готовы к чтению, вы можете отменить любой ожидающий ввод, т. е. очистить буфер и начать чтение со следующего $ и далее.

Вы также можете ограничить количество отображаемых команд NMEA (для этого есть команды), чтобы меньше информации отправлялось в последовательный порт.

Второй аспект заключается в том, что большинство модулей GPS отправляют данные со скоростью 9600 бод, что довольно медленно, и каждую секунду (для этого нужен вывод PPS). Итак, вы читаете данные, сохраняете строки в буфере и анализируете буфер. Промыть и повторить. Даже Arduino Uno должно подойти. И, как я уже сказал, если данных слишком много, отбросьте их и начните читать заново.

,

0

Чтобы избежать переполнения буфера, я бы рекомендовал увеличить скорость связи на стороне ПК. Вы можете легко установить Serial.begin(115200); или даже выше, как предполагает комментарий в вашем коде. ПК и Arduino прекрасно с этим справляются. Это гарантирует, что вы сможете отправлять все входные данные с GPS-приемника намного быстрее, чем поступают новые данные, поэтому вы ничего не потеряете. Просто убедитесь, что вы используете одно и то же значение на Arduino и ПК.

,