我在将微控制器上的一些数据从一个结构复制到另一个结构时遇到了硬错误异常。我尝试了不同的实现,它们应该完全相同。请参阅我的代码行:
memcpy(&msg.data, data, 8);
memcpy(&msg.data, data, sizeof(*data));
memcpy(&msg.data, data, sizeof(msg.data));
msg.data = *data; // Hard Fault
前三行工作得很好。最后一个以硬故障异常结束。具有memcpy
的行的程序集是相同的。直接分配的程序集不同:
memcpy(&msg.data, data, sizeof(msg.data));
800c480: f107 030c add.w r3, r7, #12
800c484: 330b adds r3, #11
800c486: 2208 movs r2, #8
800c488: 6879 ldr r1, [r7, #4]
800c48a: 4618 mov r0, r3
800c48c: f7f4 f82e bl 80004ec <memcpy>
msg.data = *data; // Hard Fault
800c490: 687b ldr r3, [r7, #4]
800c492: f107 0217 add.w r2, r7, #23
800c496: cb03 ldmia r3!, {r0, r1}
800c498: 6010 str r0, [r2, #0]
800c49a: 6051 str r1, [r2, #4]
我正在使用GNU Arm Embedded Toolchain 5.4.1 20160919。
这是一个最小的代码示例(希望)显示问题。数据结构msg_t
必须使用packed
属性来匹配某些硬件寄存器。在微控制器上,此代码以msg.data = *data;
#include <stdint.h>
#include <string.h>
#include <stdio.h>
typedef struct canData_s {
uint8_t d1;
uint8_t d2;
uint8_t d3;
uint8_t d4;
uint8_t d5;
uint8_t d6;
uint8_t d7;
uint8_t d8;
} canData_t;
#pragma pack(push, 1)
typedef struct msg_s {
uint32_t stdId;
uint32_t extId;
uint8_t ide;
uint8_t rtr;
uint8_t dlc;
canData_t data; // 8 Bytes
uint8_t navail; // not available
uint32_t timestamp;
} msg_t;
#pragma pack(pop)
void setData(canData_t *data) {
msg_t msg;
msg.data = *data;
// Do something more ...
printf("D1:%d", msg.data.d1);
// ...
}
int main() {
canData_t data;
memset(&data, 0, 8);
setData(&data);
}
为什么通过直接分配复制结构会失败?
答案 0 :(得分:9)
当您使用非标准data
时,强制编译器存储结构而不进行任何填充。 data
之前的结构成员是4 + 4 + 3的组,然后是字节11的data
,它是未对齐的。
因此,强制msg.data = *data;
总是被分配未对齐,如果以字(32位)访问,可能会导致某些CPU出现硬件异常。编译器生成的代码order by to_number(regexp_substr(location_id, '\d+$'))
可能会假设当您复制两个结构时,它们总是正确对齐,通常就是这种情况。并且最有效的副本实现将适用于32位数据块,因此它将使用它。
这里的问题是为什么这个结构被打包开始,因为它既不是硬件寄存器映射也不是数据协议映射。像CAN-bus IDE和RTR这样的东西只是单个位;我非常怀疑任何CAN控制器都会保留一个完整的8位寄存器。例如,ST的“bxCAN”控制器将它们作为单独的位放在CAN_TIxR寄存器(CAN TX邮箱标识寄存器)中。市场上的每个其他CAN控制器的行为都相似。
至于CAN帧本身,你不能直接对它进行内存映射。 CAN控制器将获取原始CAN帧并将其放入其自己的存储器映射寄存器中。
重新制作此结构而不填充或使用硬件提供的实际CAN控制器寄存器。
答案 1 :(得分:0)
我发现有一个CFSR寄存器,其中包含有关硬故障异常类型的信息。寄存器显示位24没有设置。 ARM编程手册PM0214在第221页说:
Bit 24 UNALIGNED:未对齐的访问使用故障。启用陷印 通过将CCR中的UNALIGN_TRP位设置为1来进行未对齐访问,请参阅 第214页的配置和控制寄存器(CCR)。未对齐的LDM, 无论是什么,STM,LDRD和STRD指令总是出错 设置UNALIGN_TRP。
0:没有未对齐的访问错误或未对齐 访问陷阱未启用
1:处理器未对齐 记忆访问。
这真的与@Lundin的答案相符。