Искажение данных при чтении из Serial
- Войдите на сайт для отправки комментариев
Ср, 12/10/2016 - 14:27
Решил на MEGA2560 сделать чтение из Serial1 с использованием прерывания. Для контроля результат вывожу в Serial. Скетч работает, количество считываемых байт равно количеству отправленных, НО они искажены. Причем всегда одинаково. Для проверки делал без использования прерывания - все читается верно.
Вот пример посылки и результата отображения:
Используемый скетч:
// тест чтения порта Serial1 с помощью прерывания
// ------------------------------------------------------------------------------
#define INTERRUPT_PIN 19 // pin Rx для Serial1
const byte MAX_WaitingCount = 50; // макс кол-во циклов ожидания данных
//--------------------------------------------------------------------------------
void setup()
{
Serial.begin(9600);
delay(100);
Serial.println("BEGIN");
attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), f_Serial1_ISR, RISING); // определяем функцию по прерыванию
} // end of setup()
//--------------------------------------------------------------------------------
String Serial1_string; // результат чтения(строка)
byte Serail1_byte[255]; // результат чтения(строка)
int WaitingCount = 0; // время ожидания данных, мс
int Serial_amount = 0; // кол-во прочитанных байт
volatile boolean flag_Serial1 = false;
//--------------------------------------------------------------------------------
// обработка прерывания для Serial1
void f_Serial1_ISR()
{ if (!flag_Serial1)
flag_Serial1 = true;
}// end of f_Serial1_ISR()
//--------------------------------------------------------------------------------
// чтение данных из Serial1
void f_Serial1_Handler()
{
if (Serial_amount) // данные не обработаны, выход
return;
detachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN)); // отключаем прерывание
Serial1.begin(9600,SERIAL_8N1); // активируем Serial1
Serial1_string = "";
WaitingCount = 0;
/* while (!Serial1.available() and (WaitingCount< MAX_WaitingCount)) // ожидание данных в Serial1
{ WaitingCount++;
delay(1);
}
*/
delay(500); // ожидание в явном виде
if(Serial1.available()) // данные обнаружены
{
// delay(500); // ожидаем получения всего сообщения
while (Serial1.available()>0) // читаем данные и формируем строку
{ int ci = Serial1.read();
if (ci>=0)
{
char c = (char)ci;
Serail1_byte[Serial_amount] = (byte)ci;
Serial1_string += c;
Serial_amount++;
}
else // признак ошибки чтения
{ Serial_amount = 1;
Serail1_byte[0] = 0;
}
} // end of while
} // end of if(Serial1.available())
Serial1.end(); // закрываем Serial1
//delay(300);
attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), f_Serial1_ISR, RISING); // активируем прерывание
} // end of f_Serial1_Handler()
//--------------------------------------------------------------------------------
// основной цикл
void loop()
{
if (flag_Serial1)
f_Serial1_Handler(); // чтение данных из Serial1
if(Serial_amount)
{
Serial.print("WaitingCount : "); // тестовый вывод
Serial.print(WaitingCount);
Serial.print(" Serial_amount : ");
Serial.print(Serial_amount);
Serial.print(" string :");
Serial.println(Serial1_string);
for(byte k=0;k<Serial_amount;k++)
{ Serial.print(Serail1_byte[k],HEX);
Serial.print(" ");
}
Serial.println("");
Serial_amount = 0;
WaitingCount = 0;
flag_Serial1 = false; // снимаем флаг для ISR
delay(300);
}
}
//--------------------------------------------------------------------------------
Прошу подсказать, где ошибка?

