2016-09-08 1 views
2

Я хочу разбить регистр общего назначения на три отдельных регистра; два 8-битных регистра & один 16-разрядный регистр. Это подход, который я использую, но я думаю, что это может быть неправильно.Разбиение 32-битного регистра в байтах с переменным размером

typedef struct { 
     volatile uint8_t reg_0; 
     volatile uint8_t reg_1; 
     volatile uint16_t reg_2; 
} reg_split; 

#define REG_BASE (0xA040000C) 
#define REG ((reg_split *)REG_BASE) 

И это, как я доступа к регистру:

REG->reg_0 = 0xFF; 

Является ли это неправильный подход или их уборщик решение?

+1

подход нормально, если ABI гарантии вашей платформы правильный макет. В любом случае это зависит от платформы. например ARM AAPCS, например. Однако не делайте одиночные поля 'volatile'. Либо квалифицируйте 'struct', либо cast. – Olaf

+0

Похоже, что ответ был удален кем-то со всеми комментариями. Короче, это решение, которое я сейчас использую достаточно? – homeGrown

ответ

3

Что наиболее важно для всех видов сопоставлений памяти, подобных этим, заключается в том, что вы проверяете, что код C дает ожидаемый результат. Вы должны учитывать заполнение и выравнивание.

Некоторой незначительный NIT-кирка с вашим примером:

  • Всегда убедитесь, что все шестнадцатеричные литералы в коде есть тип, который имеет смысл. 0xA040000C в вашем случае имеет подписанный тип, скорее всего unsigned int. Если бы вы использовали литерал, например, 0x5040000C, он был бы (подписанный) int. В большинстве систем нет смысла подписывать адреса. «Sloppy types» может вызывать всевозможные тонкие ошибки, особенно в сочетании с различными формами цельной рекламы.
  • Извлеките изменчивый классификатор из структуры. Избегайте использования классификаторов типов внутри структур, если это возможно - они обычно вызывают множество иконов совместимости типов.
  • Обозначение указателя для аппаратных регистров немного своеобразно. Наиболее распространенный стиль - рассматривать регистры как объявленные переменные.

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

typedef struct { 
    uint8_t reg_0; 
    uint8_t reg_1; 
    uint16_t reg_2; 
} reg_split; 

#define REG_BASE (0xA040000Cu) 
#define REG (*(volatile reg_split *)REG_BASE) 

_Static_assert(sizeof(reg_split) == 4, "Unwanted padding detected"); 

... 

REG.reg_0 = 0xFF; 
+0

SIdenote: '0xA040000C', скорее всего, является неподписанным типом на платформах с' CHAR_BIT == n * 8' ('n = 1 ..N'). Но для уточнения и единообразия, это хорошая практика, чтобы сделать это явным. И, например, ARM CMSIS использует указатели для периферийного регистра 'struct'. Это просто соглашение для платформы. – Olaf

Смежные вопросы