Считывание серийного номера с заголовком и конечным маркером

Я пытаюсь использовать дисплей Topway для ЖК-дисплея HMI с Arduino. Этот дисплей с использованием протокола RS232C в документах имеет "Структуру пакетов связи", подобную этой.

Поэтому мой запрос должен начинаться с AA и заканчиваться CC 33 C3 3C. Этот пример кода для чтения был взят на YouTube-канале topway.

Serial.write(0xaa); // packet head
Serial.write(0x3e); // VP_N16 read command
Serial.write(0x00); // VP_N16 address
Serial.write(0x08);
Serial.write(0x00);
Serial.write(0x00);
Serial.write(0xcc); // packet tail
Serial.write(0x33); // packet tail
Serial.write(0xc3); // packet tail
Serial.write(0x3c); // packet tail

3E-это код команды (для чтения), и это список всех команд. документы здесь

Дело в том, чтобы проанализировать реакцию смарт-ЖК-дисплея в оригинале. Они используют этот код: (при первом комментарии)

     while (!Serial.available()) {}  
     if (Serial.read()==0xaa) {                     // packet head
     packet_OK=1;
     while (!Serial.available()) {}
     if (Serial.read()==0x3e) {                   // packet command
      while (!Serial.available()) {}  
      temp_h=Serial.read();                      // read back value high byte
      while (!Serial.available()) {}  
      temp_l=Serial.read();                      // read back value low byte
      while (!Serial.available()) {}  
      if (!(Serial.read()==0xcc)) {packet_OK=0;} // packet tail
      while (!Serial.available()) {}  
      if (!(Serial.read()==0x33)) {packet_OK=0;} // packet tail
      while (!Serial.available()) {}  
      if (!(Serial.read()==0xc3)) {packet_OK=0;} // packet tail
      while (!Serial.available()) {}  
      if (!(Serial.read()==0x3c)) {packet_OK=0;} // packet tail
      }
      else
      {packet_OK=0;}
     }
     else
     {packet_OK=0;}

Это выглядит неприятно и громоздко, потому что я должен писать разные методы для каждой команды ЖК-дисплея. Есть ли способ прочитать все последовательные ответы в виде массива символов с начальным маркером "0xAA" и читать до конечного маркера "0xCC 0x33 0xC3 0x3C" и после этого проанализировать его?

Вы можете посмотреть и проверить этот код на YouTube

, 👍2

Обсуждение

Похоже, вы приложили некоторые усилия к этому сообщению, но в чем вопрос?, @timemage

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

@timemage забавно :), @mehmet


1 ответ


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

6

Короткий ответ на ваш вопрос: “Да, способ есть”. На самом деле, несколькими способами. Вы могли бы написать функцию блокировки, которая более или менее следует логике показанного вами примера, блокируя во время ожидания каждого нового байта. Или вы могли бы написать неблокирующую функцию, которая всегда возвращается немедленно и либо выдает вам полный пакет, либо сообщает, что до сих пор не был получен полный пакет.

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

// Return whether the payload of this command is of string type.
bool command_payload_is_string(uint8_t command);

// Return the expected packet length for the provided command,
// excluding header and tail.
int packet_length(uint8_t command);

// Attempt to read a packet, but do not block.
// Return a pointer to a static buffer holding the packet,
// or nullptr if no complete packet was received this time.
uint8_t *read_packet() {
    const uint8_t expected_header = 0xaa;
    const uint8_t expected_tail[4] = {0xcc, 0x33, 0xc3, 0x3c};

    // Return immediately unless we have a byte to process.
    if (Serial.available() == 0) return nullptr;
    uint8_t data = Serial.read();

    static uint8_t buffer[BUFFER_SIZE];
    static int bytes_received, bytes_expected;
    static enum {
        EXPECTING_HEADER, EXPECTING_COMMAND,
        EXPECTING_STRING, EXPECTING_PAYLOAD, EXPECTING_TAIL
    } state = EXPECTING_HEADER;
    switch (state) {
        case EXPECTING_HEADER:
            if (data = expected_header)
                state = EXPECTING_COMMAND;
            break;
        case EXPECTING_COMMAND:
            buffer[0] = data;  // command byte
            bytes_received = 1;
            if (command_payload_is_string(data)) {
                state = EXPECTING_STRING;
                // bytes_expected is not relevant in this case.
            } else {
                state = EXPECTING_PAYLOAD;
                bytes_expected = packet_length(data);
            }
            break;
        case EXPECTING_STRING:
            if (data == '\0') {  // end of string
                buffer[bytes_received++] = '\0';
                state = EXPECTING_TAIL;
                bytes_expected = 4;
                bytes_received = 0;
            } else if (bytes_received < BUFFER_SIZE - 1) {
                buffer[bytes_received++] = data;
            } else {
                // Do nothing. As the string is too long
                // to fit in the buffer, this byte is discarded.
            }
            break;
        case EXPECTING_PAYLOAD:
            buffer[bytes_received++] = data;
            if (bytes_received >= bytes_expected) {
                state = EXPECTING_TAIL;
                bytes_expected = 4;
                bytes_received = 0;
            }
            break;
        case EXPECTING_TAIL:
            // If we don't get the expected tail,
            // give up and wait for a new header.
            if (data != expected_tail[bytes_received++]) {
                state = EXPECTING_HEADER;
                break;
            }
            // If the tail is all right, return the buffer
            // and get ready for the next packet.
            if (bytes_received >= bytes_expected) {
                state = EXPECTING_HEADER;
                return buffer;
            }
    }
    return nullptr;
}

Это будет зависеть от вас, чтобы реализовать packet_length(). Смотрите комментарий jstola о том, почему это необходимо.

Вы бы использовали read_packet() следующим образом:

void loop() {
    uint8_t *packet = read_packet();
    if (packet)
        parse_and_handle(packet);

    // the rest of the program, never blocked by read_packet()...
}

Правка: Согласно вашему комментарию, некоторые команды имеют полезную нагрузку, представляющую собой строку неизвестной длины, но, по-видимому, завершеннуюНУЛЕМ. Я отредактировал код и добавил дополнительное состояние EXPECTING_STRING для обработки этого особого случая. Это зависит от вас, чтобы реализовать булеву функцию command_payload_is_string().

,

Эдгар, большое спасибо! за ваш мудрый ответ, прочитав 10 раз, я понял половину из них :). У меня есть последний вопрос. Почти вся длина командных ответов известна ранее. Но только строка чтения ранее не была известна. это влияет на пользователя. Вы можете думать так же, как при вводе с клавиатуры. на данный момент, что я должен делать, читая строку для packet_length()., @mehmet

@mehmet: Смотрите измененный ответ. Теперь packet_length() не будет вызываться, если полезная нагрузка имеет строковый тип., @Edgar Bonet