Упаковка битов в байт

Тема в разделе "Микроконтроллеры AVR", создана пользователем Yerabdi, 24 окт 2020.

  1. Igor68

    Igor68 Гуру

    Ну типа так:
    Код (C++):
    //шаблоны бит
    #define _vipit     0x01
    #define _zakusit 0x02

    //работа с битами
    uint8_t mask = 0;
    if(/* что-то в условии */) mask |= _vipit;

    //или так
    /* какое-то условие(выражение) */ ? mask |= _zakusit : mask &= _zakusit;
     
    тут вообще тип bool не нужен. Работаем сразу с битами байта и потом уже этот байт передаём:)
     
  2. Код (Text):

    // Декларируем объединение 2-х байтов и структуры из 16-ти однобитовых полей
    union my_union {
      uint8_t bytes[2];
      struct {
        unsigned f0 : 1;
        unsigned f1 : 1;
        unsigned f2 : 1;
        unsigned f3 : 1;
        unsigned f4 : 1;
        unsigned f5 : 1;
        unsigned f6 : 1;
        unsigned f7 : 1;
        unsigned f8 : 1;
        unsigned f9 : 1;
        unsigned f10 : 1;
        unsigned f11 : 1;
        unsigned f12 : 1;
        unsigned f13 : 1;
        unsigned f14 : 1;
        unsigned f15 : 1;
      } __attribute__ ((__packed__)) fields;
    };

    // какая-то функция куда передают 2 байта
    void parse2bytes(uint8_t a, uint8_t b) {
      my_union u;
      // Соберем 2 байта в переменную my_union
      u.bytes[0] = a;
      u.bytes[1] = b;
      // Отобразим битовые поля
      Serial.print("passed bits: ");
      Serial.print(u.fields.f0);
      Serial.print(u.fields.f1);
      Serial.print(u.fields.f2);
      Serial.print(u.fields.f3);
      Serial.print(u.fields.f4);
      Serial.print(u.fields.f5);
      Serial.print(u.fields.f6);
      Serial.print(u.fields.f7);
      Serial.print(u.fields.f8);
      Serial.print(u.fields.f9);
      Serial.print(u.fields.f10);
      Serial.print(u.fields.f11);
      Serial.print(u.fields.f12);
      Serial.print(u.fields.f13);
      Serial.print(u.fields.f14);
      Serial.print(u.fields.f15);
      Serial.println();
    }

    void setup() {
      Serial.begin(9600);
      // Объявим переменную и занулим её
      my_union a = {
        .bytes = {0, 0}
      };
      // Отобразим её размер
      Serial.print("sizeof my_union: ");
      Serial.print(sizeof(a));
      Serial.println(" bytes");
      // Установим несколько битовых полей
      a.fields.f0 = 1;
      a.fields.f3 = 1;
      a.fields.f10 = 1;
      a.fields.f11 = 1;
      // Передаеём 2 байта куда-то для разбора
      parse2bytes(a.bytes[0], a.bytes[1]);
    }

    void loop() {
    }
     
    Вывод
    sizeof my_union: 2 bytes
    passed bits: 1001000000110000

    Устанавливаем биты, передаем в 2-х байтах, проверяем (или читаем) то-же биты, не требуется совсем ничего
     
    Последнее редактирование: 27 окт 2020
    Andrey12 нравится это.
  3. Asper Daffy

    Asper Daffy Иксперд

    Алексей.А,

    структуру с битовыми полями лучше оставлять безымянной. Тогда Вы сможете писать короче. Не
    Код (C++):
    u.fields.f14
    а
    Код (C++):
    u.f14
    Удобнее же. Зачем Вам этот fields?
     
    Andrey12 нравится это.
  4. Да мне без разницы, дело привычки, равносильно как в методах класса всегда для данных класса писать this, лишний раз указав что это не какая-то переменная, а член этого класса.
     
  5. Yerabdi

    Yerabdi Гик

    Спасибо большое что подробно все расписали. union используется в С++, я пишу в основном на Си по этому не могу понять если сработал f0 и f4 и f7 как их данные передаем?
     
    Последнее редактирование: 29 окт 2020
  6. b707

    b707 Гуру

    с чего вы взяли? Как раз именно в Си и используется
    поэтому ?:) помоему вы не можете понять, потому что ничего сами не делаете
    А вы хоть как-нибудь попробуйте, предложите свой вариант кода. Тогда уже можно будет обсуждать, правильно или нет
     
  7. parovoZZ

    parovoZZ Гуру

    использовать позиционные маски и оператор |
     
  8. b707

    b707 Гуру

    нафига тогда унион ?

    И кстати, нефиг ТС-у подсказывать. В том, что ему написали - уже готовый ответ есть, пусть поищет сам.
     
  9. Yerabdi

    Yerabdi Гик

    Спасибо за совет
     
  10. Оно самое Programming languages - C
    Раздел 6.7.2.1 Structure and union specifiers
    6.5.2.3 Structure and union members
     
    Последнее редактирование: 29 окт 2020
  11. SergeiL

    SergeiL Оракул Модератор

    А как по мне, то мне удобней битовые операции при работе с вводом выводом. С битовыми обработку можно в цикл загнать. А юнион удобно когда с битами регистров работаешь, и нужны определенные биты. Получается лучше читаемость кода.
     
  12. Asper Daffy

    Asper Daffy Иксперд

    А с юнионом кто мешает? Объявите не отдельные биты, а массив битов и работайте себе в цикле на здоровье.
     
  13. Нагляднее битовым полям дать смысловые названия, пусть будут значения датчиков закрытия окон и включения освещения, ну и заодно и анонимную структуру используем, как это разрешено в C11 (как рекомендует @Asper Daffy :) )
    Код (C++):

    // Декларируем объединение 2-х байтов и структуры из 16-ти однобитовых полей
    union my_union {
      uint8_t bytes[2];
      struct {
        unsigned kitchen_window : 1;
        unsigned kitchen_lighting : 1;
        unsigned bathroom1_window : 1;
        unsigned bathroom1_lighting : 1;
        unsigned bathroom2_lighting : 1;
        unsigned living_room_window1 : 1;
        unsigned living_room_window2 : 1;
        unsigned living_room_lighting : 1;
        unsigned bedroom1_window : 1;
        unsigned bedroom1_lighting : 1;
        unsigned bedroom2_window : 1;
        unsigned bedroom2_lighting : 1;
        unsigned kids_room_window : 1;
        unsigned kids_room_lighting : 1;
        unsigned cabinet_window : 1;
        unsigned cabinet_lighting : 1;
      } __attribute__ ((__packed__));
    };

    // какая-то функция куда передают 2 байта
    void parse2bytes(uint8_t a, uint8_t b) {
      my_union u;
      // Соберем 2 байта в переменную my_union
      u.bytes[0] = a;
      u.bytes[1] = b;
      // Отобразим состояние некоторых датчиков
      if (u.bathroom1_window) {
        Serial.println("alert: the window in the first bathroom is open");
      }
      if (u.bathroom1_lighting) {
        Serial.println("alert: the lighting in the first bathroom is switched on");
      }
      if (u.kids_room_window) {
        Serial.println("alert: the window in the kids room is open");
      }
      if (u.kids_room_lighting) {
        Serial.println("alert: the lighting in the kids room is switched on");
      }
    }

    void setup() {
      Serial.begin(9600);
      Serial.println();
      union my_union a = {{0, 0}}; // Объявим переменную и занулим её
      // Если сработали некоторые датчики установим соответствующие поля
      a.bathroom1_lighting = 1;    // Оставили включенным свет в первой ванной
      a.kids_room_window = 1;      // Забыли закрыть окно в детской
      // Передаеём как просили 2 байта куда-то для разбора
      parse2bytes(a.bytes[0], a.bytes[1]);
    }

    void loop() {
    }
     
    Вывод:
    Код (Text):

    alert: the lighting in the first bathroom is switched on
    alert: the window in the kids room is open
     
     
  14. SergeiL

    SergeiL Оракул Модератор

    Со структурой и элементами структуры все понятно, а с массивом битов в юнион я не сталкивался, попробовал и так и так - выдает ошибки.
    Как правильно определить?
     
  15. Asper Daffy

    Asper Daffy Иксперд

    Ну, там не честный массив, оно просто выглядит как массив и пользоваться им можно как массивом. Нужно к структуре с битовыми полями или к самому юниону присобачить оператор доступа по индексу. Пример нужен?
     
  16. SergeiL

    SergeiL Оракул Модератор

    Нужен. Если не затруднит.
    Я и понимаю, что структура должна остаться, иначе не получается организовать доступ к битам одного байта через юнион.
     
  17. Asper Daffy

    Asper Daffy Иксперд

    Все примеры проверены в Arduino IDE, в другой среде, возможно, потребуется указать атрибут упаковки. В примерах я использую маленькую библиотечку для печати в Serial, её можно взять здесь.

    Ну, самый простой вариант массива bool'ов - это без доступа по именам полей, просто массив в котором каждый bool занимает один бит (а не байт, как если честно объявить массив типа bool). Его я приводил вот здесь.

    Но, сейчас нам нужен доступ по именам полей, поэтому я модифицировал тот пример и добавил проверку доступа по именам в тестовый скетч.

    Код (C++):
    #include "Printing.h"
    #include "BoolArray.h"

    static constexpr int8_t totalBools = 22;

    static MixedAccessBoolArray<totalBools> booleanArray;

    //
    // Печать массива.
    // Обратите внимание, что для печати "TRUE" и "FALSE" мы используем
    //    сам элемент массива в качестве bool в операторе "?"
    //
    void printArray(const char * title) {
        Serial << "*** " << title << "\r\n";
        for (int8_t i = 0; i < totalBools; i++) {
            Serial << "booleanArray[" << i << "]="
                << (booleanArray[i] ? "TRUE" : "FALSE") << "\r\n";
        }
        Serial << "-------------------------------\r\n";
    }

    void setup(void) {
        Serial.begin(115200);
        //
        // Убедимся, что размер правильный
        Serial << "sizeof(booleanArray) = " << sizeof(booleanArray) << "\r\n";
        //
        //    Печатаем массив. Там сначала все false
        printArray("All elements are FALSE");
        //
        //    Делаем все true и опять печатаем
        for (int8_t i = 0; i < totalBools; booleanArray[i] = true, ++i);
        printArray("All elements are TRUE");
        //
        //    Делаем чётные элементы false, а нечётные - true
        for (int8_t i = 0; i < totalBools; booleanArray[i] = (i % 2), ++i);
        printArray("Even elements are FALSE while odd elements are TRUE");
        //
        //    Присваиваем все нечётные элементы предыдущим чётным (должно стать всё TRUE)
        for (int8_t i = 0; i < totalBools; booleanArray[i] = booleanArray[i + 1], i += 2);
        printArray("All elements are TRUE");
        //
        // Проверка доступа по именам
        // В результате все TRUE, кроме №№ 0, 1, 10, 20
        booleanArray.bit0 = false;
        booleanArray.bit20 = false;
        booleanArray.bit10 = booleanArray.bit0;
        booleanArray.bit1 = booleanArray.bit0 & booleanArray.bit1;
        booleanArray.bit2 = booleanArray.bit0 | booleanArray.bit2;
        printArray("Elements 0, 1, 10, 20 are FALSE. All other ones are TRUE");
    }

    void loop(void) {}
    Код (C++):
    #ifndef    BOOLARRAY_H
    #define BOOLARRAY_H

    template <byte bitSize>
    union MixedAccessBoolArray {
       
        struct CunningBool {
            byte & bt, mask;
            CunningBool(byte & _bt, byte _mask) : bt(_bt), mask(_mask) {}
            operator bool(void) { return static_cast<bool> (bt & mask); }
            bool operator = (const bool b) { return (bt = b ? bt | mask : bt & ~mask); }
            bool operator = (CunningBool & other) { return static_cast<bool>(*this = static_cast<bool>(other)); }
        };

        MixedAccessBoolArray(void) { memset(anArray, 0, sizeof(anArray)); }
        CunningBool operator [] (const int n) { return CunningBool (anArray[n / 8], 1 << (n % 8)); }
        byte anArray[(bitSize + 7) / 8];
        struct {
            unsigned bit0:1;
            unsigned bit1:1;
            unsigned bit2:1;
            unsigned bit3:1;
            unsigned bit4:1;
            unsigned bit5:1;
            unsigned bit6:1;
            unsigned bit7:1;
            unsigned bit8:1;
            unsigned bit9:1;
            unsigned bit10:1;
            unsigned bit11:1;
            unsigned bit12:1;
            unsigned bit13:1;
            unsigned bit14:1;
            unsigned bit15:1;
            unsigned bit16:1;
            unsigned bit17:1;
            unsigned bit18:1;
            unsigned bit19:1;
            unsigned bit20:1;
            unsigned bit21:1;
        };
    };

    #endif    //    BOOLARRAY_H

    Обратите внимание, у нас 22 бита и sizeof всей байды - 3, т.е. всё как нужно.
     
    Andrey12 и SergeiL нравится это.
  18. SergeiL

    SergeiL Оракул Модератор

    Спасибо большое!!! Буду разбираться.
    Под Мегу собралось, ошибок нет!
    С собой только ESP, под ESP ругнулось на __uint24 в printing.h. Посмотрю сегодня вечером.
     
  19. Asper Daffy

    Asper Daffy Иксперд

    Просто выбросьте строки с 9-ой по 12-ую. Этот __uint24 нахрен не нужен, просто в ардуине есть, вот я и выпендрился - добавил :)

    А так, там нечему быть непереносимым, разве что packed нужно будет поставить, если по умолчанию упаковка не байтовая.
     
  20. akl

    akl Гуру

    все эти юнионы выглядят уж очень замороченно. имеют ли они смысл с точки зрения удобства, а так же производительности, экономии памяти и типа того?
    мне кажется битовые шаблоны как в примере Igor68 проще и лучше. даже в виндосе при передаче параметров, ошибок и прочего используется что-то подобное