Как разбить двухбайтную переменную на 2 байта в СИ?

Обсуждаем контроллеры компании Atmel.
Kalisnik
Мучитель микросхем
Сообщения: 430
Зарегистрирован: Вс апр 18, 2021 15:43:55

Как разбить двухбайтную переменную на 2 байта в СИ?

Сообщение Kalisnik »

Требуется разбить двухбайтную переменную (uint16_t) на старший байт и младший. Как это сделать в СИ?
Реклама
Аватара пользователя
uldemir
Друг Кота
Сообщения: 7359
Зарегистрирован: Пт авг 28, 2009 21:34:30
Откуда: 845-й км.

Re: Как разбить двухбайтную переменную на 2 байта в СИ?

Сообщение uldemir »

a0 = (ui16 >> 0) & 0x00ff;
a1 = (ui16 >> 8) & 0x00ff;
Реклама
DimAlt
Вымогатель припоя
Сообщения: 576
Зарегистрирован: Пт май 19, 2006 05:39:11
Контактная информация:

Re: Как разбить двухбайтную переменную на 2 байта в СИ?

Сообщение DimAlt »

Код: Выделить всё

typedef union 
	{
	uint8_t Byte[2];
	uint16_t i;
	float f;
	} TValue;
TValue Temp;
Temp.Byte[0]=0xFF;//uint8_t 
Temp.Byte[1]=0xFF;//uint8_t 
Temp.i=0xFFFF//uint16_t
Temp.float=1.0;//float 
Dimon456
Мудрый кот
Сообщения: 1849
Зарегистрирован: Вс дек 25, 2016 08:34:54

Re: Как разбить двухбайтную переменную на 2 байта в СИ?

Сообщение Dimon456 »

Код: Выделить всё

union BytByte {
struct {
uint8_t l;
uint8_t h;
} bit;
uint16_t byte;
};

union BytByte myByte;

myByte.byte = 0x1235;

a0 = myByte.bit.l;
a1 = myByte.bit.h;
Реклама
Эиком - электронные компоненты и радиодетали
Аватара пользователя
Eddy_Em
Собутыльник Кота
Сообщения: 2516
Зарегистрирован: Пт июл 12, 2019 22:52:01
Контактная информация:

Re: Как разбить двухбайтную переменную на 2 байта в СИ?

Сообщение Eddy_Em »

Можно еще проще, т.к. всегда "конечность" архитектуры известра:

Код: Выделить всё

uint16_t data[N];
uint8_t *dptr = (uint8_t*) data;
for(int i = 2*N; i > 0; --i)
  do_something_with(*dptr++);
Если остроконечная, то сначала будет L, потом H; если тупоконечная - наоборот.
Если порядок байт нужно поменять, делаем так:

Код: Выделить всё

uint16_t data[N];
uint8_t *dptr = (uint8_t*) data;
for(int i = 0; i < N; ++i){
  do_something_with(dptr[1]);
  do_something_with(dptr[0]);
  dptr += 2;
}
Linux rules! Windows must die. Здравомыслящий человек добровольно будет пользоваться мастдаем лишь в двух случаях: под дулом автомата или под влиянием анального зонда.
Я на гитхабе, в ЖЖ
Реклама
Аватара пользователя
ddr4
Потрогал лапой паяльник
Сообщения: 352
Зарегистрирован: Ср дек 30, 2020 23:05:29

Re: Как разбить двухбайтную переменную на 2 байта в СИ?

Сообщение ddr4 »

Код: Выделить всё

uint16_t in_val = 0xBBAA;

unsigned char byte_1 = (uint8_t) (in_val & 0x00ff);
unsigned char byte_2 = (uint8_t) ((in_val & 0xff00) >> 8);

printf("b1: 0x%X, b2: 0x%X\n", byte_1, byte_2);

Код: Выделить всё

Вывод: 
        b1: 0xAA, b2: 0xBB
Реклама
Аватара пользователя
Аlex
Модератор
Сообщения: 4614
Зарегистрирован: Чт мар 18, 2010 23:09:57
Откуда: Планета Земля
Контактная информация:

Re: Как разбить двухбайтную переменную на 2 байта в СИ?

Сообщение Аlex »

[uquote="ddr4",url="/forum/viewtopic.php?p=4136758#p4136758"]

