Почему не удается скопировать структуру путем прямого назначения?


Я сталкиваюсь с жестким исключением ошибки при копировании некоторых данных на микроконтроллере из одной структуры в другую. Я пробовал разные реализации, которые должны были делать все то же самое. Смотрите мои строки кода:

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 2016-0919 .

Вот минимальный пример кода, который (надеюсь) показывает проблему. Структура данных 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);
}

Почему не удается скопировать структуру путем прямого назначения?

2 10

2 ответа:

При использовании нестандартного #pragma pack вы заставляете компилятор хранить структуру без заполнения. Члены структуры перед data находятся в группах 4+4+3, затем data в байте 11, который смещен.

Таким образом, вы заставляете data всегда выделяться несогласованно, что может вызвать аппаратные исключения на некоторых процессорах, если к нему обращаются как к слову (32 бита). Код msg.data = *data;, созданный компилятором, может предполагать, что при копировании двух структур они всегда правильно выровнены, как это обычно и происходит случай. И наиболее эффективная реализация копии будет работать с 32-битными фрагментами данных, так что это то, что он будет использовать.

Вопрос в том, почему эта структура изначально упакована, поскольку она не может быть ни отображением аппаратного регистра, ни отображением протокола данных. Такие вещи, как CAN-bus IDE и RTR-это всего лишь отдельные биты; я очень сомневаюсь, что любой контроллер CAN резервирует для этого целый 8-битный регистр. Например, контроллер ST "bxCAN" помещает их как отдельные биты в CAN_TIxR Регистрация (Техас можно зарегистрировать идентификатор почтового ящика). Любой другой контроллер CAN на рынке будет вести себя аналогично.

Что касается самого кадра CAN, вы не можете напрямую сопоставить его с памятью. Контроллер CAN захватит необработанный кадр CAN и поместит его в свои собственные регистры, отображенные в памяти.

Либо переделайте эту структуру без заполнения, либо используйте фактические регистры контроллера CAN, как это предусмотрено вашим оборудованием.

Я обнаружил, что существует регистр CFSR, который содержит информацию о типе жесткого исключения ошибки. Регистр показывает, что бит № 24 установлен. Руководство по программированию АРМ PM0214 гласит на странице 221:

Бит 24 UNALIGNED: ошибка использования Unaligned access. Включить улавливание не выровненный доступ, установив бит UNALIGN_TRP в CCR на 1, см. Регистр конфигурации и управления (CCR) на стр. 214. Несогласованный LDM, СТМ, МЦРУ, и strd успешно инструкции всегда виноват независимо от того, установка UNALIGN_TRP.

0: Нет ошибки доступа без выравнивания или без выравнивания перехват доступа не включен

1: Процессор сделал невыровненный доступ к памяти.

Это действительно соответствует ответу @Lundin.