Ошибка возврата сверхбыстрой передачи UART

Код был основан на очень полезном примере Фахада Мирзы здесь Отправить структуру через серийный номер

Код Отправителя теперь стал

struct Gyro_data_structure
{
  char command_name[5];
  float gyro_X;
  float gyro_Y;
  float gyro_Z;
};

Gyro_data_structure Gyro_data;
int size_gyro = sizeof(struct Gyro_data_structure);    

void setup() {
  //Серийный.начало(9600); // открывает последовательный порт, устанавливает скорость передачи данных 9600 бит/с
  //Серийный.начало(115200); // открывает последовательный порт, устанавливает скорость передачи данных 9600 бит/с
  Serial.begin(250000);    
}   

void loop() {

  Gyro_data.command_name[0] = 'H';
  Gyro_data.command_name[2] = 'l';
  Gyro_data.gyro_X++;
  Gyro_data.gyro_Y=12;    

  Serial.write('p');
  Serial.write('L');
  Serial.write(Gyro_data.command_name, sizeof(Gyro_data.command_name));


  Serial.write('m');
  Serial.write('x');

  send(&Gyro_data);
  delay(100); //---------------------параметр играл в этом посте
  //Serial.flush();
}

void send (const Gyro_data_structure* table)
{
  Serial.write((const char*)table, size_gyro);  // 2 байта.
}

Код получателя

struct Gyro_data_structure
{
  char command_name[5];
  float gyro_X;
  float gyro_Y;
  float gyro_Z;
};

Gyro_data_structure  Gyro_data_received;
int size_gyro = sizeof(struct Gyro_data_structure);

void setup() {
  Serial.begin(9600);
  //Serial1.begin(9600);
  //Serial1.begin(115200);
  Serial1.begin(250000);
}


void loop() {

  //если(Serial1.доступно()>0)
  if(Serial1.available()>=size_gyro)//----------------работает очень хорошо//---------------- ---------
  {
      Serial.println(Serial1.available());

    //Serial1.findUntil('p','L');
    //Serial1.find('p');
    //Serial1.find('L');

  if ((Serial1.read() == 'p') && (Serial1.read() == 'L'))
  {
    Serial1.readBytes(Gyro_data_received.command_name, sizeof(Gyro_data_received.command_name));

    Serial.println("Received confirmation");
    Serial.println(Gyro_data_received.command_name[0]);
    Serial.println(Gyro_data_received.command_name[2]);


    if ((Serial1.read() == 'm') && (Serial1.read() == 'x')) {

      Serial.println("Gyro data received");
      receive2(&Gyro_data_received);

      Serial.println(Gyro_data_received.command_name[0]);
      Serial.println(Gyro_data_received.gyro_X);
      Serial.println(Gyro_data_received.gyro_Y);
    } else {
      Serial.println("Gyro data lost");
    }

  } else {

    Serial.println("No code confirmed");
    while(Serial1.peek()!='p'){
    Serial.print(Serial1.read()); //основная очистка ресивера
    //Serial1.flush();
    }


  }

  }
}


bool receive2(Gyro_data_structure* table)
{
  return (Serial1.readBytes((char*)table, sizeof(Gyro_data_structure)) == sizeof(Gyro_data_structure));
}

Что он делает, отправитель отправляет 5-байтовый массив символов и структуру получателю. Если получатель получил "код", т.е. char 'p' 'L' и 'm' 'x', он прочитал входящий 5-байтовый массив символов и структуру. Если код не подтвердился, он очищает входящие байты.

Однако я столкнулся со следующей проблемой.

Сначала обратите внимание на код получателя

  if(Serial1.available()>0)      
  if(Serial1.available()>=size_gyro)

условие, при котором получатель начинает обрабатывать последовательность read().

Однако при скорости (9600) я заметил, что если использовать

if(Serial1.available()>0)

очень часто он начинает читать, когда Serial1.available()=1, и возвращает неверные числа, которые были очищены

Serial.println("No code confirmed");
while(Serial1.peek()!='p'){
Serial.print(Serial1.read()); //основная очистка ресивера
//Serial1.flush();
}

Но если использовать

  if(Serial1.available()>=size_gyro)

в качестве условия для срабатывания read() это сработало очень хорошо. С другой стороны, если увеличить последовательную скорость, скажем, до 115200, это не имеет значения, так как они оба возвращают правильный результат. Не могли бы вы объяснить мне, почему это было проблемой, пожалуйста?

Во-вторых, обратите внимание на

  send(&Gyro_data);
  delay(100);

При скорости 100 или выше с нано- и мега-платой все работало нормально. Но если вы хотите увеличить скорость передачи, измените задержку (100) на задержку (20) в коде отправителя, код получателя вернет ошибку. Эта проблема не может быть вызвана изменением проводки или увеличением последовательной скорости. В частности, получатель не в состоянии

  Serial.println(Serial1.available()); 

обычно возвращают число от 62 до 63 при возникновении ошибки.

Не могли бы вы объяснить мне, откуда взялись эти два вопроса? Были ли они из-за того, что чип установлен на Arduino? В частности, как добиться ответа связи менее 20 мс?

, 👍0

Обсуждение

о каких двух вопросах вы говорите?, @jsotola