Код: Выделить всё

uint16_t in_val = 0xBBAA;

unsigned char byte_1 = (uint8_t) (in_val & 0x00ff);
unsigned char byte_2 = (uint8_t) ((in_val & 0xff00) >> 8);

printf("b1: 0x%X, b2: 0x%X\n", byte_1, byte_2);
[/uquote]
Зачем "& 0x00ff" и "& 0xff00" ?
В первом случае, при присвоении к 8-ми битной переменной, старший байт сам улетит.
Во втором случае, он улетит при сдвиге вправо.

Лишние букафки в исходнике.

Добавлено after 8 minutes 21 second:

Код: Выделить всё

uint16_t  uint16_val = 0x0123;

uint8_t v1 = uint16_val;
uint8_t v2 = uint16_val >> 8;
Dimon456
Мудрый кот
Сообщения: 1849
Зарегистрирован: Вс дек 25, 2016 08:34:54

Re: Как разбить двухбайтную переменную на 2 байта в СИ?

Сообщение Dimon456 »

И конечно эффективность кода
СпойлерИзображение

Изображение

do_something_with
warning: implicit declaration of function 'do_something_with' [-Wimplicit-function-declaration]
функция из с++, очень бы хотелось глянуть на выхлоп с++, плиз, если вас не затруднит
Добавлено after 3 hours 54 minutes 29 seconds:
Что-то все так промолчали?

Давайте усложним, выдернем не только байты, но и полубайты, и что характерно одной конструкцией
Спойлер

Код: Выделить всё

union BytByte {
struct {
uint8_t l;
uint8_t h;
} bit;
struct {
unsigned m:4;
unsigned l:4;
unsigned h:4;
unsigned g:4;
} tet;
uint16_t byte;
};
Изображение
Аватара пользователя
DX168B
Друг Кота
Сообщения: 4468
Зарегистрирован: Вс янв 24, 2010 19:19:52
Откуда: Главный Улей России (Moscow)
Контактная информация:

Re: Как разбить двухбайтную переменную на 2 байта в СИ?

Сообщение DX168B »

В последнем проекте у меня много вот такого "ужаса", потому что оперативку и EEPROM нужно было экономить. Особенно EEPROM.
Спойлер

Код: Выделить всё

// Outputs config
#pragma pack(1)
typedef struct COutsCfg
{
    union
    {
        struct
        {
            uint16_t out_0_cfg : 2;
            uint16_t out_1_cfg : 2;
            uint16_t out_2_cfg : 2;
            uint16_t out_3_cfg : 2;
            uint16_t out_4_cfg : 2;
            uint16_t out_5_cfg : 2;
            uint16_t out_6_cfg : 2;
            uint16_t out_7_cfg : 2;
        };
        uint16_t config16;
    };
    uint8_t beforeResetTimeout;
    uint8_t resetTimeout;
    uint8_t afterResetTimeout;
}OutsCfg;
#pragma pack()
 
Битовые поля в union.

Короче, есть два варианта:

Код: Выделить всё

// Номер раз
uint16_t foo = 0xAA55;

uint8_t low = foo & 0x00FF; // low = 0x55
uint8_t high = (foo & 0xFF00) >> 8; // high = 0xAA    

Код: Выделить всё

// Вариант номер двас
typedef union _FooType
{
    struct
    {
        uint8_t low;
        uint8_t high;
    };
    uint16_t foo;
}FooType;

FooType foo;
foo.foo = 0xAA55;

//foo.low = 0x55;
//foo.high = 0xAA;
Суть второго варианта в том, что структура упакует свои поля в одно 16-битное слово, которое уже отражается в поле foo объединения FooType.
I am DX168B and this is my favourite forum on internet!
Аватара пользователя
ddr4
Потрогал лапой паяльник
Сообщения: 352
Зарегистрирован: Ср дек 30, 2020 23:05:29

Re: Как разбить двухбайтную переменную на 2 байта в СИ?

Сообщение ddr4 »

[uquote="Аlex",url="/forum/viewtopic.php?p=4136861#p4136861"][uquote="ddr4",url="/forum/viewtopic.php?p=4136758#p4136758"]

Код: Выделить всё

uint16_t in_val = 0xBBAA;

