Присвоение указателей массивам — неверные результаты?

Я пытаюсь построить кадр передачи для библиотеки Workshop для Arduino Uno. У меня есть кадр байта внутри массива, я передаю его другой функции, которая создает кадр бита, а затем хочу передать этот кадр бита.

Когда я пытаюсь распечатать массивы с помощью последовательного монитора, я получаю правильные результаты для всего массива байтов и правильные результаты для битового массива до определенной точки. Однако, поскольку мой вывод (который я проверяю с помощью логического анализатора) также имел больше неправильных значений при попытке распечатать значения, я подозреваю, что это может быть связано с проблемами синхронизации моей передачи и Serial.print.

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

Мой массив байтов выглядит следующим образом:

10100101  // Преамбула
10000000  // Первая строка моего изображения
00100000
00010000
01000000
00000000
00000000
00000000
00000000  // Последняя (8-я) строка моего изображения
11110000  // Контрольная сумма

Эти значения также представлены в битовом массиве (до назначения тому, что в моем коде называется _frame, а также после этого). Однако, когда я считываю значения из логического анализатора, контрольная сумма передается как 11111111 вместо 11110000.

Вот мой код в файле .cpp:

Transmitter::Transmitter()
{
  [...]
  _frame = new uint8_t[90];
  frameCheck = _frame;
  [...]
}

[...]

int Transmitter::prepFrame(LEDBitmap image) {
  if(_busy) return -1; //нет Данные не могут быть отправлены прямо сейчас, так как передатчик уже занят
  if(!_pin) return -2;
  /** construct the frame that is to be transmitted
    *
    * frame structure is
    * 1 byte preamble (0xA5) | 8 byte payload | checksum
    *
    * the image will be transmitted in rows
    * with one row being one byte
    *  1  2  3  4  5  6  7  8   <-- 1st byte
    *  9 10 11 12 13 14 15 16   <-- 2nd byte
    * 17 18 19 20 21 22 23 24   <-- 3rd byte
    * ...                           ...
    */
  uint8_t size = 10;
  uint8_t frame[size];
  uint8_t checksum = 0x00;

    for(uint8_t h = 0; h < image.getHeight(); h++) {
      uint8_t value = image.getBitmap()[h];
      checksum = (checksum + value) % 255;
      frame[h+1] = value;
    }

    for(uint8_t h = image.getHeight(); h < 8; h++) {
      frame[h+1] = 0x00;
    }

  // построение каркаса
  frame[0] = PREAMBLE;
  frame[size-1] = checksum;


//Байтовый кадр "frame" на данный момент правильный

  _frame = buildBitFrame(frame, size);

//Фрейм, созданный этой функцией (см. ниже), а также битовый фрейм "_frame" имеют правильную контрольную сумму

  frameCheck = _frame;  // <-- Это checkFrame, который я печатаю в своем .ino, но который дает мне ложные результаты.

  // перевести передатчик в режим занятости, чтобы режим ожидания больше не работал
  // будет передан, но вместо этого будет отправлен кадр.
  _busy = true;
  _pos = 0;

  return 1; //подготовка прошла успешно
}

uint8_t* Transmitter::buildBitFrame(uint8_t* frame, uint16_t size) {
  _frameSize = size*8;
  uint8_t bitFrame[_frameSize];
  int pos = 0;
    for (size_t byte = 0; byte < size; byte++) {
      for (size_t bit = 0; bit < 8; bit++) {
        bitFrame[pos] = (frame[byte] >> (7 - bit)) & 0x01;
        pos++;
      }
    }

  return bitFrame; // <-- Этот кадр также правильный.
}


void Transmitter::transmitBit() {
  if(_active) {
    // Подготовка
    // изменить состояние только после полной передачи бита Манчестера.
    if(!_manHalf) {
      if(_busy) {
        // если в данный момент идет передача
        // читаем следующий бит из кадра
        _state = _frame[_pos];
        if(_pos >= _frameSize) {
          _busy = false;
          _state = 0;
          _pos = 0;
        }
        _pos++;
      } else { // в противном случае передаем шаблон ожидания
        _state = 0;
      }
    }
    // Передача инфекции
    if (!_manHalf) {
      digitalWrite(_pin, !_state);
    } else {
      digitalWrite(_pin, _state);
    }
    _manHalf = !_manHalf;
  } else {
    digitalWrite(_pin, HIGH);
  }
}

[...]

Соответствующие строки в заголовочном файле следующие:

[...]

class Transmitter
{
  public:
    Transmitter();

