Jump to content

    
Arlleex

Плавный переход C -> C++ под МК

Recommended Posts

Спасибо на добром слове. ) Обоих застал и даже успел с zltigo обменятся любезностями. Спорить люблю. Но предпочитаю побеждать не умением спорить, а силой знаний. )

Share this post


Link to post
Share on other sites
12 hours ago, Kabdim said:

Бит-филды мне по прежнему не нравятся большим

Прочитал всё изложенное всеми участниками. Да, я с таким не сталкивался. Битовые поля всегда определял классически в столбик

struct {
  uint32_t a     : 1;
  uint32_t b     : 1;
  uint32_t c     : 4;
  uint32_t rsrvd : 26;
}BitField;

Проблем ни разу не возникало, поэтому искренне удивился, что у вас они появились. К сожалению, действительно есть такое, что если что=то в своё время не получилось, то потом возникает на долгое время недоверие. Тем не менее, когда у вас появится свободное время и желание, рекомендую всё же разобраться с битовыми полями. Это очень мощное средство управления данными, предоставляемое языком. И совершенно неправильно не использовать его) Этот подход удобен хотя бы для прямого отображения на регистры управления периферией. Или вот, на глаза попался драйвер SD-карты, где битовые поля используются в достатке


    union spi_r2_response_cmd_t {
        __packed struct fields_t {
            uint16_t card_is_locked : 1;
            uint16_t wp_erase_skip : 1;
            uint16_t error : 1;
            uint16_t cc_error : 1;
            uint16_t card_ecc_failed : 1;
            uint16_t wp_violation : 1;
            uint16_t erase_param : 1;
            uint16_t out_of_range : 1;
            uint16_t in_idle_state : 1;
            uint16_t erase_reset : 1;
            uint16_t illegal_command : 1;
            uint16_t com_crc_error : 1;
            uint16_t erase_sequence_error : 1;
            uint16_t address_error : 1;
            uint16_t parameter_error : 1;
            uint16_t : 1;
        }
        fields;
        std::array<uint8_t, 2> data;
        uint16_t word;
    };

На классических масках такое делать неудобное (ИМХО).

12 hours ago, Arlleex said:

Это Вы еще @zltigo не застали,

Эх, было время)))

Share this post


Link to post
Share on other sites
5 часов назад, haker_fox сказал:

Или вот, на глаза попался драйвер SD-карты, где битовые поля используются в достатке...

А вот я не очень оценил битовые поля при описании регистров периферии:to_take_umbrage: Тот или иной подход всяко диктуется стремлением повысить качество кода в плане лексической читаемости. Вот для описания битов периферии существует два основных способа: за-define-нные имена битов/масок и, собственно, битовые поля структур. По-хорошему, эти способы должны сосуществовать как бы независимо, на мой взгляд. Т.е. для полноценной работы необходимо и достаточно только первого или второго способа описания. Если перечислять только минусы этих способов, то у способа с define-ми это потенциальная громоздкость и глобальность ("замусоривание" глобального пространства имен) идентификаторов, например, DBGMCU_IDCODE_DEV_ID; у способа с битовыми полями "видимым" глобальным символом было бы только определение DBGMCU, а внутреннее устройство "наружу" не видно: DBGMCU.IDCODE.DEVID. Однако, я поднимал вопрос относительно недавно, как изменить несколько полей в структуре через битовые поля одновременно. В итоге понял, что или инициализировать промежуточную структуру с последующей записью исходной (что с синтаксической стороны не очень красиво), или держать рядом все те же старые добрые define-ы, а структуры переименовать в union, в которых будет битовое описание + регистр в целом. Но тогда мы потеряем все преимущество битовых полей (а их основное преимущество в сокрытии полезных идентификаторов регистров). То, что в битовых полях не нужно думать куда что двигать и насколько - не аргумент, т.к. легким движением руки прекрасно пишутся тривиальные макросы для облегчения и сведения в ноль такой необходимости. Поэтому, на сегодняшний день, битовые поля у себя использую лишь для удобочитаемой реализации каких-либо частей в самопальных протоколах и (реже) бизнес-логике драйверов.

Share this post


Link to post
Share on other sites
17 minutes ago, Arlleex said:

А вот я не очень оценил битовые поля при описании регистров периферии:to_take_umbrage: Т

Знаете, я их тоже не использую для этого) Это я лишь привёл как пример. На самом деле я использую битовые поля в пакетах протокола (как и Вы), вот пример из драйвера SD-карточки привёл. Также они находят применение в различных настроечных структурах, как тут (настройка ПДП в STMках)

struct ChannelSetupQuick {
        union Ctrl {
            struct {
                uint32_t reserved0           : 5;
                uint32_t isCircularMode      : 1;
                uint32_t isPeriphIncEnabled  : 1;
                uint32_t isMemIncEnabled     : 1;
                uint32_t reserved1           : 24;
            }fields;
            uint32_t ccr_reg;
            Ctrl() {
                fields.isMemIncEnabled = 1;
                fields.isPeriphIncEnabled = 0;
                fields.isCircularMode = 0;
            }
        }ctrl;
        std::uint16_t   dataCount;
        std::uint32_t   peripheralAddress;
        std::uint32_t   memoryAddress;
        ChannelSetupQuick()
            : dataCount(0)
            , peripheralAddress(0)
            , memoryAddress(0) {}
    };

