[STM32U5] 让__packed 属性在 STM32CubeIDE 中使用的 GCC 编译器中生效

[复制链接]
369|5
WHALEE 发表于 2025-10-23 15:26 | 显示全部楼层 |阅读模式
我有一个结构体,由编码器的供应商提供。
数据大多是 16 位字。有时是带位域的 16 位字,有时是 32 位变量,有时是 48 位变量或 64 位变量。
我创建建了这个个结构体,是 44 个 16 位字,也就是 88 字节。但我检查大小时,它却是 100 字节。这表明结构体中插入了填充字节。幸好有__packed 属性这种东西。
我添加了__packed 属性,却收到了 “packed 属性被忽略” 的警告。
后来我意识到需要在所有地方都加上 packed。于是我在能想到的所有地方都加了,之后就没有再收到警告了!
但是,结构体的大小仍然是 100 字节,而不是 88 字节。
代码如下:
typedef __packed union
{
        uint8_t byte;
        __packed struct
        {
                uint8_t start_address:4;
                uint8_t end_address:4;
        };
} OEM_COMP_MEMORY_STRUCT;


typedef __packed union
{
        uint16_t word[4];
        uint32_t dword[2];
        uint8_t  byte[8];
        OEM_COMP_MEMORY_STRUCT addresses[8];
} MEMORY_ALLOCATION_STRUCT;
typedef __packed union
{
        uint8_t  byte[88];
        uint16_t word[44];
        __packed struct {
                uint16_t MASK0;          // 4
                uint16_t MASK1;                // 5
                uint16_t MASK2;                // 6
                uint16_t MASK3;                // 7
                uint16_t EnDat_version;        // 8
                MEMORY_ALLOCATION_STRUCT OEM;        // 9, 10, 11, 12
                uint16_t transfer_format_position;        // 13
                uint16_t Encoder_type;        // 14
                uint32_t Signal_period;        // 15, 16
                uint16_t Number_of_revolutions;        // 17
                uint16_t Distance_of_reference_marks;        // 18
                uint16_t Position_first_reference_mark;        // 19
                uint32_t Measuring_step;        // 20 21
                uint32_t Datum_shift;                // 22 23
                ID_NUMBER_STRUCT id_number;        // 24, 25, 26
                SERIAL_NUMBER_STRUCT serial_number;        // 27, 28, 29
                DIRECTION_ROTATION_STRUCT direction_of_rotation; // 30
                EXTERNAL_COMMISSIONING_DIAG_STRUCT external_commissioning_diagnostics; // 31
                uint16_t  Maximum_velocity;        // 32
                VELOCITY_RANGE_STRUCT Velocity_range1;        // 33
                VELOCITY_RANGE_STRUCT Velocity_range2;        // 34
                ERROR_MESSAGE_STRUCT Support_of_error_message1;// 35
                WARNINGS_STRUCT Support_of_warnings; // 36
                ENDAT_COMMAND_SET_STRUCT EnDat_command_set; // 37
                VALUE_UNITS_STRUCT measuring_length; // 38
                VALUE_UNITS_STRUCT maximum_processing_time; // 39
                TWO_BYTES_STRUCT EnDat_ordering; // 40
                uint16_t word41;
                uint16_t word42;
                uint16_t word43;
                uint16_t word44;
                uint16_t word45;
                uint16_t word46;
                uint16_t checksum; // simple sum of the bytes in the structure word[43] = sum(word[0]...word[42] & 0xFFFF
        };

} PARM_ENCODER_MANUFACTURER_STRUCT;  // *PEM

PARM_ENCODER_MANUFACTURER_STRUCT *PEM = ((PARM_ENCODER_MANUFACTURER_STRUCT *) &EMEM.NVMword[4]);

void check_PEM(void)
{
        char buf[100];
        uint16_t i;
        for (i=0;i<44;i++)
        {
                PEM->word = i + 4;
        }

        write_debug_str("In check_PEM\r\n");

        sprintf(buf,"MASK0: %u, 0x%04X, should be 4\r\n", PEM->MASK0, PEM->MASK0);
    write_debug_str(buf);

    sprintf(buf,"EnDat_version: %u, 0x%04X, should be 8\r\n", PEM->EnDat_version, PEM->EnDat_version);
    write_debug_str(buf);

    sprintf(buf,"OEM.word[0]: %u, 0x%04X, should be 9\r\n", PEM->OEM.word[0], PEM->OEM.word[0]);
        write_debug_str(buf);

    sprintf(buf,"OEM.word[1]: %u, 0x%04X, should be 10\r\n", PEM->OEM.word[1], PEM->OEM.word[1]);
        write_debug_str(buf);

    sprintf(buf,"OEM.word[2]: %u, 0x%04X, should be 11\r\n", PEM->OEM.word[2], PEM->OEM.word[2]);
        write_debug_str(buf);

    sprintf(buf,"OEM.word[3]: %u, 0x%04X, should be 12\r\n", PEM->OEM.word[3], PEM->OEM.word[3]);
        write_debug_str(buf);

        sprintf(buf,"transfer_format_position: %u, 0x%04X, should be 13\r\n", PEM->transfer_format_position, PEM->transfer_format_position);
    write_debug_str(buf);

        sprintf(buf,"Datum_shift: %lu, 0x%08lX, should be 22 23\r\n", PEM->Datum_shift, PEM->Datum_shift);
    write_debug_str(buf);

}

运行程序该函数输出如下:
In check_PEM


MASK0: 4, 0x0004, should be 4


EnDat_version: 8, 0x0008, should be 8


OEM.word[0]: 10, 0x000A, should be 9


OEM.word[1]: 11, 0x000B, should be 10


OEM.word[2]: 12, 0x000C, should be 11


OEM.word[3]: 13, 0x000D, should be 12


transfer_format_position: 14, 0x000E, should be 13


Datum_shift: 1638424, 0x00190018, should be 22 23



问题出在 MEMORY_ALLOCATION_STRUCT 类型的 OEM 变量存在内存对齐问题;
而 __PACKED 关键字本应解决这个问题……

海滨消消 发表于 2025-10-23 16:06 | 显示全部楼层
编译器警告 "packed attribute is ignored",说明 __packed 未正确应用。
豌豆爹 发表于 2025-10-23 17:07 | 显示全部楼层
强制所有嵌套结构体使用packed
豌豆爹 发表于 2025-10-23 19:08 | 显示全部楼层
手动指定内存布局,如果packed无法完全解决问题,可以改用 uint16_t 数组直接映射内存,然后通过指针访问字段
麻花油条 发表于 2025-10-23 19:08 | 显示全部楼层
__packed 必须正确定义,并应用于所有嵌套结构体。
classroom 发表于 2025-10-23 20:09 | 显示全部楼层
改用uint16_t数组手动映射内存是最可靠的方法。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

61

主题

61

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部