4 последовательных байта в буфере в unsigned long

byte-order

Интересно, есть ли более умный способ получить эти 4 последовательных байта из буфера, объединенных в unsigned long.

// временная метка начинается с байта 40 принятого пакета и составляет четыре байта,
// или два слова, длинные. Сначала извлеките два слова:

unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
// объединить четыре байта (два слова) в длинное целое число
// это время NTP (секунды с 1 января 1900 г.):
unsigned long secsSince1900 = highWord << 16 | lowWord;
Serial.print("Seconds since Jan 1 1900 = ");
Serial.println(secsSince1900);

Мне кажется, это излишество, поскольку эти 4 байта уже идут подряд в буфере.

Результат должен быть неизменным, т.е. иметь значение в переменной.

, 👍0

Обсуждение

Это зависит от того, находятся ли они в том же порядке байтов (то есть в порядке от младшего к старшему), что и тип данных Long в Arduino. Судя по вашему текущему методу, это не так., @Majenko


3 ответа


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

0

В итоге я сделал это:

unsigned long ntp_time = 0;
for (byte i = 0; i < 4; i++){
  ntp_time = ntp_time << 8 | (byte) packetBuffer[40 + i];
}

Это то, что я бы использовал на языке высокого уровня, и моя первая попытка до того, как спросить, — которая не удалась, потому что в ней не было приведения к байту.

Мне все еще нужно понять, почему это не удалось, поскольку packetBuffer был объявлен как

const byte *packetBuffer

что, как мне кажется, уже адресует байты...

Спасибо Majenko за помощь, его решение по-прежнему преобразует все байты буфера в 4 байта, я предпочитаю свой, который ограничивается байтами.

,

Приведение к byte бесполезно. Если без приведения тип не работает, это, вероятно, связано с какой-то не связанной с этим проблемой, которую вы исправили примерно в то же время, когда добавили приведение. В таких ситуациях может помочь программа отслеживания версий., @Edgar Bonet


3

Если бы они были в том же порядке байтов, что и long (в Arduino — в порядке байтов от младшего к старшему, чего, судя по всему, не происходит), вы могли бы просто указать на них:

uint32_t *l = (uint32_t *)&packetBuffer[40];

Затем используйте *l для доступа к содержимому как к длинному.

Однако, поскольку они выглядят в порядке big-endian, это не так просто. Самое простое, что вы можете сделать, это просто объединить их заново с помощью битового сдвига:

uint32_t l = ((uint32_t)packetBuffer[40] << 24) | ((uint32_t)packetBuffer[41] << 16) | 
             ((uint32_t)packetBuffer[42] << 8) | (uint32_t)packetBuffer[43];
,

Если я добавлю последнюю строку вашего кода, а затем выполню Serial.println(secsSince1900, HEX); Serial.println(secsSince1900, HEX);, я получу DEAAC142 и FFFFC142. Есть подсказка?, @neurino

На самом деле, на небольшом 8-битном изображении может потребоваться приводить значения к 32-битному перед сдвигом битов, чтобы избежать клиппинга. Давно я не пользовался такими маленькими чипами., @Majenko

Действительно. Если и int, и тип packetBuffer[40] меньше 32 бит, то packetBuffer[40] << 24 вызывает неопределённое поведение., @Edgar Bonet

Спасибо за ваши ответы, вы помогли мне найти ошибку в моем коде., @neurino


1

Альтернативой является использование union и struct, и предоставление компилятору возможности выполнить работу:

static inline uint32_t bswap32(uint32_t x)
{
        union {
                uint32_t x;
                struct {
                        uint8_t a;
                        uint8_t b;
                        uint8_t c;
                        uint8_t d;
                } s;
        } in, out;
        in.x = x;
        out.s.a = in.s.d;
        out.s.b = in.s.c;
        out.s.c = in.s.b;
        out.s.d = in.s.a;
        return out.x;
} 

uint32_t sinceEpoch = bswap32(*(uint32_t*) &packetBuffer[40]);

Ссылка http://lists.nongnu.org/archive/html/avr-gcc-list/2006-12/msg00076.html

Чистая переписывание буфера пакетов будет выглядеть так:

static inline uint32_t bswap32(uint8_t* buf)
{
        union {
                uint32_t x;
                struct {
                        uint8_t a;
                        uint8_t b;
                        uint8_t c;
                        uint8_t d;
                } s;
        } in, out;
        out.s.a = buf[3];
        out.s.b = buf[2];
        out.s.c = buf[1];
        out.s.d = buf[0];
        return out.x;
} 

uint32_t sinceEpoch = bswap32(&packetBuffer[40]);

Вот версия машинного кода AVR:

inline uint32_t bswap32(uint32_t value)
{
  asm volatile("mov __tmp_reg__, %A0"   "\n\t"
           "mov %A0, %D0"       "\n\t"
           "mov %D0, __tmp_reg__"   "\n\t"
           "mov __tmp_reg__, %B0"   "\n\t"
           "mov %B0, %C0"       "\n\t"
           "mov %C0, __tmp_reg__"   "\n\t"
           : "=r" (value)
           : "0" (value)
           );
  return (value);
}

Ссылка. Cosa/Types.h, https://github.com/mikaelpatel/Cosa/blob/master/cores/cosa/Cosa/Types.h#L553 и Cosa-NTP/NTP.cpp, https://github.com/mikaelpatel/Cosa-NTP/blob/master/NTP.cpp#L56

,