UART + ПРЕРЫВАНИЯ

qwedhinet
Offline
Зарегистрирован: 15.08.2012

Всем доброго дня!

Вопрос такой:

Есть две ардуины соединенные по UART, одна раз в 5 сек. передает 10 переменных, другая по событию кнопки. У меня это реализовано просто через Serial.print(p1); где р1 переменная (ее значения 1 или 0). 

Т.е. отправка выглядит так:

if (событие) {

Serial.print(p1);

Serial.print(p2);

Serial.print(p3);

........................

Serial.print(p9);

Serial.print(p10);

}

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

Помогите разобраться как работать с прерываниями и стабильно передавать данные по UART пожалуйста.

maksim
Offline
Зарегистрирован: 12.02.2012

qwedhinet пишет:

Передачу нужно делать с помощь прерываний. 

Зачем? И о каких прерываниях вы пишите?

То что у вас выше работает, а то что не работает вы не показали. Что вам нужно конкретно вы тоже не написали. 

Вставка программного кода в тему/комментарий

 

qwedhinet
Offline
Зарегистрирован: 15.08.2012

Прием переменных выглядит так:

 if (Serial.available() == 10 )    
  { 
    while(command < 10 )
    {
    commands[command] = Serial.read();
    command++;
    }
    command=0;
  }

Сейчас ардуина постоянно проверяет буфер UART. А если происходит сбой то  значения переменных сдвигаются.

Например, если в буфере по какой то причине остались 2 байта (2 значения) от старой передачи и туда записались 10 новых байт (значений) то мы считаем 2 старых + 8 новых и в итоге вся передача сдвинется на 2 значения. 

Как сделать так что бы данные из буфера забирались в тот момент когда они поступили, что бы забирать именно 10 последних записанных байт?

com
Offline
Зарегистрирован: 06.09.2013

Зачем ждать, пока в буфере накопится 10 байт? Забирайте сразу же, как только они поступили

if (Serial.available() > 0 )  

а если боитесь, что произойдет сбой - заведите специальный 11-тый символ, как конец последовательности. или наоборот, начало.

qwedhinet
Offline
Зарегистрирован: 15.08.2012

Ждать для того, что бы получив 10 байт сразу присвоить их 10ти переменным.

Я думал на тему спец символа, но 11 символ не решает проблему потери. Поясню, например, мы передаем ~1234567890 10 переменных где значение соответствует номеру переменной. И вдруг мы потеряли пятый байт, значение которого 5. Теперь на 5ом месте шестой байт значение которого 6, соответственно  пятой переменной присвоится значени 6, шестой 7 и т.д.

