Как выровнять по байтам буфер объединения с адресом RAM

В настоящее время я кодирую на C и использую микроконтроллер nRF51 от NORDIC (32-разрядный ARM Cortex M0).

Я хотел бы отправить буфер союза по радио. Для этого я должен указать адрес буфера объединения в PACKETPTR. В противном случае адрес, указанный в "PACKETPTR", должен быть, согласно ссылке на руководство: адрес ОЗУ с выравниванием по байтам. Если он не выровнен, ему удается взять следующий ближайший, но таким образом получатель получит неполный буфер и «фиктивные» данные...

Как вы можете догадаться, мой буфер не выровнен по байтам. Вот соответствующий союз и глобальная переменная.

Элементы моего объединения должны быть упакованы так, чтобы они могли поместиться в 7 байтов;

typedef union
{   
    uint8_t buf[7];  //The buffer I give to PACKETPTR
    __packed struct 
    {
        uint8_t             a[2];
        uint8_t             b;
        uint16_t            c : 10;
        uint16_t            d : 6;  
        uint16_t            e : 14;
        uint16_t            f : 2;
    }parts;
}adv_t;

adv_t m_adv; //The global variable

...

//Later in the code to set PACKETPTR

config_tx(ADV_PIPE, m_adv.buf);

...

void __INLINE config_tx(uint8_t tx_pipe, uint8_t* payload)
{
   NRF_RADIO->TXADDRESS = tx_pipe;
   NRF_RADIO->PACKETPTR = (uint32_t)payload;
}

В начале я добавил квалификатор __packed перед typedef union. Но это было так, как если бы m_adv был «упакован» некоторыми предыдущими данными, поэтому адрес buf не был выровнен по байтам с адресом оперативной памяти.

__packed typedef union /* Previous definition */
{   
    uint8_t buf[7];
    __packed struct 
    {
        ...
    }parts;
}adv_t; 

Поэтому я удалил его, и тогда адрес buf был правильным (т.е. байт выровнен с адресом RAM). Я думал, что это было решением, но через несколько минут он снова стал смещенным... Мои вопросы: влияет ли квалификатор __packed также на buf из-за объединения? есть ли способ выровнять адрес buf из моей глобальной переменной? Или любое предложение?

Спасибо


person TRandom    schedule 20.01.2016    source источник
comment
Что произойдет, если вы включите в союз long unused члена?   -  person 2501    schedule 20.01.2016
comment
О чем ты говоришь ? Адрес ОЗУ по определению всегда выровнен по байтам. У вас не может быть дробного адреса. Когда вы говорите о неправильном выравнивании, вы имеете в виду неправильно упакованный? Похоже, весь ваш вопрос об упаковке, а не о выравнивании, но в нынешнем виде ваш вопрос бессмыслен и труднопонятен.   -  person ElderBug    schedule 20.01.2016
comment
Когда в документации говорится, что адрес выровнен по байтам, это просто означает, что выравнивание не требуется, подойдет любой адрес.   -  person ElderBug    schedule 20.01.2016
comment
@ElderBug Адрес оперативной памяти будет, но, например. madv.parts.d нет (поскольку это часть битового поля). Можно было бы подумать, что в упакованном struct { char a:2; char b; }; b тоже не выровнено по байтам, потому что a:2 не заканчивается на границе байта. Но это не так, здесь b тоже будет выровнено по байтам.   -  person Ctx    schedule 20.01.2016
comment
@Ctx Конечно, члены битового поля могут не выравниваться по границам байтов, но я не об этом. Не существует такой вещи, как адрес, не выровненный по байтам. Вы не можете взять адрес члена битового поля (попробуйте). ОП неправильно понял это предложение документации и подумал, что это проблема, хотя на самом деле речь идет об упаковке.   -  person ElderBug    schedule 20.01.2016
comment
Обязательно добавьте что-то вроде _Static_assert(sizeof(m_adv.buf) == sizeof(m_adv.parts); "Error: padding detected");   -  person Lundin    schedule 20.01.2016
comment
Байт выровнен? Это кажется странным, если только оно не включает определенное количество байтов выравнивания...   -  person David Hoelzer    schedule 20.01.2016
comment
Надеюсь, вы не ожидаете, что ваши битовые поля будут сопоставляться с определенными байтами вашего члена buf. Это чрезвычайно зависит от реализации.   -  person Andrew Henle    schedule 20.01.2016


Ответы (3)


Похоже, ваша проблема связана с упаковкой, а не с выравниванием адреса ОЗУ (упаковка играет с выравниванием, но само выравнивание в вашем случае не имеет значения).

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

typedef union
{   
    uint8_t buf[7];
    struct // not here
    {
        uint8_t             a[2];
        uint8_t             b;
        uint16_t            c : 10;
        uint16_t            d : 6;  
        uint16_t            e : 14;
        uint16_t            f : 2;
    } __packed parts; // here, __packed or __attribute__((packed))

}adv_t;

После b был вставлен дополнительный байт, чтобы выровнять следующие uint16_t по 16-битным границам.

Обратите внимание, что вам не нужно создавать объединение, если вам не нужен доступ к отдельным байтам. Вы можете просто использовать структуру и передать адрес структуры.

person ElderBug    schedule 20.01.2016

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

GCC, например, определяет атрибуты: _attribute__((aligned(4)))
Visual Studio имеет другой метод __declspec(align(4)).

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

person vlad_tepesch    schedule 20.01.2016

Спасибо за ваши ответы.

Члены моей структуры идеально вписываются в buf, как я и хотел (без заполнения, MSB/LSB размещены так, как я ожидал, и т. д.), и я не имел в виду, что хочу получить доступ к определенным членам битового поля.

Я недостаточно подробно описал, но проблема пошла от ресивера.

Я получил на отл. 0xEF 0x00 0xFC 0xB2 0x38 0x00 0xB6 вместо 0xCD 0xAB 0xEF 0x00 0xFC 0xB2 0x38 (bufcontent).

Итак, я подумал, что проблема была в адресе buf, а в предложении Руководства говорилось об «адресе ОЗУ» для PACKETPTR. Я неправильно его понял. Проблема фактически исходила из обработки кадров моего приемника... извините!!

person TRandom    schedule 20.01.2016