| Форум РадиоКот https://radiokot.ru/forum/ |
|
| Как разбить двухбайтную переменную на 2 байта в СИ? https://radiokot.ru/forum/viewtopic.php?f=57&t=180033 |
Страница 1 из 2 |
| Автор: | Kalisnik [ Пн дек 06, 2021 20:33:43 ] |
| Заголовок сообщения: | Как разбить двухбайтную переменную на 2 байта в СИ? |
Требуется разбить двухбайтную переменную (uint16_t) на старший байт и младший. Как это сделать в СИ? |
|
| Автор: | uldemir [ Пн дек 06, 2021 20:39:07 ] |
| Заголовок сообщения: | Re: Как разбить двухбайтную переменную на 2 байта в СИ? |
a0 = (ui16 >> 0) & 0x00ff; a1 = (ui16 >> 8) & 0x00ff; |
|
| Автор: | DimAlt [ Пн дек 06, 2021 21:05:09 ] |
| Заголовок сообщения: | 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 |
|
| Автор: | Dimon456 [ Пн дек 06, 2021 21:10:46 ] |
| Заголовок сообщения: | 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 [ Пн дек 06, 2021 23:30:34 ] |
| Заголовок сообщения: | Re: Как разбить двухбайтную переменную на 2 байта в СИ? |
Можно еще проще, т.к. всегда "конечность" архитектуры известра: Код: 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; } |
|
| Автор: | ddr4 [ Вт дек 07, 2021 01:06:05 ] |
| Заголовок сообщения: | 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 [ Вт дек 07, 2021 10:11:30 ] |
| Заголовок сообщения: | 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); Зачем "& 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 [ Вт дек 07, 2021 14:09:34 ] |
| Заголовок сообщения: | Re: Как разбить двухбайтную переменную на 2 байта в СИ? |
И конечно эффективность кода Спойлер![]() ![]() 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 [ Вт дек 07, 2021 14:49:53 ] |
| Заголовок сообщения: | Re: Как разбить двухбайтную переменную на 2 байта в СИ? |
В последнем проекте у меня много вот такого "ужаса", потому что оперативку и 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. |
|
| Автор: | ddr4 [ Вт дек 07, 2021 15:01:14 ] |
| Заголовок сообщения: | 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); Зачем "& 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; // усекаем явно - чтоб компилятор не ругался Причём машинный код генерируется (gcc) идентичный.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 }; |
|
| Автор: | Kalisnik [ Вт дек 07, 2021 20:29:07 ] |
| Заголовок сообщения: | Re: Как разбить двухбайтную переменную на 2 байта в СИ? |
А что будет если от переменной unsigned равной нулю, отнять какое-то число? Например, 25? Unsigned переменная ведь не может иметь знака -... В онлайн симуляторе СИ биты инвертируются и переменная приобретает неприлично большое значение. В CVAVR будет такое же поведение? |
|
| Автор: | Dimon456 [ Вт дек 07, 2021 20:48:54 ] |
| Заголовок сообщения: | Re: Как разбить двухбайтную переменную на 2 байта в СИ? |
Вы как-то все радикально к этому по дошли, давайте что нибудь по проще, к примеру так Код: uint16_t temp;
uint8_t data[2]; // туда temp = *((uint16_t*)&data[0]); // обратно *((uint16_t*)&data[0]) = temp; |
|
| Автор: | Eddy_Em [ Вт дек 07, 2021 21:44:18 ] |
| Заголовок сообщения: | 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 |
|
| Автор: | Kalisnik [ Вт дек 07, 2021 22:17:52 ] |
| Заголовок сообщения: | Re: Как разбить двухбайтную переменную на 2 байта в СИ? |
DX168B, структуры не требовательны к ресурсам? AVR'у будет не тяжело? |
|
| Автор: | GoldenAndy [ Вт дек 07, 2021 23:39:52 ] |
| Заголовок сообщения: | Re: Как разбить двухбайтную переменную на 2 байта в СИ? |
А что будет если от переменной unsigned равной нулю, отнять какое-то число? Например, 25? Unsigned переменная ведь не может иметь знака -... Си не контролирует выходы за пределы диапазона. И все переполнения и прочее - на совести программиста. Пусть будет 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 [ Ср дек 08, 2021 09:14:00 ] |
| Заголовок сообщения: | Re: Как разбить двухбайтную переменную на 2 байта в СИ? |
DX168B, структуры не требовательны к ресурсам? AVR'у будет не тяжело? Нет. В Си структуры превращаются в обычные массивы. Структуры с битовыми полями у меня не потребляли больше обычных операций с масками и сдвигами. Пример из реального проекта: Код: //******************************************** // Ужасная структура // 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++ ардуиновским компилятором и без оптимизации. |
|
| Автор: | Kalisnik [ Ср дек 08, 2021 10:38:09 ] |
| Заголовок сообщения: | Re: Как разбить двухбайтную переменную на 2 байта в СИ? |
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 [ Ср дек 08, 2021 10:40:17 ] |
| Заголовок сообщения: | Re: Как разбить двухбайтную переменную на 2 байта в СИ? |
Да. Если сильно не увлекаться полиморфизмом объектов и вместо прямого полиморфизма использовать полиморфизм через шаблоны классов. |
|
| Автор: | GoldenAndy [ Ср дек 08, 2021 10:54:29 ] |
| Заголовок сообщения: | Re: Как разбить двухбайтную переменную на 2 байта в СИ? |
Kalisnik, ООП АВРкам не под силу. Они умеют только машинные коды выполнять. А как сформированы эти коды - вопрос к компилятору. (И к прокладке) А ООП - это к компилятору. Умеет компилятор ООП - будет работать на АВР, не умеет - не будет. Ардуина - это плюсы, соответственно, все прелести ООП доступны. Но нужно уметь ими красиво пользоваться, что б не выжрать все ресурсы. ЗЫ. Для МК предпочитаю голый Си. |
|
| Автор: | Ivanoff-iv [ Ср дек 08, 2021 11:09:16 ] |
| Заголовок сообщения: | Re: Как разбить двухбайтную переменную на 2 байта в СИ? |
еще интересен момент... тут лучше написать так:Код: uint8_t var[3] = {245, 235, 255}; uint8_t sum; sum = (uint16_t) (var[0] + var[1] + var[2]) / 3; тогда компилятор для промежуточных вычислений зарезервирует 16 бит переменную (2 регистра), а результат запишет в 8 битную выходную переменную. |
|
| Страница 1 из 2 | Часовой пояс: UTC + 3 часа |
| Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group http://www.phpbb.com/ |
|





