【问题标题】:Big endian byte array to small endian struct elements in C, armC,arm中的大端字节数组到小端结构元素
【发布时间】:2020-11-08 11:41:09
【问题描述】:

在我的应用程序中,微控制器 stm32f103 通过 USART 接收固定长度的消息,它们包含 gps 速度,这是大端数据。但是结构中的元素是小端的。有没有什么办法不用手动写正确的方式?

typedef struct {
    uint32_t test1;
    uint16_t test2;
}Mst;

uint8_t myArray[6] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };

void main()
{
    Mst * myStruct_p = (Mst)myArray;
}

但之后myStruct_p->test1等于0x030201,但应该是0x010203myStruct_p->test2等于0x0605,但应该是0x0506

【问题讨论】:

  • 无论如何您都必须调整字节,并且可能存在对齐问题,那么为什么不使用始终有效的可靠、可移植的代码呢?转移和或。尝试使用 ghee whiz 语言魔法不会使机器代码更好。
  • 我忘了,struct 是用打包属性 typedef struct attribute__((__packed)) 定义的,此时它就像我上面写的那样工作

标签: c struct arm stm32 endianness


【解决方案1】:

由于它是 ARM-Cortex M3,我们可以使用特殊的处理器指令。 ARM CMSIS 有一个非常方便的内部函数__REV__REV16,它们实际上编译为单个机器代码指令。

typedef union 
{
    struct 
    {
        uint32_t test1;
        uint16_t test2;
    };
    uint8_t bytes[6];
}Mst;

Mst mst = {.bytes = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }};

void main()
{
    mst.test1 = __REV(mst.test1);
    mst.test2 = __REV16(mst.test2);
}

【讨论】:

  • 任何评论静默DV-ter?
  • 对不起,我对 swd 程序员有一些小问题:D,你的答案看起来是最好的选择,我明天试试
【解决方案2】:

以这种方式投射是行不通的。

您可以进行“反序列化”操作。

虽然这可能比其他一些方法慢,但它可以让您更好地控制协议(例如struct 成员顺序不必遵循协议)并且,结构中可能存在填充,这将如果我们在结构的末尾添加(例如)uint32_t test3;,就会显示出来。

代码如下:

#include <stdio.h>
#include <stdint.h>

typedef struct {
    uint32_t test1;
    uint16_t test2;
} Mst;

uint8_t myArray[6] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };

uint32_t
get32(uint8_t **base)
{
    uint8_t *ptr;
    uint32_t val = 0;

    ptr = *base;

    for (int len = sizeof(uint32_t);  len > 0;  --len) {
        val <<= 8;
        val |= *ptr++;
    }

    *base = ptr;

    return val;
}

uint16_t
get16(uint8_t **base)
{
    uint8_t *ptr;
    uint16_t val = 0;

    ptr = *base;

    for (int len = sizeof(uint16_t);  len > 0;  --len) {
        val <<= 8;
        val |= *ptr++;
    }

    *base = ptr;

    return val;
}

int
main(void)
{
    Mst myS;

    uint8_t *arr = myArray;

    myS.test1 = get32(&arr);
    myS.test2 = get16(&arr);

    printf("test1=%8.8X test2=%4.4X\n",myS.test1,myS.test2);

    return 0;
}

更新:

是的,伙计们,他们会工作,但我想尽可能少地使用处理器。这是相当手动地将字节放入正确的顺序。同样,过程Mst * struct_p = (Mst*)myArray 工作安全,因为在定义struct 时我使用__attribute__((packed)),只是忘了写这个

我打算提及/建议packed 作为一种可能性。

在任何一种情况下,您都可以使用 [在 GNU 下]:byteswap.h 来获取 bswap_*。 Endian 交换非常普遍,因此这些为给定的拱 [高度] 优化。他们甚至可以调用编译器内部函数(例如__builtin_bswap32),这些函数利用arch 具有的任何特殊指令(例如x86 具有bswap 指令,arm 具有rev16)。

所以,你可以做 [i.e.将for 循环替换为](例如)bswap_*:

#include <stdio.h>
#include <stdint.h>
#include <byteswap.h>

typedef struct {
    uint32_t test1;
    uint16_t test2;
    uint32_t test3;
} __attribute__((__packed__)) Mst;

uint8_t myArray[] = {
    0x01, 0x02, 0x03, 0x04,
    0x05, 0x06,
    0x07, 0x08, 0x09, 0x0A
};

void
getall(Mst *myS)
{

    myS->test1 = bswap_32(myS->test1);
    myS->test2 = bswap_16(myS->test2);
    myS->test3 = bswap_32(myS->test3);
}

int
main(void)
{
    Mst *myS = (Mst *) myArray;

    getall(myS);

    printf("test1=%8.8X test2=%4.4X test3=%8.8X\n",
        myS->test1,myS->test2,myS->test3);

    return 0;
}

【讨论】:

  • 你错了。在这种情况下,指针双关语将起作用。问题不是关于双关语,而是关于字节顺序的变化
  • 不定义标准类型。只需包含stdint.h
  • @P__J__ 我没有“错”,因为我确实没有说你不得不这样做,只是你“可以”这样做.
  • Casting in that way won't work.。 1. 在这种情况下,双关语将起作用。 2.这里没有“铸造”。
  • @P__J__ 嗯? Mst * myStruct_p = (Mst)myArray; 不是演员表吗?而且,由于我提到的对齐问题(re.test3),在一般情况下双关语不是一件好事。
【解决方案3】:

由于对齐问题和结构填充,将结构指针覆盖到字节数组上可能不一定有效。

正确的方法是首先使用memcpy 复制元素:

Mst myStruct;
memcpy(&myStruct.test1, myArray, sizeof(myStruct.test1);
memcpy(&myStruct.test2, myArray + 4, sizeof(myStruct.test2);

然后使用 ntohsntohl 分别将 16 位和 32 位值从大端格式转换为主机的字节序。

myStruct.test1 = ntohl(myStruct.test1);
myStruct.test2 = ntohs(myStruct.test2);

【讨论】:

    【解决方案4】:

    如果您只需要将 8 位数据转换为大端,假设数据格式将按顺序 (ABCD),如果最终结果将存储在 32 位变量中,如果最终结果将存储在 16 位变量中,则数据格式将是 (DCBA)然后格式化将 (AB) 和 (BA) 。

    因此,如果您知道您的 UART 数据以哪种格式传输,则可以通过一点位移技术来完成。

    让我们首先假设您的 UART 数据以小端格式输入,而您希望它采用大端格式,那么您可以这样做。

    //no structure required
    uint32_t myValue;
    uint16_t value;
    uint8_t myArray[4] = { 0x04, 0x03, 0x02, 0x01 }; 
    // little endian data for 32 bit
    
    uint8_t A[2] = {0x02 ,0x01};  // little endian data for 16 bit
    void main()
    {
        myValue = myArray[3]<<24 | myArray[2]<<16 | myArray[1]<<8 | myArray[0]; // result in big endian 32 bit value
    
       value = A[1]<<8 | A[0];  // result in big endian 16bit value
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-02-29
      • 2011-01-29
      • 1970-01-01
      • 2010-10-25
      • 2020-08-20
      • 2010-09-24
      • 2013-03-15
      • 1970-01-01
      相关资源
      最近更新 更多