在 Keil 中从 uint8_t 数组获取 uint16_t 值
Getting uint16_t value from uint8_t array in Keil
我尝试在不使用“<<”运算符的情况下从 UART 数据包中获取变量。
uint8_t buffer[8] = {0x11,0x22,0x33,0x44};
uint16_t val = *((uint16_t *)buffer);
如果我在 keil 中尝试上面的代码,它可以正常工作。当我在结构编译器中为数组尝试它时,它不会给出错误,但它会在运行时进入硬故障处理程序。
typedef struct
{
uint8_t address;
uint8_t opID;
uint8_t dataLen;
uint8_t data[250];
uint8_t crc[2];
}MODBUS;
MODBUS receivedData;
uint16_t val = *((uint16_t *)receivedData.data);
我也在在线 C 编译器中尝试了这个(结构中的数组)。它工作没有任何问题。我应该怎么做才能在keil中使用相同的东西?
使用 memcpy
通常是处理此类转换的正确方法,因此:
uint16_t target;
uint8_t source[]={1,2,3,4};
memcpy(&target, source, sizeof(target));
要将较短整数数组更改为较长整数数组,您必须相应地修改它:
uint16_t target[2]={0};
uint8_t source[4]={1,2,3,4};
memcpy(target, source, sizeof(target));
记得相应地调整尺寸。
*((uint16_t *)buffer);
有两个未定义的行为错误:
buffer
可能未对齐,转换为更大的指针类型可能导致访问未对齐或硬件 trap/exception.
- 该代码违反了指针取消引用类型系统,即所谓的“严格的别名违规”。 What is the strict aliasing rule? 这在嵌入式系统编译器中通常不是问题,但无论如何它仍然是未定义的行为,您不应该编写那样的代码。
此外,您还有一个潜在问题,即 UART 协议的网络字节序可能与 CPU 字节序不匹配。 What is CPU endianness? 大多数 UART 协议使用 Big Endian。
要解决这个问题,您可以使用另一个答案中提到的 memcpy
。或者更有效地,包装器 union
:
typedef union
{
uint8_t u8 [8];
uint16_t u16 [4];
} uart_buf_t;
这解决了错位和严格别名的问题,但它不会解决潜在的字节顺序不匹配问题。要解决这个问题,您需要改用位移位。
注意:所讨论的目标是 Cortex®-M0+ 的变体,它支持未对齐的访问。因此,以下假设无关紧要。
硬故障最可能的原因是:
您正在使用不支持未对齐访问的内核。
例如,皮质 M0 变体。
建议:
一步一步调试,查看变量的内存位置,特别是:
的内存位置
receivedData.data
尝试通过字节对齐访问该位置,检查是否会发生硬故障。
uint8_t val = *((uint8_t *)receivedData.data);
如果我猜对了,像下面这样修改结构体定义,看看是否能解决问题。
typedef struct {
uint8_t address;
uint8_t opID;
uint8_t dataLen;
uint8_t dummy_byte;
uint8_t data[250];
uint8_t crc[2];
}MODBUS;
我尝试在不使用“<<”运算符的情况下从 UART 数据包中获取变量。
uint8_t buffer[8] = {0x11,0x22,0x33,0x44};
uint16_t val = *((uint16_t *)buffer);
如果我在 keil 中尝试上面的代码,它可以正常工作。当我在结构编译器中为数组尝试它时,它不会给出错误,但它会在运行时进入硬故障处理程序。
typedef struct
{
uint8_t address;
uint8_t opID;
uint8_t dataLen;
uint8_t data[250];
uint8_t crc[2];
}MODBUS;
MODBUS receivedData;
uint16_t val = *((uint16_t *)receivedData.data);
我也在在线 C 编译器中尝试了这个(结构中的数组)。它工作没有任何问题。我应该怎么做才能在keil中使用相同的东西?
使用 memcpy
通常是处理此类转换的正确方法,因此:
uint16_t target;
uint8_t source[]={1,2,3,4};
memcpy(&target, source, sizeof(target));
要将较短整数数组更改为较长整数数组,您必须相应地修改它:
uint16_t target[2]={0};
uint8_t source[4]={1,2,3,4};
memcpy(target, source, sizeof(target));
记得相应地调整尺寸。
*((uint16_t *)buffer);
有两个未定义的行为错误:
buffer
可能未对齐,转换为更大的指针类型可能导致访问未对齐或硬件 trap/exception.- 该代码违反了指针取消引用类型系统,即所谓的“严格的别名违规”。 What is the strict aliasing rule? 这在嵌入式系统编译器中通常不是问题,但无论如何它仍然是未定义的行为,您不应该编写那样的代码。
此外,您还有一个潜在问题,即 UART 协议的网络字节序可能与 CPU 字节序不匹配。 What is CPU endianness? 大多数 UART 协议使用 Big Endian。
要解决这个问题,您可以使用另一个答案中提到的 memcpy
。或者更有效地,包装器 union
:
typedef union
{
uint8_t u8 [8];
uint16_t u16 [4];
} uart_buf_t;
这解决了错位和严格别名的问题,但它不会解决潜在的字节顺序不匹配问题。要解决这个问题,您需要改用位移位。
注意:所讨论的目标是 Cortex®-M0+ 的变体,它支持未对齐的访问。因此,以下假设无关紧要。
硬故障最可能的原因是: 您正在使用不支持未对齐访问的内核。 例如,皮质 M0 变体。
建议:
一步一步调试,查看变量的内存位置,特别是:
的内存位置receivedData.data
尝试通过字节对齐访问该位置,检查是否会发生硬故障。
uint8_t val = *((uint8_t *)receivedData.data);
如果我猜对了,像下面这样修改结构体定义,看看是否能解决问题。
typedef struct {
uint8_t address;
uint8_t opID;
uint8_t dataLen;
uint8_t dummy_byte;
uint8_t data[250];
uint8_t crc[2];
}MODBUS;