преобразование unsigned long > byte (4 штуки) и обратно

ustas
Offline
Зарегистрирован: 12.03.2012

Появилась задачка - надо unsigned long сохранить в EEPROM.

Естественно, потом надо будет читать из EEPROM и собирать данные назад в unsigned long.

Подскажите, как сделать? 

правильно ли я понимаю, что собрать можно так: 

unsigned long value = (data[3] << 24) +(data[2] << 16) +(data[1] << 8) +data[0];

а как разобрать?

ustas
Offline
Зарегистрирован: 12.03.2012

Пока не получается.

Поискал по форуму, нашел похожую задачу (там int разбирали на байты). "Разбор" получился красивый, а вот мое предположение по "сборке" - не сработало.

unsigned long val = 1587632040;
unsigned long value;

void setup() {
  Serial.begin(9600);
  Serial.print("val ");
  Serial.println(val, DEC);
  byte *x = (byte *)&val;
  for (int i=0; i<4; i++) {
    Serial.println(x[i], DEC);
  }
  unsigned long value = (x[3] << 24) +(x[2] << 16) +(x[1] << 8) +x[0];
  Serial.print("val ");
  Serial.println(value, DEC);
}

void loop() {
}

Где ошибка?

step962
Offline
Зарегистрирован: 23.05.2011

На вашем месте я сначала бы изменил формат вывода:

unsigned long val = 1587632040;
unsigned long value;

void setup() {
  Serial.begin(9600);
  Serial.print("val ");
  Serial.print(val, DEC);
  Serial.print("  ");
  Serial.println(val, HEX);
  byte *x = (byte *)&val;
  for (int i=0; i<4; i++) {
    Serial.print(x[i], DEC);
    Serial.print("  ");
    Serial.println(x[i], HEX);
  }
  unsigned long value = (x[3] << 24) +(x[2] << 16) +(x[1] << 8) +x[0];
  Serial.print("val ");
  Serial.print(value, DEC);
  Serial.print("  ");
  Serial.println(value, HEX);
}

После чего мне сразу бы бросилось в глаза, что преобразование выполняется в общем-то правильно, но только для двух младших байтов:

val 1587632040  5EA157A8

168  A8

87  57

161  A1

94  5E

val 22440  57A8

После этого мне не оставалось бы ничего иного, как вспомнить, что обработка данных в микроконтроллерах AVR заточена под переменные размером в слово, т.е. два байта, т.е. (для математических действий) тип int. А, стало быть, компилятору необходимо явно указать, что работать будем не со словами а с двойными словами (тип unsigned long). А для этого необходимо выполнить кастинг (по-русски "приведение типа") участвующих в выражении переменных. Вот так (строка 16 в моем варианте):

unsigned long value = ((unsigned long)x[3] << 24) +((unsigned long)x[2] << 16) +(x[1] << 8) +x[0];

После этого я прогнал бы скетч снова и получил вот такой вывод:

val 1587632040  5EA157A8

168  A8

87  57

161  A1

94  5E

val 1587632040  5EA157A8

"Почему в двух правых слагаемых нет этого чертового кастинга?" спросите вы. Да потому что там мы оперируем информацией в пределах слова, стало быть, явное приведение типов не является необходимым. А делать лишнюю работу нам естественно лень...

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

Можно еще воспользоваться такой интересной штукой, как Union

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

Тогда к переменной этого типа можно будт обращаться целиком через l или к каждому байту через b

mytype var;
var.l=123000L;
byte StarshijBait=var.b[3];

 

ustas
Offline
Зарегистрирован: 12.03.2012

Спасибо, оба варианта завелись. Второй вариант особенно понравился - красиво получилось!

union IRcommand
{
  unsigned long l;
  byte b[4];
} val;

void setup(){
  val.l=1587632040;
  Serial.begin(9600);
  Serial.print("val ");
  Serial.print(val.l, DEC);
  Serial.print("  ");
  Serial.println(val.l, HEX);
  byte *x = (byte *)&val;
  for (int i=0; i<4; i++) {
    Serial.print(val.b[i], DEC);
    Serial.print("  ");
    Serial.println(val.b[i], HEX);
  }
}

void loop(){
}

 

gidon
Offline
Зарегистрирован: 05.08.2013

Время доброе.  Будьте добры, если не трудно разеснить выделинные действия?
unsigned long value = ((unsigned long)x[3] &lt;&lt; 24) +((unsigned long)x[2] &lt;&lt; 16) +(x[1] &lt;&lt; 8) +x[0];

 Зарание спасибо

step962
Offline
Зарегистрирован: 23.05.2011

Выделенные действия - результат выкрутасов HTML-редакторов и копипасты. В детстве все эти "&lt;&lt;" имели вид "<<". В своем посте - поправил.

 

Или вы имели в виду иное?

romeo_b
Offline
Зарегистрирован: 08.11.2013

доброго времени суток.

