C - 在u16_array中转换结构,其中每个uint16_t仅表示一个字节

C - convert a struct in a u16_array, where each uint16_t represents only a byte

提问人:HansPeterLoft 提问时间:7/5/2023 更新时间:7/5/2023 访问量:119

问:

我在德州仪器 C2000 微控制器上有一个非常讨厌的案例,它只能代表uin16_t而不是uint8_t。现在我有一个如下所示的结构,其中每个uint16_t仅表示一个字节的数据,这些数据将被写入外部 EEPROM:

typedef struct
{
    uint32_t a;
    uint32_t b;
    uint32_t c;
    uint32_t d;
} MCL_Struct_t;

MCL_Struct_t struc_write = {0};
MCL_Struct_t struc_read = {0};
uint16_t *struc_write_handle = ((uint16_t *)&struc_write);
uint16_t *struc_read_handle = ((uint16_t *)&struc_read);

struc_write.a = 0x11112222;
struc_write.b = 0x11113333;
struc_write.c = 0x11114444;
struc_write.d = 0x11115555;

//call eeprom write
EEPROM_writeTransaction(eepromAddr, struc_write_handle, sizeof(MCL_Struct_t));

//read back eeprom
EEPROM_readTransaction(eepromAddr, sizeof(MCL_Struct_t));

//copy to struc_read
memcpy(struc_read_handle, &SPI_DMA_Handle.pSPIRXDMA->pbuffer[0], sizeof(MCL_Struct_t));

由于 SPI 只能按字节传输,因此uint16_t数据作为字节处理,因此我只编写类似 struc_write.a = 0x00110022 等的内容。

我怎样才能使我的句柄按字节指向数据?

c 结构 uint16

评论

1赞 tofro 7/5/2023
恐怕你搞砸了你的文章和你的示例之间的uint16_t和uint32_t(文本说结构中有uint16_t,代码说不是这样)。就像现在一样,不可能回答。
0赞 Aconcagua 7/5/2023
在我看来,您必须手动拆分数据,即有一个单独的数组并独立地将低/高字节复制到那里。
0赞 Gerhardh 7/5/2023
目前还不完全清楚您想要实现什么。您的 SPI 实现是否需要获取数组中的数据,但只发送每个条目的低 8 位?uint16_t

答:

0赞 Diram_T 7/5/2023 #1

如果底层架构在物理上无法进行字节寻址,那么您就不走运了,尽管从您的问题中尚不清楚是否如此。

如果你的微控制器可以按字节处理(据我所知,我认为应该是这样),那么它就是一个简单的强制转换来键入指针:char

char* addr = (char*) &struct_write.a;
char lower_byte = *addr; 
char upper_byte = *(addr + 1); 

否则,您可以使用移位操作来执行此操作,但请记住,这将占用两倍的内存:

uint6_t lower_byte = struct_write.a & (0x00FF);
uint6_t upper_byte = (struct_write.a >> 16)  & (0x00FF);

我建议您进一步研究微控制器的架构并检查指令集架构。

评论

0赞 Aconcagua 7/5/2023
“简单转换为字符类型指针”——在这种情况下很可能不是:没有可用的数据类型是 16 而不是 8 的有力指标(我知道那里有这样的 TI 器件......uint8_tCHAR_BITsizeof(char) == sizeof(int16_t)
0赞 Diram_T 7/5/2023
这就是为什么我在前面加上一个介词“如果微控制器可以按字节处理”:)
0赞 Aconcagua 7/5/2023
旁注:为什么要在十六进制文字周围加上括号?只是使代码更难阅读,没有任何好处。请不要这样做。实际上,在对无符号类型进行左移后,您根本不需要屏蔽最重要的位,无论如何它们总是为 0(但如果为负数,则在有符号时定义实现)。
0赞 Aconcagua 7/5/2023
实际上,在给定的情况下,您甚至不必为低部分遮盖高位,无论如何它们都会被忽略......如果 API 发生变化?好吧,无论如何,我们都会添加多余的字节(零或看似垃圾),因此无论有没有;),实现都会中断
0赞 Diram_T 7/5/2023
我同意它们没有带来任何好处,但它使人们更容易注意到以后可以用宏观定义替换的表达式部分。我也同意未签名的班次部分。
2赞 Aconcagua 7/5/2023 #2

