Последовательная связь имеет очень "Странную проблему".

serial uart print

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

Как видно из первого скриншота, печать не закончена, потому что "===a=" застрял, вот так.

В коде он должен распечатать и остальные.

char* receiveCmd(Stream &_serial, char &cmd_ptr, int &l_ptr) {
  Stream* serial = &_serial;

  if (serial->available() && (uint8_t)serialRead(_serial) == CMD_START) {
    int buf_count = 0; //начать с данных
    cmd_ptr = serialRead(_serial); //cmd_byte

    char low_byte = serialRead(_serial);
    char high_byte = serialRead(_serial);

    uint16_t length = low_byte; //data_length
    length = length | (high_byte << 8);
    

    Serial.println("\n\n===a===");
    Serial.println(length);
    Serial.println("===b===");

    l_ptr = (int)length;

    char* _buf = (char*)malloc(length * sizeof(char));

    while (buf_count != length) {
      if (serial->available()) {
        _buf[buf_count] = serial->read();
        buf_count++;
      }
    }

    char checksum = serialRead(_serial); //checksum, не беспокойтесь об этом, пока
    char stop_byte = serialRead(_serial);

    if (stop_byte != CMD_EOF) {
      free(_buf);
      return NULL; //ошибка
    }

    return _buf;
  }

  return NULL;
}

Во всяком случае, проблема устраняется, когда я сначала распечатываю другие переменные, а затем длину var.

char* receiveCmd(Stream &_serial, char &cmd_ptr, int &l_ptr) {
  Stream* serial = &_serial;

  if (serial->available() && (uint8_t)serialRead(_serial) == CMD_START) {
    int buf_count = 0; //начать с данных
    cmd_ptr = serialRead(_serial); //cmd_byte

    char low_byte = serialRead(_serial);
    char high_byte = serialRead(_serial);

    uint16_t length = low_byte; //data_length
    length = length | (high_byte << 8);
    

    Serial.println("\n\n===a===");
    Serial.println(low_byte, HEX);
    Serial.println(high_byte, HEX);
    Serial.println(length);
    Serial.println("===b===");

    l_ptr = (int)length;

    char* _buf = (char*)malloc(length * sizeof(char));

    while (buf_count != length) {
      if (serial->available()) {
        _buf[buf_count] = serial->read();
        buf_count++;
      }
    }

    char checksum = serialRead(_serial); //checksum, не беспокойтесь об этом, пока
    char stop_byte = serialRead(_serial);

    if (stop_byte != CMD_EOF) {
      free(_buf);
      return NULL; //ошибка
    }

    return _buf;
  }

  return NULL;
}

Другой код точно такой же. Кто-нибудь знает, что может быть причиной этого? Я не могу распечатать эти вещи во время производства, потому что это только для отладки. Если я удалю эти печати, код также застрянет(опять же из-за переменной длины).

Для получения дополнительной информации последовательный сигнал генерируется из другой библиотеки Arduino и AltSerial. Структура такая.

void sendCmd(Stream &_serial, char cmd, char* payload, int length) {
  Stream* serial = &_serial;
  char buf[6 + length]; //start, data_length, data, checksum, stop

  buf[0] = CMD_START; //start_byte
  buf[1] = cmd; //cmd_byte
  buf[2] = length & 0xff; //data_length - младший байт
  buf[3] = (length >> 8) & 0xff; //data_length - старший байт
  buf[4 + length] = (char)calcCRC(payload); //контрольная
  buf[5 + length] = CMD_EOF; //stop_byte

  for (int i = 0; i < length; i++) //load buf
    buf[4 + i] = payload[i];

  for (int i = 0; i < sizeof(buf); i++)
    serial->print(buf[i]);
}

Я использовал Serial.print(altSerial.read(), HEX), чтобы проверить, правильно ли получен байт. Все они верны. Вот почему это сбивает меня с толку.QAQ

= = = = = ОБНОВЛЕННАЯ ИНФОРМАЦИЯ ==== =

Я сузил круг проблем. Похоже, что приведенный ниже код является основной причиной проблемы. Но решения для этого до сих пор нет.

uint16_t length = low_byte; //data_length
length = length | (high_byte << 8);

Спасибо.

, 👍1

Обсуждение

пожалуйста, никаких картинок с текстом ... добавьте фактический текст, @jsotola

@jsotola Ты имеешь в виду код? Хорошо, я его изменю., @Lu Chih Yuan

Вы уверены, что " _buf действительно выделяется? Вы всегда должны проверять, возвращает ли malloc()` NULL, прежде чем использовать выделенную память., @Majenko

@Majenko Большое вам спасибо! Это похоже на причину проблемы, но как проверить, что буфер был выделен?, @Lu Chih Yuan

