Как разбить двухбайтную переменную на 2 байта в СИ?
Как разбить двухбайтную переменную на 2 байта в СИ?
Требуется разбить двухбайтную переменную (uint16_t) на старший байт и младший. Как это сделать в СИ?
- Реклама
Re: Как разбить двухбайтную переменную на 2 байта в СИ?
a0 = (ui16 >> 0) & 0x00ff;
a1 = (ui16 >> 8) & 0x00ff;
a1 = (ui16 >> 8) & 0x00ff;
-
DimAlt
- Вымогатель припоя
- Сообщения: 576
- Зарегистрирован: Пт май 19, 2006 05:39:11
- Контактная информация:
Re: Как разбить двухбайтную переменную на 2 байта в СИ?
Код: Выделить всё
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 Re: Как разбить двухбайтную переменную на 2 байта в СИ?
Код: Выделить всё
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 байта в СИ?
Можно еще проще, т.к. всегда "конечность" архитектуры известра:
Если остроконечная, то сначала будет L, потом H; если тупоконечная - наоборот.
Если порядок байт нужно поменять, делаем так:
Код: Выделить всё
uint16_t data[N];
uint8_t *dptr = (uint8_t*) data;
for(int i = 2*N; i > 0; --i)
do_something_with(*dptr++);
Если порядок байт нужно поменять, делаем так:
Код: Выделить всё
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;
}
- Реклама
Re: Как разбить двухбайтную переменную на 2 байта в СИ?
Код: Выделить всё
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 байта в СИ?
[uquote="ddr4",url="/forum/viewtopic.php?p=4136758#p4136758"][/uquote]
Зачем "& 0x00ff" и "& 0xff00" ?
В первом случае, при присвоении к 8-ми битной переменной, старший байт сам улетит.
Во втором случае, он улетит при сдвиге вправо.
Лишние букафки в исходнике.
Добавлено after 8 minutes 21 second:
Код: Выделить всё
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);Зачем "& 0x00ff" и "& 0xff00" ?
В первом случае, при присвоении к 8-ми битной переменной, старший байт сам улетит.
Во втором случае, он улетит при сдвиге вправо.
Лишние букафки в исходнике.
Добавлено after 8 minutes 21 second:
Код: Выделить всё
uint16_t uint16_val = 0x0123;
uint8_t v1 = uint16_val;
uint8_t v2 = uint16_val >> 8;
Re: Как разбить двухбайтную переменную на 2 байта в СИ?
И конечно эффективность кода

do_something_with
Добавлено after 3 hours 54 minutes 29 seconds:
Что-то все так промолчали?
Давайте усложним, выдернем не только байты, но и полубайты, и что характерно одной конструкцией

Спойлер


