Конвертировать 32-битную длину в 4 байта и обратно - CAN Bus

Поэтому после двух дней попыток я не могу прийти в себя.

Поэтому я использую шину CAN для передачи данных акселерометра и данных времени (функция micros(), если менее 71 минуты, выводит данные, которые могут иметь длину без знака), однако мне приходится отправлять данные в виде байтов. Мне удалось сдвинуть 16-битное целое число в 2 байта, но у меня возникли проблемы с экстраполяцией этого метода (добавится позже) на использование длинных чисел. У меня возникли проблемы с пониманием метода объединения, поскольку я новичок в создании собственных функций. Как бы вы, ребята, это сделали? Как можно проще, пожалуйста :)

, 👍3

Обсуждение

вы вычитаете максимальный байт 3 из общей суммы, и из этого вычитаете максимальный байт 2, чтобы получить байт 3. Байт 4 - это просто максимальный байт 4 минус число минус максимальный байт 3 +1., @dandavis


3 ответа


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

1

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

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

void loop() {
  long a=0x12345678, c=0;
  byte b[4];
  Serial.println("Original:");
  Serial.println(a,HEX);
  delay(1000);
  Serial.println("Individual bytes:");
  for (int i=0; i<4; i++)
  {
    b[i]=((a>>(i*8)) & 0xff); //извлекаем самый правый байт сдвинутой переменной
    Serial.println(b[i],HEX);
    delay(1000);
  }

  for (int i=0; i<4; i++)
  {
    c+=b[i]<<(i*8);
  }
  Serial.println("Reconstructed:");
  Serial.println(c,HEX);
  delay(1000);
}

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

,

Очень простой и понятный принцип, спасибо, однако в разделе «реконструировано» печатается только 5678?, @Ross Hanna

Вы скопировали фрагмент кода из моего ответа? Потому что, если я запущу код на своем nodemcu, я получу правильный ответ., @oh.dae.su

Да, я скопировал и вставил код, добавил void setup(); и Serial.begin(9600); чтобы запустить код на моем Arduino Nano, @Ross Hanna

по умолчанию на 8-битном AVR справа от знака равно по умолчанию целочисленной арифметике, которая имеет шестнадцатибитный знак. Поэтому я привожу массив к uint32_t в цикле. Не уверен, что это самый эффективный способ, но он работает! Спасибо за помощь, @Ross Hanna

Да, верно. Я не думал о контроллере Arduino, когда пробовал nodemcu., @oh.dae.su

Я пытаюсь добиться этого долго, но без радости Оригинал: 5294А Отдельные байты: 4А 29 5 0 Реконструировано: 294А, @MC9000

У кого-нибудь есть обновление этого ответа? Над реконструкцией должно работать (не вижу ничего плохого), но этого не происходит. Я что-то пропустил?, @MC9000


3

Тип union аналогичен struct, за исключением того, что каждый из членов элемента занимает одну и ту же память. Если вы определяете struct так, чтобы она имела 2 элемента — один 4-байтовый тип и один массив из 4 элементов однобайтового типа, то вы можете легко ссылаться на одни и те же данные целиком. -байтовый элемент или побайтовый, по вашему желанию.

union packed_long {
  long l;
  byte b[4];
};

Это тип, поэтому вам нужно объявить переменную этого типа:

packed_long mydata;

Теперь у вас есть два подэлемента mydata: l и b[].

Чтобы сохранить значение long, напишите в часть l:

mydata.l = myaccel.getY();

Чтобы получить доступ к каждому из 4 байтов:

byte1 = mydata.b[0];
byte2 = mydata.b[1];
byte3 = mydata.b[2];
byte4 = mydata.b[3];

На принимающей стороне вы принимаете каждый из 4 байтов:

mydata.b[0] = canbus.read();
mydata.b[1] = canbus.read();
mydata.b[2] = canbus.read();
mydata.b[4] = canbus.read();

Пока вы получаете байты в том же порядке, в котором вы их отправили, теперь вы можете получить доступ к значению long в mydata.l.

Фактический размер различных типов зависит от компилятора и архитектуры, поэтому вы также можете определить свое значение как переменную определенного размера, чтобы гарантировать, что вы всегда работаете с 32 битами: int32_t (32-битное целое число со знаком) или uint32_t (32-битное целое число без знака).

,

1

Способ отправки длинного сообщения — это замаскировать и отправить младший байт (стандарт CAN), а затем сдвинуть следующий в позицию младшего байта на 4 байта:

uint32_t data;

// Отправляем на CAN-шину LSB-сначала
// Этот метод является разрушительным; данные должны быть временными, если
// Arduino должна сохранять данные после их отправки.
for( uint8_t b = 4; b > 0; --b ){
   sendByte(data & 0xFF);  // отправляем текущий байт
   data >>= 8;             // сдвигаем этот байт
}

// Чтение из CAN-шины
for( uint8_t b = 4; b > 0; --b ){
   data >>= 8;                 // освобождаем место для байта
   data |= (readByte() << 8);  // читаем следующий старший байт
}
//
,

Итак, в стандарте CAN сначала стоит LSB. Поскольку вы знаете CAN, насколько я могу судить, они отправляют 8-байтовый пакет, являются ли только один или два из них данными? Итак, для 16 байт информации понадобится 8 пакетов?, @Ross Hanna

Ты поймал меня! Это все, что я знаю о шине CAN, и это было из [Википедии](https://en.wikipedia.org/wiki/CAN_bus). В Интернете есть много документации по шине CAN; Библиография этой статьи — хорошее начало., @JRobert

8 байт — это размер полезных данных стандартного can-frame. поэтому вам нужны два стандартных кадра для отправки 16 байт информации. более новый can-fd поддерживает полезную нагрузку данных размером 64 байта на кадр., @oh.dae.su