unsigned char byte_1 = (uint8_t) (in_val & 0x00ff);
unsigned char byte_2 = (uint8_t) ((in_val & 0xff00) >> 8);

printf("b1: 0x%X, b2: 0x%X\n", byte_1, byte_2);
[/uquote]
Зачем "& 0x00ff" и "& 0xff00" ?
В первом случае, при присвоении к 8-ми битной переменной, старший байт сам улетит.
Во втором случае, он улетит при сдвиге вправо.

Лишние букафки в исходнике.

Добавлено after 8 minutes 21 second:

Код: Выделить всё

uint16_t  uint16_val = 0x0123;

uint8_t v1 = uint16_val;
uint8_t v2 = uint16_val >> 8;
[/uquote]

"0xff00" в образовательных целях, да это пример аналогичный

Код: Выделить всё

uint8_t v1 = (uint8_t) uint16_val; // усекаем явно - чтоб компилятор не ругался
uint8_t v2 = (uint8_t) (uint16_val >> 8);
 
Причём машинный код генерируется (gcc) идентичный.

ПС. Код (усечение + битовый сдвиг) на одну (union) или две (struct *) машинных команд меньше. (без оптимизации)

Код: Выделить всё

typedef union 
{
    struct  
    {
        uint8_t b1;
        uint8_t b2;
    };
    uint16_t dwaBytes;
} DwaBytes_t;

DwaBytes_t dwaByte = { .dwaBytes = 0xBBAA };
 
Kalisnik
Мучитель микросхем
Сообщения: 430
Зарегистрирован: Вс апр 18, 2021 15:43:55

Re: Как разбить двухбайтную переменную на 2 байта в СИ?

Сообщение Kalisnik »

А что будет если от переменной unsigned равной нулю, отнять какое-то число? Например, 25? Unsigned переменная ведь не может иметь знака -...
В онлайн симуляторе СИ биты инвертируются и переменная приобретает неприлично большое значение. В CVAVR будет такое же поведение?
Dimon456
Мудрый кот
Сообщения: 1849
Зарегистрирован: Вс дек 25, 2016 08:34:54

Re: Как разбить двухбайтную переменную на 2 байта в СИ?

Сообщение Dimon456 »

Вы как-то все радикально к этому по дошли, давайте что нибудь по проще, к примеру так

Код: Выделить всё

uint16_t temp;
uint8_t data[2];

// туда
temp = *((uint16_t*)&data[0]);

// обратно
*((uint16_t*)&data[0]) = temp;
Аватара пользователя
Eddy_Em
Собутыльник Кота
Сообщения: 2516
Зарегистрирован: Пт июл 12, 2019 22:52:01
Контактная информация:

Re: Как разбить двухбайтную переменную на 2 байта в СИ?

Сообщение Eddy_Em »

Dimon456, зачем ты завел две переменные для одних и тех же данных? Вот так же:

Код: Выделить всё

uint16_t temp;
uint8_t *data = (uint8_t*) &temp;

data[0] = 0xad; data[1] = 0xde; printf("16bit: 0x%x\n", temp);  // Little-endian 8 -> 16bit

temp = 0xaabb; printf("lo: 0x%s, hi: 0x%x\n", data[0], data[1]); // 16bit -> little-endian 8
Linux rules! Windows must die. Здравомыслящий человек добровольно будет пользоваться мастдаем лишь в двух случаях: под дулом автомата или под влиянием анального зонда.
Я на гитхабе, в ЖЖ
Kalisnik
Мучитель микросхем
Сообщения: 430
Зарегистрирован: Вс апр 18, 2021 15:43:55

Re: Как разбить двухбайтную переменную на 2 байта в СИ?

Сообщение Kalisnik »

DX168B, структуры не требовательны к ресурсам? AVR'у будет не тяжело? :)
Аватара пользователя
GoldenAndy
Поставщик валерьянки для Кота
Сообщения: 1925
Зарегистрирован: Чт июл 28, 2016 07:58:37
Откуда: Kyiv, UA
Контактная информация:

Re: Как разбить двухбайтную переменную на 2 байта в СИ?

Сообщение GoldenAndy »