do_something_with
функция из с++, очень бы хотелось глянуть на выхлоп с++, плиз, если вас не затруднитwarning: implicit declaration of function 'do_something_with' [-Wimplicit-function-declaration]
Что-то все так промолчали?
Давайте усложним, выдернем не только байты, но и полубайты, и что характерно одной конструкцией
Спойлер
Код: Выделить всё
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 байта в СИ?
В последнем проекте у меня много вот такого "ужаса", потому что оперативку и EEPROM нужно было экономить. Особенно EEPROM.
Битовые поля в union.
Короче, есть два варианта:
Суть второго варианта в том, что структура упакует свои поля в одно 16-битное слово, которое уже отражается в поле foo объединения FooType.
Спойлер
Код: Выделить всё
// 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()
Короче, есть два варианта:
Код: Выделить всё
// Номер раз
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;
I am DX168B and this is my favourite forum on internet!
Re: Как разбить двухбайтную переменную на 2 байта в СИ?
[uquote="Аlex",url="/forum/viewtopic.php?p=4136861#p4136861"][uquote="ddr4",url="/forum/viewtopic.php?p=4136758#p4136758"][/uquote]
Зачем "& 0x00ff" и "& 0xff00" ?
В первом случае, при присвоении к 8-ми битной переменной, старший байт сам улетит.
Во втором случае, он улетит при сдвиге вправо.
Лишние букафки в исходнике.
Добавлено after 8 minutes 21 second:[/uquote]
"0xff00" в образовательных целях, да это пример аналогичный
Причём машинный код генерируется (gcc) идентичный.
ПС. Код (усечение + битовый сдвиг) на одну (union) или две (struct *) машинных команд меньше. (без оптимизации)
Код: Выделить всё
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);Зачем "& 0x00ff" и "& 0xff00" ?
В первом случае, при присвоении к 8-ми битной переменной, старший байт сам улетит.
Во втором случае, он улетит при сдвиге вправо.
Лишние букафки в исходнике.
Добавлено after 8 minutes 21 second:
Код: Выделить всё
uint16_t uint16_val = 0x0123;
uint8_t v1 = uint16_val;
uint8_t v2 = uint16_val >> 8;
"0xff00" в образовательных целях, да это пример аналогичный
Код: Выделить всё
uint8_t v1 = (uint8_t) uint16_val; // усекаем явно - чтоб компилятор не ругался
uint8_t v2 = (uint8_t) (uint16_val >> 8);
ПС. Код (усечение + битовый сдвиг) на одну (union) или две (struct *) машинных команд меньше. (без оптимизации)
Код: Выделить всё
typedef union
{
struct
{
uint8_t b1;
uint8_t b2;
};
uint16_t dwaBytes;
} DwaBytes_t;
DwaBytes_t dwaByte = { .dwaBytes = 0xBBAA };
Re: Как разбить двухбайтную переменную на 2 байта в СИ?
А что будет если от переменной unsigned равной нулю, отнять какое-то число? Например, 25? Unsigned переменная ведь не может иметь знака -...
В онлайн симуляторе СИ биты инвертируются и переменная приобретает неприлично большое значение. В CVAVR будет такое же поведение?
В онлайн симуляторе СИ биты инвертируются и переменная приобретает неприлично большое значение. В CVAVR будет такое же поведение?
Re: Как разбить двухбайтную переменную на 2 байта в СИ?
Вы как-то все радикально к этому по дошли, давайте что нибудь по проще, к примеру так
Код: Выделить всё
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 байта в СИ?
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
Re: Как разбить двухбайтную переменную на 2 байта в СИ?
DX168B, структуры не требовательны к ресурсам? AVR'у будет не тяжело? 
- GoldenAndy
- Поставщик валерьянки для Кота
- Сообщения: 1925
- Зарегистрирован: Чт июл 28, 2016 07:58:37
- Откуда: Kyiv, UA
- Контактная информация:
Re: Как разбить двухбайтную переменную на 2 байта в СИ?
[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.
То же самое при вычитании работает.
Си не контролирует выходы за пределы диапазона.
И все переполнения и прочее - на совести программиста.
Пусть будет 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.
То же самое при вычитании работает.
- DX168B
- Друг Кота
- Сообщения: 4468
- Зарегистрирован: Вс янв 24, 2010 19:19:52
- Откуда: Главный Улей России (Moscow)
- Контактная информация:
Re: Как разбить двухбайтную переменную на 2 байта в СИ?
[uquote="Kalisnik",url="/forum/viewtopic.php?p=4137225#p4137225"]DX168B, структуры не требовательны к ресурсам? AVR'у будет не тяжело?
[/uquote]
Нет. В Си структуры превращаются в обычные массивы. Структуры с битовыми полями у меня не потребляли больше обычных операций с масками и сдвигами.
Пример из реального проекта:
Как видно, это банальная запись в оперативку и вызов процедуры записи в EEPROM.
Этот дизасм - результат компиляции кода, написанного на C++ ардуиновским компилятором и без оптимизации.
Нет. В Си структуры превращаются в обычные массивы. Структуры с битовыми полями у меня не потребляли больше обычных операций с масками и сдвигами.
Пример из реального проекта:
Код: Выделить всё
//********************************************
// Ужасная структура
// 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>
Этот дизасм - результат компиляции кода, написанного на C++ ардуиновским компилятором и без оптимизации.
I am DX168B and this is my favourite forum on internet!
Re: Как разбить двухбайтную переменную на 2 байта в СИ?
DX168B, и ООП под силу AVR?
Добавлено after 1 hour 14 minutes 34 seconds:
GoldenAndy, еще интересен момент...
Интересно как обрабатывается вычисление в скобках? Ведь там результат сложения явно будет больше одного байта. Но после деления все снова будет в нужном диапазоне. Куда запишется временный результат сложения в скобках и нужно ли заботится о том, что он выходит из допустимого диапазона чисел?
Добавлено 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 байта в СИ?
Да. Если сильно не увлекаться полиморфизмом объектов и вместо прямого полиморфизма использовать полиморфизм через шаблоны классов.
I am DX168B and this is my favourite forum on internet!
- GoldenAndy
- Поставщик валерьянки для Кота
- Сообщения: 1925
- Зарегистрирован: Чт июл 28, 2016 07:58:37
- Откуда: Kyiv, UA
- Контактная информация:
Re: Как разбить двухбайтную переменную на 2 байта в СИ?
Kalisnik, ООП АВРкам не под силу. Они умеют только машинные коды выполнять. А как сформированы эти коды - вопрос к компилятору. (И к прокладке)
А ООП - это к компилятору. Умеет компилятор ООП - будет работать на АВР, не умеет - не будет.
Ардуина - это плюсы, соответственно, все прелести ООП доступны. Но нужно уметь ими красиво пользоваться, что б не выжрать все ресурсы.
ЗЫ. Для МК предпочитаю голый Си.
А ООП - это к компилятору. Умеет компилятор ООП - будет работать на АВР, не умеет - не будет.
Ардуина - это плюсы, соответственно, все прелести ООП доступны. Но нужно уметь ими красиво пользоваться, что б не выжрать все ресурсы.
ЗЫ. Для МК предпочитаю голый Си.
- Ivanoff-iv
- Друг Кота
- Сообщения: 7077
- Зарегистрирован: Пт ноя 11, 2016 05:48:09
- Откуда: Сердце Пармы
Re: Как разбить двухбайтную переменную на 2 байта в СИ?
[uquote="Kalisnik",url="/forum/viewtopic.php?p=4137348#p4137348"]еще интересен момент...[/uquote]тут лучше написать так:
тогда компилятор для промежуточных вычислений зарезервирует 16 бит переменную (2 регистра), а результат запишет в 8 битную выходную переменную.
Код: Выделить всё
uint8_t var[3] = {245, 235, 255};
uint8_t sum;
sum = (uint16_t) (var[0] + var[1] + var[2]) / 3;
Для тех, кто не учил магию мир полон физики 
Безграмотно вопрошающим про силовую или высоковольтную электронику я не отвечаю, а то ещё посадят за участие в (само)убиении оболтуса...
Безграмотно вопрошающим про силовую или высоковольтную электронику я не отвечаю, а то ещё посадят за участие в (само)убиении оболтуса...