if (_buf == NULL) { // сделайте что-нибудь, чтобы предупредить пользователя и прервать операцию }, @Majenko

@Majenko Он не выделяется из-за нехватки памяти? Я думаю, что у меня достаточно памяти для этого., @Lu Chih Yuan

Это *всегда* стоит проверить. Вы можете *думать*, что у вас достаточно, но вы не можете, так как другие вещи, кроме вас, используют память., @Majenko

Кроме того, что такое функция serialRead()?, @Majenko

@Majenko char serialRead(Поток и _serial) { Поток* serial = &_serial; задержка(1); return (uint8_t)serial->read(); }, @Lu Chih Yuan

А с какой скоростью передачи данных вы работаете?, @Majenko

Вы должны читать из serial только в том случае, если .available() сказал вам, что там действительно есть байт для чтения, или если вы проверите, что вы действительно прочитали байт после чтения (он вернет -1, если читать было нечего). Вы проверяете, есть ли хотя бы один доступный байт, а затем переходите к чтению множества байтов, которые могут быть там, а могут и не быть. Вы полностью обязаны причудам времени, чтобы сделать это правильно, а это плохо. Вы всегда должны ждать, пока байт действительно появится, прежде чем пытаться его прочитать., @Majenko

[Здесь](https://github.com/MajenkoLibraries/ICSC/blob/master/src/ICSC.cpp#L229) является примером того, как обрабатывать чтение пакетов неблокирующим образом., @Majenko

@Majenko Я так и сделал, есть функция цикла для многократной проверки наличия любого доступного байта для чтения. Только после этого будет вызвана функция receiveCmd. Скорость передачи данных в бодах составляет 115200., @Lu Chih Yuan

Да, но вы только один раз проверяете наличие одного байта в начале readCommand, а затем переходите к чтению нескольких байтов. Откуда вы знаете, что эти байты прибыли? А ты-нет., @Majenko

https://majenko.co.uk/blog/reading-serial-arduino, @Majenko

для чего нужен псевдоним указателя "Stream* serial", если у вас есть ссылка?, @Juraj


1 ответ


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

2

Почему это не работает? Вы уже получили ответ в комментариях:

  • Вы читаете больше байтов из последовательного потока, чем то, что вы знаете , доступно. Затем ваша переменная length может содержать мусор, что приводит к сбою malloc (), даже если у вас достаточно доступной памяти.

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

Вы можете заметить, что поведение вашей программы зависит от времени. Добавление дополнительных инструкций или задержек может заставить его работать. Это происходит просто потому, что вы даете своему сверстнику больше времени для отправки пакета данных. Конечно, полагаться на тайминги не очень хороший подход: вместо этого вы должны убедиться, что никогда не читаете() больше, чем то, что на самом деле доступно().

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

/*
 * Packet format:
 *   CMD_START cmd_byte length_L length_H [payload] checksum CMD_EOF
 */
char *receiveCmd(Stream &serial, char &cmd_ref, int &l_ref) {

    // Текущее состояние определяется тем, какие части пакета
    // мы уже получили.
    static enum {
        GOT_NONE, GOT_START, GOT_HEADER, GOT_PAYLOAD
    } state = GOT_NONE;

    // Переменные действительны только в состояниях GOT_HEADER и GOT_PAYLOAD.
    static char cmd_byte;
    static uint16_t length;
    static char *buffer;
    static size_t buffer_pos;

    switch (state) {
        case GOT_NONE:
            if (serial.available() < 1 || serial.read() != CMD_START)
                break;
            state = GOT_START;
            // fallthrough
        case GOT_START:
            if (serial.available() < 3)
                break;
            cmd_byte = serial.read();
            length = serial.read();
            length |= (uint16_t) serial.read() << 8;
            buffer = (char *) malloc(length);
            if (!buffer) {
                Serial.print(F("ERROR: Failed to allocate "));
                Serial.print(length);
                Serial.println(F(" bytes."));
                state = GOT_NONE;
                break;
            }
            buffer_pos = 0;
            state = GOT_HEADER;
            // fallthrough
        case GOT_HEADER:
            while (buffer_pos < length && serial.available())
                buffer[buffer_pos++] = serial.read();
            if (buffer_pos < length)
                break;
            state = GOT_PAYLOAD;
            // fallthrough
        case GOT_PAYLOAD:
            if (serial.available() < 2)
                break;
            serial.read();  // потреблять контрольную сумму
            if (serial.read() != CMD_EOF) {
                Serial.println(F("ERROR: Expected CMD_EOF."));
                free(buffer);
                state = GOT_NONE;
                break;
            }
            cmd_ref = cmd_byte;
            l_ref = length;
            state = GOT_NONE;
            return buffer;
    }
    return NULL;
}

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

,

Большое вам спасибо. Я нашел эту проблему. Просто не хватает памяти. поэтому он терпит неудачу. Тем не менее, я приму ваше предложение для функции "receiveCmd"., @Lu Chih Yuan