[uquote="Kalisnik",url="/forum/viewtopic.php?p=4137174#p4137174"]А что будет если от переменной unsigned равной нулю, отнять какое-то число? Например, 25? Unsigned переменная ведь не может иметь знака -...[/uquote]
Си не контролирует выходы за пределы диапазона.
И все переполнения и прочее - на совести программиста.
Пусть будет unsigned char n = 0
тогда
n -= 25;
приведет к тому, что из 0 вычтется 25.
Результат будет -25.
-25 в знаковом представлении записывается как 0xE7.
Этот результат и запишется в нашу переменную n.
Но поскольку она беззнаковая - значение будет интерпретироваться как 231.
Вас же не удивит факт, если к байтовой переменной, в которой записано 240 (0xF0) прибавить 20 (0x14), то получится не 260, а 4 (0x04) ?
0xF0 + 0x14 = 0x104. Но 1 теряется, ибо переменная байтовая. Остается 0x04.
То же самое при вычитании работает.
ИзображениеИзображение
Изображение
 
Telegram               Лучшая благодарность ->
[+]
Аватара пользователя
DX168B
Друг Кота
Сообщения: 4468
Зарегистрирован: Вс янв 24, 2010 19:19:52
Откуда: Главный Улей России (Moscow)
Контактная информация:

Re: Как разбить двухбайтную переменную на 2 байта в СИ?

Сообщение DX168B »

[uquote="Kalisnik",url="/forum/viewtopic.php?p=4137225#p4137225"]DX168B, структуры не требовательны к ресурсам? AVR'у будет не тяжело? :)[/uquote]
Нет. В Си структуры превращаются в обычные массивы. Структуры с битовыми полями у меня не потребляли больше обычных операций с масками и сдвигами.

Пример из реального проекта:

Код: Выделить всё

//********************************************
// Ужасная структура

// Outputs config
#pragma pack(1)
typedef struct COutsCfg
{
    union
    {
        struct
        {
            uint16_t out_0_cfg : 2;
            uint16_t out_1_cfg : 2;
            uint16_t out_2_cfg : 2;
            uint16_t out_3_cfg : 2;
            uint16_t out_4_cfg : 2;
            uint16_t out_5_cfg : 2;
            uint16_t out_6_cfg : 2;
            uint16_t out_7_cfg : 2;
        };
        uint16_t config16;
    };
    uint8_t beforeResetTimeout;
    uint8_t resetTimeout;
    uint8_t afterResetTimeout;
}OutsCfg;
#pragma pack()

//********************************************
// Код 

// Reset outputs config
        OutsCfg outCfg;
        outCfg.config16 = OUTPUTS_DEFAULT_CONFIG;
        outCfg.beforeResetTimeout = OUTPUTS_TIMEOUT_BEFORE_RESET;
        outCfg.resetTimeout = OUTPUTS_TIMEOUT_RESET;
        outCfg.afterResetTimeout = OUTPUTS_TIMEOUT_AFTER_RESET;

        eeprom_write_block(&outCfg, (void*)(CONFIG_OUTS_OFFSET), sizeof(OutsCfg));

//********************************************
// Дизасм
        // Reset outputs config
        OutsCfg outCfg;
        outCfg.config16 = OUTPUTS_DEFAULT_CONFIG;
    35d4:    8a ea           ldi    r24, 0xAA    ; 170
    35d6:    9a e0           ldi    r25, 0x0A    ; 10
    35d8:    9a 83           std    Y+2, r25    ; 0x02
    35da:    89 83           std    Y+1, r24    ; 0x01
E:\PlatformIO\DailyTimer/src/Config.cpp:38
        outCfg.beforeResetTimeout = OUTPUTS_TIMEOUT_BEFORE_RESET;
    35dc:    8e e1           ldi    r24, 0x1E    ; 30
    35de:    8b 83           std    Y+3, r24    ; 0x03
E:\PlatformIO\DailyTimer/src/Config.cpp:39
        outCfg.resetTimeout = OUTPUTS_TIMEOUT_RESET;
    35e0:    12 e0           ldi    r17, 0x02    ; 2
    35e2:    1c 83           std    Y+4, r17    ; 0x04
E:\PlatformIO\DailyTimer/src/Config.cpp:40
        outCfg.afterResetTimeout = OUTPUTS_TIMEOUT_AFTER_RESET;
    35e4:    85 e0           ldi    r24, 0x05    ; 5
    35e6:    8d 83           std    Y+5, r24    ; 0x05
