Arduino EthernetServer read() работает только тогда, когда Serial инициализирован и выводятся прочитанные символы.

У меня есть проект Arduino, в котором я читаю данные с веб-сервера.

У меня есть EthernetClient, который считывает данные символ за символом в функции обратного вызова.

Мой рабочий код выглядит так (только соответствующие части):

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

void loop() {
  char* processedData = processData(callback); // this is in a external lib
}

boolean callback(char* buffer, int& i) {
  ...

  if (Client.available()) {
    char c      = client.read();
    buffer[i++] = c;

    Serial.print(c);
  }

  ...
}

Это работает без проблем (чтение и обработка данных), но когда я удаляю Serial.begin(9600); и Serial.print(c);, оно перестает работать, и я не знаю, почему? Единственное, что изменилось, это то, что char c не печатается. В чем может быть проблема?


person tbraun89    schedule 19.12.2012    source источник
comment
Я не знаю API Arduino, инициализирует ли Serial.begin() периферийное устройство Ethernet или это только для последовательного монитора UART (на что намекает скорость передачи 9600)?   -  person Lundin    schedule 19.12.2012
comment
Я думаю, что это только для UART, после этого я вызываю Ethernet.begin(macAddress).   -  person tbraun89    schedule 20.12.2012
comment
Кстати, почему это помечено C? Это С++.   -  person Lundin    schedule 20.12.2012


Ответы (2)


Распространенная причина, по которой функции обратного вызова меняют свое поведение при изменении, казалось бы, несвязанного кода, — это ошибки, связанные с оптимизатором.

Многие встроенные компиляторы не понимают, что функция обратного вызова (или процедура обслуживания прерывания) когда-либо будет вызываться в программе. Они не видят явного вызова этой функции, а затем предполагают, что она никогда не вызывалась.

Когда компилятор сделал такое предположение, он оптимизирует переменные, которые изменяются функцией обратного вызова, потому что он не видит, что переменная изменяется программой между точкой инициализации и точкой доступа.

// Bad practice example:

int x; 

void main (void)
{
  x=5;
  ...

  if(x == 0) /* this whole if statement will get optimized away, 
                the compiler assumes that x has never been changed. */
  {
    do_stuff();
  }
}

void callback (void)
{
  x = 0;
}

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

Решение состоит в том, чтобы всегда объявлять все переменные файловой области ("глобальные"), совместно используемые main() и прерыванием/обратным вызовом/потоком, как volatile. Это делает невозможным для компилятора неправильные предположения оптимизатора.


(Обратите внимание, что ключевое слово volatile нельзя использовать для достижения синхронизации и оно не гарантирует каких-либо барьеров памяти. Этот ответ ни в малейшей степени не связан с такими проблемами!)

person Lundin    schedule 19.12.2012
comment
Вопросы оптимизации заслуживают внимания, но обратите внимание, что в этом случае нет переменных, совместно используемых обратным вызовом и другими частями программы, поэтому предлагаемые критерии для того, что объявлять volatile, неприменимы. objdump результата компиляции было бы полезно, хотя тенденция Arduino IDE создавать временную папку и записывать оттуда, не делая очевидным местоположение объекта, может немного разочаровать. - person Chris Stratton; 19.12.2012
comment
Извините, но нет глобальных переменных, общих с обратным вызовом, он должен просто добавить символ в буфер, увеличить целочисленное значение на количество добавленных символов и вернуть true (или false, если символы не были добавлены). Таким образом, он имеет только возвращаемое значение и значения, переданные по ссылке. - person tbraun89; 20.12.2012
comment
@ tbraun89 Если буфер используется совместно с main (), у вас все равно будет та же проблема, что описана здесь. А как насчет Ethernet, у него тоже есть обратные вызовы/прерывания? - person Lundin; 20.12.2012
comment
Нет, другого обратного вызова или прерывания нет, я проверю это с помощью буфера, когда завтра вернусь в офис. - person tbraun89; 20.12.2012

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

На что вы надеялись, что последовательный обратный вызов будет делать в отсутствие данных?

Предоставление дополнительной информации о Client и processData может помочь.

person AShelly    schedule 19.12.2012
comment
Значит, мне всегда нужно инициализировать последовательный драйвер, когда я хочу использовать библиотеку Ethernet? - person tbraun89; 19.12.2012
comment
Это действительно зависит от того, что делает ProcessData. Когда он должен вызвать обратный вызов? Кроме того, вызывается ли loop в цикле? - person AShelly; 19.12.2012
comment
loop работает все время, поэтому обратный вызов вызывается каждый раз, когда выполняется один цикл. Обратный вызов должен только записывать символы в буфер. Если я уберу Serial.println(c), в буфер тоже ничего не запишется. - person tbraun89; 19.12.2012
comment
как loop работает все время? Это вызывается внутри while(1) или как-то еще? А вы уверены, что callback вызывается каждый раз, когда вызывается processData? Условия не проверялись? - person AShelly; 19.12.2012
comment
То, что loop работает все время, определяется здесь: arduino.cc/en/Reference/loop. и обратный вызов вызывается как первый оператор в processData. Мое текущее решение состоит в том, чтобы иметь оператор Serial.print и в производственной версии, не требует больше времени для обработки, если последовательный адаптер не подключен, а на Mega 2560 больше 1k Flash не имеет значения. - person tbraun89; 19.12.2012