看起来好像 EEPROM 实现是为了将单个字符视为 8 字节值,即使在您的系统上显然是 16(函数签名很可能基于或直接)。CHAR_BITcharunsigned char

在这种情况下,你就不能直接写入整个结构,尽管这样做无论如何都是有问题的,因为存在潜在的填充字节(通常,不是在具体情况下)和字节顺序问题。因此,我建议显式序列化,无论您在哪种架构上,您都可以获得定义良好的行为:

void serialize(uint32_t value, char buffer[])
{
// for little endian byte order:
    buffer[0] = value >>  0 & 0xff;
    buffer[1] = value >>  8 & 0xff;
    buffer[2] = value >> 16 & 0xff;
    buffer[3] = value >> 24 & 0xff;
//                       ^^
// revert order of shifts for big endian byte order
}

char* serialize_s(uint32_t value, size_t length, char buffer[])
{
    return length < 4 ? NULL : (serialize(value, buffer), buffer + 4);
}

uint32_t deserialize(char buffer[])
{
    uint32_t value
        = (uint32_t)(buffer[0] & 0xff) <<  0
        | (uint32_t)(buffer[1] & 0xff) <<  8
        | (uint32_t)(buffer[2] & 0xff) << 16
        | (uint32_t)(buffer[3] & 0xff) << 24;
    return value;
}

char* deserialize_s(uint32_t* value, size_t length, char buffer[])
{
    return !value || length < 4
        ? NULL : (*value = deserialize(buffer), buffer + 4);
}

char buffer[16];
serialize(struct_write.a, buffer +  0);
serialize(struct_write.b, buffer +  4);
serialize(struct_write.c, buffer +  8);
serialize(struct_write.d, buffer + 12);

// send buffer
// receive buffer

struct_write.a = deserialize(buffer +  0);
struct_write.b = deserialize(buffer +  4);
struct_write.c = deserialize(buffer +  8);
struct_write.d = deserialize(buffer + 12);

评论

0赞 Shark 7/5/2023
呵呵,我不太明白这部分你在哪里做,你是想 OR () 他们还是这样做,因为它是,它不会重新分配 4 次吗?还是我的 C 真的生锈了?deserialize(char buffer[])uint32_t value = highestByte = nextByte = nextByte = lowestByte;||=
0赞 Aconcagua 7/5/2023
@Shark 复制粘贴错误...当然是,谢谢你的提示。|
0赞 Shark 7/6/2023
啊,没问题,不客气。我只是想,也许这是一些奇怪的伎俩,我对:)一无所知
0赞 dalfaB 7/5/2023 #3

在这里,我们有===。
A 是一个字节,但一个字节是 2 个八位字节。
不可能在八位字节上有指针。如果必须发送/接收八位字节,则必须通过转换函数来实现。
sizeof(char)sizeof(uint16_t)sizeof(int)1char

typedef union BiOctets {
    uint16_t  u16;    // 2 octets
    unsigned char uc; // also 2 octets
    struct {          // also 2 octets
        unsigned char  high : 8;
        unsigned char  low  : 8;  // cannot take address of low
    //Note: depending en endianess of C2000, may be high and low must be swapped
    };
} BiOctets;

// access to an octet in an array of bytes
unsigned char  BiOctetToOctet( BiOctets bi[], int index ) {
    if ( index & 1 )
        return  bi[index>>1].low;     // odd values is low part
    else
        return  bi[index>>1].high;    // even values is high part
} // notice that returns a byte where high octet is null, it's an octet

// insert an octet in array of bytes
void  OctetToBiOctet( BiOctets bi[], int index, unsigned char value ) {
    if ( index & 1 )
        bi[index>>1].low = value;
    else
        bi[index>>1].high = value;
} // notice that 'value' is supposed to have null high octet, it's an octet

// then these function must be used each time you have to deal with octets