Замечание по использованию
Внутри функции обработки прерывания не работает delay(), значения возвращаемые millis() не изменяются. Возможна потеря данный передаваемых по последовательному соединению (Serial data) в момент выполнения функциии обработки прерывания. Переменные, изменяемые в функции, должным быть объявлены как volatile.
Отсюда : http://arduino.ru/Reference/AttachInterrupt
Скриншот и скетч вставились криво, поэтому повторяю:
Скетч:
// тест чтения порта Serial1 с помощью прерывания // ------------------------------------------------------------------------------ #define INTERRUPT_PIN 19 // pin Rx для Serial1 const byte MAX_WaitingCount = 50; // макс кол-во циклов ожидания данных //-------------------------------------------------------------------------------- void setup() { Serial.begin(9600); delay(100); Serial.println("BEGIN"); attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), f_Serial1_ISR, RISING); // определяем функцию по прерыванию } // end of setup() //-------------------------------------------------------------------------------- String Serial1_string; // результат чтения(строка) byte Serail1_byte[255]; // результат чтения(строка) int WaitingCount = 0; // время ожидания данных, мс int Serial_amount = 0; // кол-во прочитанных байт volatile boolean flag_Serial1 = false; //-------------------------------------------------------------------------------- // обработка прерывания для Serial1 void f_Serial1_ISR() { if (!flag_Serial1) flag_Serial1 = true; }// end of f_Serial1_ISR() //-------------------------------------------------------------------------------- // чтение данных из Serial1 void f_Serial1_Handler() { if (Serial_amount) // данные не обработаны, выход return; detachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN)); // отключаем прерывание Serial1.begin(9600,SERIAL_8N1); // активируем Serial1 Serial1_string = ""; WaitingCount = 0; /* while (!Serial1.available() and (WaitingCount< MAX_WaitingCount)) // ожидание данных в Serial1 { WaitingCount++; delay(1); } */ delay(500); // ожидание в явном виде if(Serial1.available()) // данные обнаружены { // delay(500); // ожидаем получения всего сообщения while (Serial1.available()>0) // читаем данные и формируем строку { int ci = Serial1.read(); if (ci>=0) { char c = (char)ci; Serail1_byte[Serial_amount] = (byte)ci; Serial1_string += c; Serial_amount++; } else // признак ошибки чтения { Serial_amount = 1; Serail1_byte[0] = 0; } } // end of while } // end of if(Serial1.available()) Serial1.end(); // закрываем Serial1 //delay(300); attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), f_Serial1_ISR, RISING); // активируем прерывание } // end of f_Serial1_Handler() //-------------------------------------------------------------------------------- // основной цикл void loop() { if (flag_Serial1) f_Serial1_Handler(); // чтение данных из Serial1 if(Serial_amount) { Serial.print("WaitingCount : "); // тестовый вывод Serial.print(WaitingCount); Serial.print(" Serial_amount : "); Serial.print(Serial_amount); Serial.print(" string :"); Serial.println(Serial1_string); for(byte k=0;k<Serial_amount;k++) { Serial.print(Serail1_byte[k],HEX); Serial.print(" "); } Serial.println(""); Serial_amount = 0; WaitingCount = 0; flag_Serial1 = false; // снимаем флаг для ISR delay(300); } } //--------------------------------------------------------------------------------Так внутри обработчика delay() нет - все вынесено в основной цикл ((
Так внутри обработчика delay() нет - все вынесено в основной цикл ((
А, ну да (я не delay() имел в виду в цитате). Ещё мысль навскидку - может быть стоит попробовать Serial1.begin(9600,SERIAL_8N1); вынести в setup() ?
Так внутри обработчика delay() нет - все вынесено в основной цикл ((
А, ну да (я не delay() имел в виду в цитате). Ещё мысль навскидку - может быть стоит попробовать Serial1.begin(9600,SERIAL_8N1); вынести в setup() ?
Спасибо, помогло.
Подчищу скетч и выложу сюда.
Выкладываю работающий скетч.
Задержка 1 мсек между чтением байт сделана вынужденно для заполнения буфера для длинных пакетов(более 10 байт).
ИМХО: преимуществом такой реализации является разгрузка основного цикла, в котором постоянно проводится только проверка одного флага.
[code] //------------------------------------------------------------------------------- // тест чтения порта Serial1 с помощью прерывания // результат записывается и в строку, и в массив char[] - выбираем кому как удобнее // макс.кол-во считываемых байт ограничено значением MAX_READBYTES // ------------------------------------------------------------------------------ #define INTERRUPT_PIN 19 // pin Rx для Serial1 #define MAX_READBYTES 180 // максимальная кол-во считываемых байт, // остальные игнорируются #define SERIAL_BAUDRATE 9600 // скорость обмена с Serial #define SERIAL_1_BAUDRATE 9600 // скорость обмена с Serial_1 const byte MAX_WaitingCount = 50; // макс кол-во циклов ожидания данных = мсек //-------------------------------------------------------------------------------- void setup() { Serial.begin(SERIAL_BAUDRATE); // для контроля начала работы выводим "BEGIN" delay(100); Serial.println("BEGIN"); Serial1.begin(SERIAL_1_BAUDRATE); attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), f_Serial_1_ISR, RISING); // определяем функцию по прерыванию } // end of setup() //-------------------------------------------------------------------------------- String Serial1_string; // результат чтения(строка) byte Serail1_byte[MAX_READBYTES]; // результат чтения(байт) int WaitingCount = 0; // время ожидания данных, мс int Serial_amount = 0; // кол-во прочитанных байт volatile boolean flag_Serial_1 = false; // флаг чтения данных //-------------------------------------------------------------------------------- // обработка прерывания для Serial_1 void f_Serial_1_ISR() { if (!flag_Serial_1) flag_Serial_1 = true; }// end of f_Serial_1_ISR() //-------------------------------------------------------------------------------- // чтение данных из Serial_1 void f_Serial_1_Handler() { if (Serial_amount) // данные еще не обработаны, выход return; detachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN)); // отключаем прерывание Serial1_string = ""; WaitingCount = 0; while (!Serial1.available() and (WaitingCount< MAX_WaitingCount)) // ожидание данных в Serial1 { WaitingCount++; delay(1); } delay(10); // задержка в явном виде while (Serial1.available() &&(Serial_amount<MAX_READBYTES)) // читаем данные и формируем строку { int ci = Serial1.read(); delay(1); if (ci>=0) { char c = (char)ci; Serail1_byte[Serial_amount] = (byte)ci; Serial1_string += c; Serial_amount++; } else // признак ошибки чтения { Serial_amount = 1; Serail1_byte[0] = 0; } } // end of while attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN),f_Serial_1_ISR, RISING); // активируем прерывание } // end of f_Serial_1_Handler() //-------------------------------------------------------------------------------- // тестовый вывод результата чтения // здесь может быть функция обработки полученых данных void f_testoutput() { Serial.print("WaitingCount : "); Serial.print(WaitingCount); Serial.print(" Serial_amount : "); Serial.print(Serial_amount); Serial.print(" string :"); Serial.println(Serial1_string); for(byte k=0;k<Serial_amount;k++) { Serial.print(Serail1_byte[k],HEX); Serial.print(" "); } Serial.println(""); } // end of f_testoutput() //-------------------------------------------------------------------------------- // подготовка к чтению следующего пакета void f_ReadingEnd() { while (Serial1.available()>0) // очистка остатка данных в Serial_1 { int cii = Serial1.read(); } // !!!!! ОБЯЗАТЕЛЬНО СНЯТЬ ФЛАГ для чтение следующего пакета flag_Serial_1 = false; // снимаем флаг для ISR Serial_amount = 0; WaitingCount = 0; }// end of f_ReadingEnd() //-------------------------------------------------------------------------------- // основной цикл void loop() { if (flag_Serial_1) // установлен флаг прерывания { f_Serial_1_Handler(); // чтение данных из Serial1 if(Serial_amount) // имеются прочитанные данные { f_testoutput(); // тестовый вывод результата чтения или обработка полученных данных f_ReadingEnd(); // ОБЯЗАТЕЛЬНО !!! подготовка к чтению следующего пакета } } }// end of loop() //-------------------------------------------------------------------------------- [/code]