Получается передавать надо пакетом и если потерян хотя бы 1 байт пакета, то в помойку весь пакет, но в этом случае надо повторно запрашивать потеряный пакет =(

А что если сразу после отправки вызывать внешнее прерывание на получающей ардуне, которое сразу считает данные из буфера? Чем может быть плох такой способ?

 

maksim
Offline
Зарегистрирован: 12.02.2012

А с чего вдруг должен потеряться байт? Я вам могу и за платно написать 100% стабильную передачу данных, но как только вы засуните мой код в свой у вас опять начнутся потери. Так что не надо тут каких то кусков кода с вашими обьяснениями, просто покажите ВЕСЬ под приемника и ВЕСЬ код передатчика.

Dimsan
Offline
Зарегистрирован: 25.10.2013

Байт может потеряться по причине плохого соединения, временного отсутствия питания на одном конце и т.д.

И прерывания не решат эту проблему.

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

qwedhinet
Offline
Зарегистрирован: 15.08.2012

Вот это ближе к делу, Dimsan прав, ведь даже скачок напряжения может привести к потере байта.

Так из вышенаписанного получается, что невозможно реализовать стабильную передачу отправляя последовательно байты в UART?

Вот ВЕСЬ код как сказали.

Код одной

char commands[10];                                 
int command = 0;                             
unsigned long sec = 0; 
int switch31Pin = 31;
int ledPin = 13;
boolean lastButton = LOW;      //последний
boolean currentButton = LOW;   //текущий
boolean ledOn = false;
boolean ignor = false;
char c = 0;

void setup() 
{
  Serial.begin(9600); 
  Serial3.begin(9600); 
  pinMode(switch31Pin, INPUT);
  pinMode(ledPin, OUTPUT);
}
 
void loop()
{
  currentButton = debounce(lastButton);
  if (lastButton  != currentButton)//(lastButton == LOW && currentButton ==HIGH)
  {
    ledOn = !ledOn;
    ignor = true;
  }
  lastButton = currentButton;
  digitalWrite(ledPin, ledOn);
  
  if (sec == 110000)
  {
    if (ignor == true)
    {
      Serial3.print(ledOn);
      Serial3.print("2");
      Serial3.print("3");
      Serial3.print("4");
      Serial3.print("5");
      Serial3.print("6");
      Serial3.print("7");
      Serial3.print("8");
      Serial3.print("9");
      Serial3.print("0"); 
    }
    if (Serial3.available() > 1 )       //   Если в буфере 5 байт
    { 
      while(command < 10 )
      {
        commands[command] = Serial3.read();
        Serial.print(commands[command]);
        command++;
      }
      Serial.println();
      command=0;
      if ( ignor == false )
      {
        ExecuteCommands();
      }
    }
    ignor = false;
    sec=0; 
  }
  sec++;
}
  
void ExecuteCommands()
{
  if(commands[0] == '1' ) 
  {
    ledOn = true;
  }
  else if(commands[0] == '0') 
  {
    ledOn = false;
  }
}

boolean debounce(boolean last)
{
  boolean current = digitalRead(switch31Pin);  //+++
  if (last !=current)
  {
    delay(5);
    current = digitalRead(switch31Pin);
  }
  return current;
}

Код другой

#include <SPI.h>
#include <Ethernet.h>
byte mac[] = {  0xC8, 0x60, 0x00, 0xCB, 0x57, 0x43 };  // задаеи MAC
char serverName[] = "google.com";
EthernetClient client;
int command = 0;       // для перебора переменных
char commands[10];      // для хранени 5 значений
String id = String(1); // ID идентификатор
unsigned long sec = 0; // переменная таймера
boolean port = false;  

void setup() 
{
  Serial.begin(9600); 
  Ethernet.begin(mac); 
  delay(1000);         
  Serial.print("test");
}
void loop()
{
  if (Serial.available() == 10 )  
  { 
    while( command < 5 )         
    {
      commands[command] = Serial.read();
      command++;
    }
    command=0;   
    port = true; 
  }              

  if (sec == 110000)         
  {
      if (client.connect(serverName, 80))   
      {
        if (port == true)    
        {
          client.println("GET /index.php?id=" +id+ "&v1=" + commands[0] + " HTTP/1.0");   
          client.println();  
          port = false;
        }
        else                 
        {
          client.println("GET /index.php?id=" +id+ " HTTP/1.0");   
          client.println();  
        }
      }   
      delay(100);                      
      if (client.available() > 1)      
      {
          char c = client.read();
          if(c != '@')                 
          {
              while(c != '@')
              {
                  c = client.read();
              }   
          }                            
          for (commands[command] = client.read(); command < 5; command++)  
          {
              commands[command] = client.read();
              Serial.print(commands[command]);
          }
      }  
      client.stop();  
      sec=0;          
      command = 0;     
  }      
  sec++;              
}                     

 

qwedhinet
Offline
Зарегистрирован: 15.08.2012

Код одной, с 35 по 44 передача с 46 по 53 прием.

Код другой с 21 по 27 прием с 59 по 63 передача.

alex_r61
Offline
Зарегистрирован: 20.06.2012

В прерываниях всё прекрасно работает, за исключением обрыва линии или

потери питания. МК соединяются по схеме Master-Slave.

Master периодически, по таймеру, передаёт данные и опрашивает

Slave. В конце каждого пакета передаётся контрольная сумма,

если не совпадает то пакет не принимается во внимание и приёмный  буфер

очищается. Slave в ответ передаёт либо свой статус, либо данные.

У меня так работают Master и 6 Slave.

maksim
Offline
Зарегистрирован: 12.02.2012

В общем так и не ясно сколько же байт вы хотите передать ,толи 10 толи 5, по коду этого определить не получается.

Делаете так и будет вам стабильность

if (Serial.available() >= 10 )  
  {
    while( command < 10 )         
    {
      commands[command] = Serial.read();
      command++;
    }
......
}

......

 

maksim
Offline
Зарегистрирован: 12.02.2012

alex_r61 пишет:

МК соединяются по схеме Master-Slave.

Если не секрет это как?

qwedhinet
Offline
Зарегистрирован: 15.08.2012

alex_r61, у вас на аппаратных прерываниях это реализовано?

И видите, в чем дело, я еще не дорос до понимания "контрольная сумма"

Как Master опрашивает Slave?

Каим образом очищается буфер, я такой команды не нашел?

Можно посмотреть ваши коды, мне будет очень полезно!

qwedhinet
Offline
Зарегистрирован: 15.08.2012

maksim я выложил коды как вы просили =) что скажете?