По поводу того, что иногда нужно одновременно изменить группу бит в регитсрах периферии и через битовые поля это сделать затруднительно, Вы привели хороший пример. Да, пожалуй здесь либо лучше использовать старые добрые маски на основе макросов или констант, либо иногда я пишу "в лоб", поскольку считаю, что проще заглянуть в даташит, если возникнет необходимость разобраться, а что же конфигурируется.

    static const uint32_t TIEF_ALL_CH_MASK = ( 1UL << ( 4 * 0 + 3 ) )
                                             | ( 1UL << ( 4 * 1 + 3 ) )
                                             | ( 1UL << ( 4 * 2 + 3 ) )
                                             | ( 1UL << ( 4 * 3 + 3 ) )
                                             | ( 1UL << ( 4 * 4 + 3 ) )
                                             | ( 1UL << ( 4 * 5 + 3 ) )
                                             | ( 1UL << ( 4 * 6 + 3 ) );

Хотя признаюсь, что данный вариант не очень хорош в плане понимания.

Share this post


Link to post
Share on other sites
51 minutes ago, Arlleex said:

битовые поля у себя использую лишь для удобочитаемой реализации каких-либо частей в самопальных протоколах и (реже) бизнес-логике драйверов

У меня вообще если в коде где-то есть битовые поля, то это означает, что этот код я откуда-то взял ☺

По собственной воле никогда битовые поля не использовал и использовать не собираюсь. Макросы и булева логика намного удобней. А с некоторыми компиляторами (вроде sdcc) вообще веселуха с битовыми полями…

Share this post


Link to post
Share on other sites

В структуре управления устройством имею много вложенных структур, а в них переменные необходимой длины, включая битовые поля. Если есть 4 режима работы чего-либо, то для описания этих режимов достаточно двух битов. 

Страх вижу лишь в одном - bit endian, когда это связано с передачей по последовательным интерфейсам. 

Поэтому такую структуру передаю целиком, как массив. 

Share this post


Link to post
Share on other sites

Есть такая полезная штука, как union, позволяет превратить любую сложную структуру во что угодно для передачи, в том числе в массив, скажем, байтов.

Это позволяет отказаться от опасных указателей и заодно получить более надежный от ошибок код. Ну и читаемость растет в разы.

Да, да, именно читаемость, ради нее и стоит использовать битовые поля. Да и не надо их так бояться )) 

Если не писать все поля в одну строку, то число ошибок c ними стремиться к нулю ))

 

Share this post


Link to post
Share on other sites
1 час назад, Forger сказал:

Да, да, именно читаемость, ради нее и стоит использовать битовые поля. Да и не надо их так бояться...

Читаемость да, но

Цитата

...если не писать все поля в одну строку, то число ошибок c ними стремиться к нулю...

есть такие регистры в периферии, в которые нельзя писать раздельными командами. И тут явно придется юзать define-ы битов. А значит для унификации их все (все биты периферии) придется определить (описать) в заголовочном файле (или взять готовый), таким образом дублировав работу по определению структур с битовыми полями и define-альтернатив. Получается, бессмысленная какая-то работа, проще оставить что-то одно, и пока define-ам нет альтернатив по применимости и универсальности, для описания битов периферии самым гибким вариантом остаются именно они.

Share this post


Link to post
Share on other sites
3 часа назад, Arlleex сказал:

у способа с define-ми это потенциальная громоздкость и глобальность ("замусоривание" глобального пространства имен) идентификаторов, например, DBGMCU_IDCODE_DEV_ID; у способа с битовыми полями "видимым" глобальным символом было бы только определение DBGMCU, а внутреннее устройство "наружу" не видно

А зачем пользоваться #define? Я в таких случаях всегда использую список битовых полей определённых через enum {} внутри структуры. И нет "замусоривания глобального пространства".

Share this post


Link to post
Share on other sites
20 minutes ago, Arlleex said:

Читаемость да, но

есть такие регистры в периферии, в которые нельзя писать раздельными командами. И тут явно придется юзать define-ы битов. А значит для унификации их все (все биты периферии) придется определить (описать) в заголовочном файле (или взять готовый), таким образом дублировав работу по определению структур с битовыми полями и define-альтернатив. Получается, бессмысленная какая-то работа, проще оставить что-то одно, и пока define-ам нет альтернатив по применимости и универсальности, для описания битов периферии самым гибким вариантом остаются именно они.

Для регистров периферии уже существуют готовые h-файлы от производителя камня. Вполне годные для всей низкоуровневой "ботвы".

Я лишь создал вокруг этих регистров свои библиотеки на плюсах, которые обобщают таймера, ацп, can. Т.е. вся "тряхомуть" с дейфайнами отлажена в отдельных файлах, зафиксена в базе и забыта на время что там внутри.