В мой ответ на ваш предыдущий вопрос я написал: «_Binary, однако, сложнее в обращении: [...] вам может потребоваться определить протокол своего рода, чтобы получить правильное кадрирование. [...] Обычно я рекомендую использовать ASCII, если только вам действительно не нужна дополнительная эффективность двоичного протокола». Вы видите, что я имею в виду сейчас?, @Edgar Bonet

Вы хоть понимаете, что означает [последовательная скорость](https://en.wikipedia.org/wiki/Бод)? Подумайте о том, что делает ваша программа и как быстро передаются биты и байты., @the busybee


1 ответ


Лучший ответ:

1

Во-первых, вы ошиблись в логике получателя: у вас есть тест на Serial1.available()>=size_gyro и, когда это верно, вы читаете size_gyro + 9 байт. Вы должны изменить тест, чтобы только начать чтение, когда у вас действительно есть size_gyro+9 байт.

Затем обратите внимание, что имя_команды передается дважды: сначала как часть 9-байтовая преамбула (между "pL" и "mx"), затем как часть переданный Gyro_data_structure. В этом нет смысла.

при скорости (9600) я заметил, что если использовать if(Serial1.available()>0) очень часто он начинает читать, когда Serial1.available()=1, и возвращает неправильные номера

Это ожидаемо: вы ждете, пока на входе станет доступен 1 байт. буфер, то вы пытаетесь прочитать 9-байтовую преамбулу. Один из ваших звонков to Serial1.read() обнаружит, что буфер пуст, и затем вернуть −1.

Но если использовать if(Serial1.available()>=size_gyro) в качестве условия чтобы вызвать read(), это сработало очень хорошо.

Другими словами, когда вы ждете, пока не станет доступно 17 байтов, прежде чем читая 9-байтовую преамбулу, вы правильно понимаете преамбулу. Является это удивительно?

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

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

если нужно увеличить скорость передачи, измените задержку(100) на delay(20) в коде отправителя, код получателя возвращает ошибку

Отправитель движется слишком быстро, а получатель не справляется с данными скорость.

Serial1.available() обычно возвращает число от 62 до 63, когда есть ошибка.

Это означает, что приемный буфер заполнен.

Обратите внимание, что приемник работал бы намного быстрее, если бы не печатал много. После получения правильной передачи будет выполнено Serial.println() куча сообщений, которые должны составлять не менее 64 байт. Передача эти байты со скоростью 9600 бит/с занимают более 66,5 мс.


ОБНОВЛЕНО: предлагаемое решение.

Ваша идея добавить «код» к структуре данных, чтобы определить, что правильная передача находится в правильном направлении. Это своего рода индикатор формата, широко известный как "магическое число" или "подпись". Не обеспечивает правильную передачу на 100% уверенность, так как подпись могла быть случайно представлена как часть сама структура данных. Но если байтов подписи маловероятно, чтобы случается случайно, у вас есть довольно высокая уверенность, что где бы вы ни найти подпись в потоке данных, это начало нового пакета. Вы можете увеличить эту уверенность, сделав подпись длиннее в стоимость более длительного времени передачи.

Теперь для простоты я бы не стал отправлять подпись двумя непересекающиеся части. Обычной практикой является отправка его как одного непрерывного набора байт в самом начале пакета. Например, я бы отправил ваши пакеты вот так:

// 5-байтовая подпись, включая завершающий '\0'.
const uint8_t Gyro_signature[] = "Gyro";

void Gyro_send(const Gyro_data_structure &gyro) {
    Serial1.write(Gyro_signature, sizeof Gyro_signature);
    Serial1.write((uint8_t *) &gyro, sizeof gyro);
}

Возможно, вы захотите изменить подпись на что-то менее вероятное случайно.

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

  1. пусть i = 0 будет количеством правильных байтов, полученных на данный момент
  2. прочитать входящий байт
  3. если этот входящий байт правильный, увеличить i
  4. иначе, если мы только что получили первый байт подписи, сбросить i = 1
  5. в противном случае сбросить i = 0
  6. если мы не получили полную подпись, перейдите к шагу 2

Это предполагает, что первый байт подписи больше нигде не встречается. внутри подписи. В противном случае шаги 4–5 будут более сложными.

Вот функция получателя, которая реализует этот алгоритм, а затем считывает структура данных:

void Gyro_receive(Gyro_data_structure &gyro) {

    // Читаем, пока не получим полную подпись.
    int discarded = 0;
    for (size_t i = 0; i < sizeof Gyro_signature; ) {
        if (Serial1.available()) {
            char c = Serial1.read();
            if (c == Gyro_signature[i]) {  // правильный байт
                i++;
            } else if (c == Gyro_signature[0]) {  // первый байт
                discarded += i;
                i = 1;
            } else {
                discarded += i+1;
                i = 0;
            }
        }
    }
    if (discarded) {
        Serial.print("Discarded ");
        Serial.print(discarded);
        Serial.println(" bytes.");
    }

    // Ждем данных.
    while ((size_t) Serial1.available() < sizeof gyro)
        /* wait */;

    // Читаем.
    Serial1.readBytes((char *) &gyro, sizeof gyro);
}

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

,