Искажение данных при чтении из Serial

mrPG
Offline
Зарегистрирован: 01.06.2016

Решил на 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);              
   }

}
//--------------------------------------------------------------------------------

Прошу подсказать, где ошибка?

 

 

Araris
Offline
Зарегистрирован: 09.11.2012

Замечание по использованию

Внутри функции обработки прерывания не работает delay(), значения возвращаемые millis() не изменяются. Возможна потеря данный передаваемых по последовательному соединению (Serial data) в момент выполнения функциии обработки прерывания. Переменные, изменяемые в функции, должным быть объявлены как volatile.

Отсюда : http://arduino.ru/Reference/AttachInterrupt

mrPG
Offline
Зарегистрирован: 01.06.2016

Скриншот и скетч вставились криво, поэтому повторяю:

Скетч:

// тест чтения порта 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);              
   }

}
//--------------------------------------------------------------------------------

 

 

 

 

mrPG
Offline
Зарегистрирован: 01.06.2016

Так внутри обработчика delay() нет - все вынесено в основной цикл ((

Araris
Offline
Зарегистрирован: 09.11.2012

mrPG пишет:

Так внутри обработчика delay() нет - все вынесено в основной цикл ((

А, ну да (я не delay() имел в виду в цитате). Ещё мысль навскидку - может быть стоит попробовать Serial1.begin(9600,SERIAL_8N1); вынести в setup() ?

mrPG
Offline
Зарегистрирован: 01.06.2016

Araris пишет:

mrPG пишет:

Так внутри обработчика delay() нет - все вынесено в основной цикл ((

А, ну да (я не delay() имел в виду в цитате). Ещё мысль навскидку - может быть стоит попробовать Serial1.begin(9600,SERIAL_8N1); вынести в setup() ?

Спасибо, помогло.

Подчищу скетч и выложу сюда.

mrPG
Offline
Зарегистрирован: 01.06.2016

Выкладываю работающий скетч.

Задержка 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]