E:\PlatformIO\DailyTimer/src/Config.cpp:42
        eeprom_write_block(&outCfg, (void*)(CONFIG_OUTS_OFFSET), sizeof(OutsCfg));
    35e8:    45 e0           ldi    r20, 0x05    ; 5
    35ea:    50 e0           ldi    r21, 0x00    ; 0
    35ec:    68 e5           ldi    r22, 0x58    ; 88
    35ee:    71 e0           ldi    r23, 0x01    ; 1
    35f0:    ce 01           movw    r24, r28
    35f2:    01 96           adiw    r24, 0x01    ; 1
    35f4:    0e 94 d2 1e     call    0x3da4    ; 0x3da4 <eeprom_write_block>
Как видно, это банальная запись в оперативку и вызов процедуры записи в EEPROM.
Этот дизасм - результат компиляции кода, написанного на C++ ардуиновским компилятором и без оптимизации.
I am DX168B and this is my favourite forum on internet!
Kalisnik
Мучитель микросхем
Сообщения: 430
Зарегистрирован: Вс апр 18, 2021 15:43:55

Re: Как разбить двухбайтную переменную на 2 байта в СИ?

Сообщение Kalisnik »

DX168B, и ООП под силу AVR?

Добавлено after 1 hour 14 minutes 34 seconds:
GoldenAndy, еще интересен момент...

Код: Выделить всё

uint8_t var[3] = {245, 235, 255};
uint8_t sum;

sum = (var[0] + var[1] + var[2]) / 3;
Интересно как обрабатывается вычисление в скобках? Ведь там результат сложения явно будет больше одного байта. Но после деления все снова будет в нужном диапазоне. Куда запишется временный результат сложения в скобках и нужно ли заботится о том, что он выходит из допустимого диапазона чисел?
Аватара пользователя
DX168B
Друг Кота
Сообщения: 4468
Зарегистрирован: Вс янв 24, 2010 19:19:52
Откуда: Главный Улей России (Moscow)
Контактная информация:

Re: Как разбить двухбайтную переменную на 2 байта в СИ?

Сообщение DX168B »

Да. Если сильно не увлекаться полиморфизмом объектов и вместо прямого полиморфизма использовать полиморфизм через шаблоны классов.
I am DX168B and this is my favourite forum on internet!
Аватара пользователя
GoldenAndy
Поставщик валерьянки для Кота
Сообщения: 1925
Зарегистрирован: Чт июл 28, 2016 07:58:37
Откуда: Kyiv, UA
Контактная информация:

Re: Как разбить двухбайтную переменную на 2 байта в СИ?

Сообщение GoldenAndy »

Kalisnik, ООП АВРкам не под силу. Они умеют только машинные коды выполнять. А как сформированы эти коды - вопрос к компилятору. (И к прокладке)
А ООП - это к компилятору. Умеет компилятор ООП - будет работать на АВР, не умеет - не будет.
Ардуина - это плюсы, соответственно, все прелести ООП доступны. Но нужно уметь ими красиво пользоваться, что б не выжрать все ресурсы.

ЗЫ. Для МК предпочитаю голый Си.
ИзображениеИзображение
Изображение
 
Telegram               Лучшая благодарность ->
[+]
Аватара пользователя
Ivanoff-iv
Друг Кота
Сообщения: 7077
Зарегистрирован: Пт ноя 11, 2016 05:48:09
Откуда: Сердце Пармы

Re: Как разбить двухбайтную переменную на 2 байта в СИ?

Сообщение Ivanoff-iv »

[uquote="Kalisnik",url="/forum/viewtopic.php?p=4137348#p4137348"]еще интересен момент...[/uquote]тут лучше написать так:

Код: Выделить всё

uint8_t var[3] = {245, 235, 255};
uint8_t sum;

sum = (uint16_t) (var[0] + var[1] + var[2]) / 3;
тогда компилятор для промежуточных вычислений зарезервирует 16 бит переменную (2 регистра), а результат запишет в 8 битную выходную переменную.
Для тех, кто не учил магию мир полон физики :)
Безграмотно вопрошающим про силовую или высоковольтную электронику я не отвечаю, а то ещё посадят за участие в (само)убиении оболтуса...
Ответить

Вернуться в «AVR»