Конвертировать 32-битную длину в 4 байта и обратно - CAN Bus
Поэтому после двух дней попыток я не могу прийти в себя.
Поэтому я использую шину CAN для передачи данных акселерометра и данных времени (функция micros(), если менее 71 минуты, выводит данные, которые могут иметь длину без знака), однако мне приходится отправлять данные в виде байтов. Мне удалось сдвинуть 16-битное целое число в 2 байта, но у меня возникли проблемы с экстраполяцией этого метода (добавится позже) на использование длинных чисел. У меня возникли проблемы с пониманием метода объединения, поскольку я новичок в создании собственных функций. Как бы вы, ребята, это сделали? Как можно проще, пожалуйста :)
@Ross Hanna, 👍3
Обсуждение3 ответа
Лучший ответ:
Вы можете добиться того, чего хотите, используя битовое смещение и побитовые операторы и.
Вот пример, позволяющий извлечь отдельные байты вашей переменной 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
Тип 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-битное целое число без знака).
Способ отправки длинного сообщения — это замаскировать и отправить младший байт (стандарт 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
- Как использовать SPI на Arduino?
- Как решить проблему «avrdude: stk500_recv(): programmer is not responding»?
- Как создать несколько запущенных потоков?
- Как подключиться к Arduino с помощью WiFi?
- avrdude ser_open() can't set com-state
- Как узнать частоту дискретизации?
- Что такое Serial.begin(9600)?
- Я закирпичил свой Arduino Uno? Проблемы с загрузкой скетчей на плату
вы вычитаете максимальный байт 3 из общей суммы, и из этого вычитаете максимальный байт 2, чтобы получить байт 3. Байт 4 - это просто максимальный байт 4 минус число минус максимальный байт 3 +1., @dandavis