alex_r61
Offline
Зарегистрирован: 20.06.2012

Я соединял через RS-485 трансиверы, а два можно напрямую.

maksim
Offline
Зарегистрирован: 12.02.2012

alex_r61 пишет:

Я соединял через RS-485 трансиверы, а два можно напрямую.

Понятно, сами не знаете как. Открою вам секрет UART это не есть RS-485 и не имеет разделение на мастер и слейв.

maksim
Offline
Зарегистрирован: 12.02.2012

qwedhinet пишет:

maksim я выложил коды как вы просили =) что скажете?

Что вам еще сказать? #10

alex_r61
Offline
Зарегистрирован: 20.06.2012

maksim пишет:

Понятно, сами не знаете как. Открою вам секрет UART это не есть RS-485. UART не имеет ни мастера ни слейва.

Rs-485  это  стандарт физического уровня, т.е. железо. А реализация Master-Slave

это програмный уровень, можете хоть ModBus прилепить. И держите свой секрет в

большом секрете.

maksim
Offline
Зарегистрирован: 12.02.2012

А, программно.... Я просто еще разок вас процитирую.

alex_r61 пишет:

МК соединяются по схеме Master-Slave.

alex_r61
Offline
Зарегистрирован: 20.06.2012

maksim пишет:

А программно.... Я просто еще разок вас процитирую.

alex_r61 пишет:

МК соединяются по схеме Master-Slave.

Я имел в виду именно программное подключение.

alex_r61
Offline
Зарегистрирован: 20.06.2012

qwedhinet  задайте в поисковике "сrс8 для arduino".

 

maksim
Offline
Зарегистрирован: 12.02.2012

Программное подключение по программной схеме программными проводами. 

В общем, ладно, разговор ни о чем. Нет такого подключения.

Контрольную сумму в вашем случае передаете одиннадцатым байтом.

byte crc = 0;
for(byte i = 0; i < 10; i++) crc ^= commands[i];
commands[10] = crc;

При приеме также ее считаете и сравниваете с тем же 11-м байтом

byte crc = 0;
for(byte i = 0; i < 10; i++) crc ^= commands[i];
if(commands[10] == crc) /*все хорошо, пакет дошел*/;

 

Dimsan
Offline
Зарегистрирован: 25.10.2013

Что-то вы тут много всего написали, только запутали человека. Он использует UART, нет тут ни master, ни slave.

Я предложил решение. CRC конечно хорошо, не спорю, но что будет если, например, 11-й байт (контрольная сумма) придёт 10-м? Приёмник будет долго ждать последний 11-й байт, по ка не дождётся начала следующих 11 байт.

alex_r61
Offline
Зарегистрирован: 20.06.2012

Dimsan пишет:

CRC конечно хорошо, не спорю, но что будет если, например, 11-й байт (контрольная сумма) придёт 10-м? Приёмник будет долго ждать последний 11-й байт, по ка не дождётся начала следующих 11 байт.

Ожидание нужно ограничить. Если, после начала приёма,  в течении определённого

времени не получено определённого количества байт выходить по TimeOut с флагом

ошибки.

Dimsan
Offline
Зарегистрирован: 25.10.2013

Правильно.

На этот случай есть функция Serial.readBytes(buffer, length) - http://arduino.cc/en/Serial/ReadBytes. Принимает массив байт с учётом таймаута. Если вдруг что-то потеряется, то можно смело кричать: "Плохи дела, давай ещё раз!"

maksim
Offline
Зарегистрирован: 12.02.2012

А что если, а что если....

Вы предложили увеличить размер данных в два раза.  А что если под одним и тем же номером прийдут разные значения? Не надо здесь ни конторольной суммы, ни номеров.  Есть проблемы с соединением - устраните проблему с соединением. Есть проблемы с питанием устраните проблему с питанием, так как это не шина и не сеть коллизии исключены, а значит данные просто так пропасть не могут.

Dimsan
Offline
Зарегистрирован: 25.10.2013

Не спорю.

А что скажете на счёт 24-го сообщения?

maksim
Offline
Зарегистрирован: 12.02.2012

У этой функции (метода) есть один минус - она блокирующая. Если так уж надо то можно обьявить переменную, в которой сохранять значение millis() как только пришел первый байт пакета и сбрасывать ее в 0 когда получили последний байт.

com
Offline
Зарегистрирован: 06.09.2013

qwedhinet,  а что за опасные условия, если не секрет, где работает эта неустойчивая конструкция - то провод оборвут, то питание пропадет, то байт сам по себе испарится? просто интересно.....