Битовые поля это уже для юзер-кода, так сказать (протоколы там всякие, где это реально оч удобно). А дефайны в юзер коде лишь его захламляют. Избегаю максимально.

 

зы. Опять-таки что не тема, то разговор уходит в личную вкусовщину в том или ином виде :boredom:

Но суть такова - битовые поля в прямых руках не создают никаких проблем.

Не утверждаю, что у меня прямые руки, сужу лишь о сугубо личном опыте с битовыми полями.

Share this post


Link to post
Share on other sites
3 часа назад, haker_fox сказал:

либо иногда я пишу "в лоб", поскольку считаю, что проще заглянуть в даташит, если возникнет необходимость разобраться, а что же конфигурируется.


    static const uint32_t TIEF_ALL_CH_MASK = ( 1UL << ( 4 * 0 + 3 ) )
                                             | ( 1UL << ( 4 * 1 + 3 ) )
                                             | ( 1UL << ( 4 * 2 + 3 ) )
                                             | ( 1UL << ( 4 * 3 + 3 ) )
                                             | ( 1UL << ( 4 * 4 + 3 ) )
                                             | ( 1UL << ( 4 * 5 + 3 ) )
                                             | ( 1UL << ( 4 * 6 + 3 ) );

Хотя признаюсь, что данный вариант не очень хорош в плане понимания.

Откройте для себя enum! Вместо 0, 1, 2, 3, ... И будет и читаемо и не менее оптимально.

 

35 минут назад, Arlleex сказал:

И тут явно придется юзать define-ы битов.

Честно говоря - совершенно не понимаю вашей тяги к #define...  :unknw:

Классическая структура с бит-картами у меня:

struct FaultState {
  enum { //список причин аварий
    GF_swUL,          //авария драйвера нижнего силового ключа фазы U
    GF_swUH,          //авария драйвера верхнего силового ключа фазы U
    GF_swVL,          //авария драйвера нижнего силового ключа фазы V
    GF_swVH,          //авария драйвера верхнего силового ключа фазы V
    GF_swWL,          //авария драйвера нижнего силового ключа фазы W
    GF_swWH,          //авария драйвера верхнего силового ключа фазы W
    GF_sw,            //общая авария от платы драйверов (объединение всех GF_sw..)
    GF_emergency,     //нажатие кнопки аварийной остановки PIN_EMERGENCY
    GF_amperU,        //авария по току силовых ключей фазы U
    GF_amperV,        //авария по току силовых ключей фазы V
    GF_amperW,        //авария по току силовых ключей фазы W
    GF_amperDc,       //авария по току батареи
    GF_pedal,         //авария датчиков педалей (газа или тормоза)
    GF_rotor,         //авария датчика углового положения ротора
    GF_remPrnd,       //авария дистанционного управления направлением
    GF_remReduAccBrk, //авария дистанционного управления ослаблением разгона и торможения
    GF_remReduAcc,    //авария дистанционного управления ослаблением разгона
    GF_remReduBrk,    //авария дистанционного управления ослаблением торможения
    GF_voltDcL,       //авария по напряжению батареи, ниже допустимого
    GF_voltDcH,       //авария по напряжению батареи, выше допустимого
    GF_volt,          //аварии по каналам напряжения (VOLT_CH_MON_n штук)
    GF_temp = GF_volt + VOLT_CH_MON_n, //аварии по температурным каналам (TEMP_CH_n штук)
    GF_n = GF_temp + TEMP_CH_n,
    GF_hall = GF_rotor
  };
  enum {
    LATCH_DEF = 1ull << GF_swUL | 1ull << GF_swUH | 1ull << GF_swVL | 1ull << GF_swVH | 1ull << GF_swWL | 1ull << GF_swWH |
    1ull << GF_sw | 1ull << GF_emergency};
  u64 avar;       //биткарта текущих состояний аварий
  u64 avarMsk;    //биткарта текущих состояний аварий с наложенной (атомарно) маской
  u64 msk;        //биткарта маски аварий; биты==0 - игнорировать соотв.сигнал аварии
  u64 latch;      //биткарта защёлкиваемых аварий (каждый бит ==1 - соотв.авария должна защёлкиваться до сброса)
...
};

Как видно - #define-ами и не пахнет.  :unknw:

Share this post


Link to post
Share on other sites
23 минуты назад, jcxz сказал:

Я в таких случаях всегда использую список битовых полей определённых через enum {} внутри структуры...

Можно пример? Я понимаю (примерно) что Вы имеете ввиду, но не понимаю где там гибкость как у define-ов.

Share this post


Link to post
Share on other sites
5 минут назад, Arlleex сказал:

Можно пример? Я понимаю (примерно) что Вы имеете ввиду, но не понимаю где там гибкость как у define-ов.

Я же привёл...

Share this post


Link to post
Share on other sites
6 hours ago, jcxz said:

Откройте для себя enum! Вместо 0, 1, 2, 3, ... И будет и читаемо и не менее оптимально.

Да можно как угодно... Я не фанатик только одного или двух способов)

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.