  public: //методы
    int sendData(LEDBitmap image);
    void transmitBit();
    [...]
    uint8_t* frameCheck;

  private: //участники
    uint8_t _state;
    uint8_t* _frame;
    [...]

    int prepFrame(LEDBitmap image);
    uint8_t* buildBitFrame(uint8_t* frame, uint16_t size);
    [...]

};

[...]

Функция transmitBit() вызывается каждую 1 мс через ISR.

И последнее, но не менее важное: вот мой скетч для Arduino:

[...]

Transmitter transmitter;

[...]

int smallData[] = {1, 0, 0, 0,
                   0, 0, 1, 0,
                   0, 0, 0, 1,
                   0, 1, 0, 0};

LEDBitmap small(4, 4, smallData);

void setup() {
  Serial.begin(2000000);
  transmitter.setPin(4);
  transmitter.start();
  delay(200);
  pinMode(5, OUTPUT);
  digitalWrite(5, HIGH);
  transmitter.sendData(small);
  delay(1000);
  for(int a = 0; a < 88; a++) {
    Serial.println(transmitter.frameCheck[a]);  
  }
}

В настоящее время цикл пуст.

Теперь перейдем к странным результатам, которые я получаю при печати checkFrame (которые я уже получал, пытаясь распечатать полные массивы внутри моего cpp). Вот что он напечатал (уже сгруппировано в байты):

1 0 1 0 0 1 0 1
1 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0
0 0 0 1 0 0 0 0
0 1 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0        // До сих пор все хорошо.
0 0 0 0 0 2 149 0      // На логическом анализаторе эта строка читается как 0x00, как и должно быть
221 5 15 0 2 149 0 1   // На логическом анализаторе эта строка читается как 0xff вместо
1 222 0 98 2 149 8 218 // Эта строка не имеет значения, так как это всего лишь буфер, который я добавил, чтобы проверить, заключается ли проблема в моем массиве, в котором недостаточно места

Теперь перейдем к вопросам!

Есть ли у вас идеи, что могло вызвать это? Я неправильно назначил массив? Может ли это быть просто проблемой с синхронизацией Serial.print и моим передатчиком, срабатывающим каждую 1 мс? Но почему тогда мой логический анализатор говорит, что последняя строка - 0xff? Есть ли у вас идеи, как это исправить?

Я был бы очень рад, если бы вы ответили как можно быстрее. Заранее спасибо!

ПРАВКА: После увеличения начального значения a в моем скетче до 64 я обнаружил, что 8-й байт по-прежнему равен 00000000, как и говорит логический анализатор. Однако контрольная сумма все еще была беспорядочной. Это также не сильно улучшилось после увеличения ее до 72. Тогда это было 1 1 2 149 8 218 5 15. Эти последние несколько значений также не изменились, когда я снова увеличил a до 74.

, 👍0


1 ответ


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

1
uint8_t* Transmitter::buildBitFrame(uint8_t* frame, uint16_t size) {
  _frameSize = size*8;
  uint8_t bitFrame[_frameSize];
  int pos = 0;
    for (size_t byte = 0; byte < size; byte++) {
      for (size_t bit = 0; bit < 8; bit++) {
        bitFrame[pos] = (frame[byte] >> (7 - bit)) & 0x01;
        pos++;
      }
    }

  return bitFrame; // <-- Этот кадр также правильный.
}

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

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

См. https://www.fayewilliams.com/2015/06/30/a-challenge-discussion-returning-pointers-to-local-variables/

В подобных случаях (особенно при постоянной максимальной длине кадра) вам следует переписать управление памятью, чтобы использовать статически выделенные буферы в вашем классе.

Например:

uint8_t* _frame;

к

#define SOME_MAX_SIZE 128
uint8_t _frame[SOME_MAX_SIZE];

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

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

Если вы действительно хотите продолжать это делать, вы также можете выделить новый кадр необходимой длины и обязательно удалить его после использования!

uint8_t* Transmitter::buildBitFrame(uint8_t* frame, uint16_t size) {
  _frameSize = size*8;
  uint8_t* bitFrame = new uint8_t[_frameSize];
  int pos = 0;
    for (size_t byte = 0; byte < size; byte++) {
      for (size_t bit = 0; bit < 8; bit++) {
        bitFrame[pos] = (frame[byte] >> (7 - bit)) & 0x01;
        pos++;
      }
    }

  return bitFrame; //возвращаем указатель на выделенный массив, теперь все в порядке.
}
,