Присвоение указателей массивам — неверные результаты?
Я пытаюсь построить кадр передачи для библиотеки 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.
@Lithimlin, 👍0
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; //возвращаем указатель на выделенный массив, теперь все в порядке.
}
- Как преобразовать строку в шестнадцатеричный массив
- Ввод строки в массив символов
- Матричный дисплей с Arduino UNO (ПРОБЛЕМА)
- Почему я не могу получить размер массива указателей
- Отправка массива размером >255 байт на arduino
- Как преобразовать строку шестнадцатеричных чисел с разделителями в массив int
- Разбор массива объекта в конструкторе библиотеки
- Программа для умножения чисел на самом деле не будет их умножать