собрал скетч  по теме 

union IRcommand
{
  unsigned long l;
  byte b[4];
} val;

void setup(){
// val.l=1131134310;     // - хорошо  
// val.l=1587632040;    // - хорошо   
val.l=1111134310;         //  странно както сборка получается
  Serial.begin(9600);
  Serial.print("val ");
  Serial.print(val.l, DEC);
  Serial.print("  ");
  Serial.println(val.l, HEX);
  byte *x = (byte *)&val;
  for (int i=0; i<4; i++) 
  {
    Serial.print(val.b[i], DEC);
    Serial.print("  ");
    Serial.println(val.b[i], HEX);
  }
  
      
  unsigned long value = ((unsigned long)x[3] << 24) +((unsigned long)x[2] << 16) +(x[1] << 8) +x[0];  
   Serial.println("------------------");
     Serial.print("value ");
  Serial.print(value, DEC);
  Serial.print("  ");
  Serial.println(value, HEX);

}

void loop(){
}

 

с значениями  val 1587632040 и 1587632040    собрать обратно число получается хорошо    но с значение  1111134310    выводит

val 1111134310  423A9066
102  66
144  90
58  3A
66  42
------------------
val 1111068774  42399066

Подсажите пожалуйста где я ошибся.

Спасибо

maksim
Offline
Зарегистрирован: 12.02.2012

У вас "каша-малаша" получилась. Вы зарбираете передавая ссылку на объединение (бред полный), а собираете сдвигами.

void setup(){ 
  Serial.begin(9600);

  byte val_1[4];
  unsigned long &value_1 = (unsigned long&)val_1;
  value_1 = 1111134310;
  Serial.println(value_1, DEC);

  byte val_2[4];  
  memcpy(val_2, val_1, 4);  // присваиваем массив val_1 массиву val_2
  
  unsigned long &value_2 = (unsigned long&)val_2;

  Serial.println(value_2, DEC);
}

 

uu3jpv@mail.ru
Offline
Зарегистрирован: 18.09.2016

Тоже интересовал этот вопрос  вот тут нашёл  понятный для мне ответ http://playground.arduino.cc/Code/EEPROMReadWriteLong . Суть аналогична разложить на 4  байта , записать в епром , считать и собрать эти 4 байта обратно.

Тут https://www.arduino.cc/en/Reference/Bitshift  наглядно объяснняют  как это работает =)

fogary
Offline
Зарегистрирован: 05.03.2016

ustas пишет:

Появилась задачка - надо unsigned long сохранить в EEPROM.

Естественно, потом надо будет читать из EEPROM и собирать данные назад в unsigned long.

Подскажите, как сделать?

Чем методы EEPROM.put() и EEPROM.get() не устраивают?

Дмитро5
Offline
Зарегистрирован: 16.12.2019

Решил не создавать лишнюю тему, т.к. задача очень подобная.

Есть float, надо преобразовать в 4 байта.

float temper;
byte sendbuf[4];

  temper = sensor.getTempC();
  memcpy(&temper, &sendbuf[0], sizeof(float));

В результате операции в массиве sendbuf все нули, хотя в temper есть нормальное число.

Что не так?

DetSimen
DetSimen аватар
Онлайн
Зарегистрирован: 25.01.2017

У тебя источник и премник данных местами перепутаны в параметрах. Сначала адрес " куда", потом "откуда"

zorro2
Offline
Зарегистрирован: 04.01.2021
 
Serial.begin(9600);

 unsigned long k = 1000000;
 byte* pk = (byte*)&k; //разобрали на байты

 for(int i=0; i<sizeof(k); i++)
 {
  Serial.print(pk[i]);Serial.print(" ");
 }
 Serial.println("");

 unsigned long nk = *(unsigned long*)pk;//Собрали байты воедино
 Serial.println(nk);

 

 
Можно с любым типом данных так делать.
DetSimen
DetSimen аватар
Онлайн
Зарегистрирован: 25.01.2017

zorro2 пишет:

Можно с любым типом данных так делать.

Ну так, шаблон наваяй, вместо того, чтоб некрофилией занимаца.  А еще лучше - лямбда-шаблон. 

rkit
Offline
Зарегистрирован: 23.11.2016

Нельзя. Наебнется на куче платформ. Или порядок байтов будет неправильный.

zorro2
Offline
Зарегистрирован: 04.01.2021

Ну, на всех ардуинах этот код пишу при записи во внешний EEPROM по I2C, все работает, мне хватает, просто написал, может кому понадобиться.

-NMi-
Offline
Зарегистрирован: 20.08.2018

Нормально. Компилил в линь_х64, тока буте на чар заменил. 8 байт длина лонга в х64. Какая хер разница, ежли пихать в еепром и оттуда же вытаскивать? Всё всё-равно останецца на своих местах.

Kakmyc
Offline
Зарегистрирован: 15.01.2018

EEPROM.put() поддерживает даже структуры