использование ссылок на SFR в встроенном ассемблере gcc

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

Поэтому я хочу, чтобы все ссылки на регистры таймера были абстрагированы, например, OCRA для OCR0A/OCR2A и т. д.

Я мог бы сделать это с помощью простых макросов, например:

#define _CONCAT3(a,b,c)   a##b##c
#define CONCAT3(a,b)   _CONCAT3(a,b,c)

// глобальная установка номера таймера
#define MY_TIMER 2

// определяем псевдоним для каждого управляющего регистра
#define OCRA CONCAT3(OCR, MY_TIMER, A)

но по какой-то причине я не хочу использовать там макросы.

Поэтому я придумал такую схему:

typedef volatile uint8_t& tRegister;

constexpr volatile uint8_t& timer_register (volatile uint8_t& a, volatile uint8_t& b)
{ return MY_TIMER==0 ? a : b; }

tRegister OCRA = timer_register (OCR0A, OCR2A);

Несмотря на всю свою неуклюжесть, это работает очень хорошо, но когда я пытаюсь использовать это определение в директиве встроенного ассемблера, я получаю странные результаты:

asm volatile("lds r24, %0" : : "i"(&OCRA));

получит ошибку: невозможное ограничение в 'asm'

Теперь это:

asm volatile("lds r24, %0" : : "i"(&timer_register (OCR0A , OCR2A )));

будет скомпилировано (и создаст lds r24, 0x00B3 как следует).

По определению, ссылка на статическую переменную — это постоянный адрес, известный во время компиляции, поэтому я не понимаю, почему его нельзя использовать в этом контексте.

Итак, мои вопросы:

  • что там происходит?
  • как связать эти регистры (без макросов), чтобы угодить встроенному ассемблеру?

, 👍1


1 ответ


1

Вы можете использовать указатели вместо ссылок. С указателями у вас есть раздельные понятия «константный указатель» и «указатель на константу». А ссылка — это своего рода указатель, покрытый синтаксическим сахаром. Однако, тогда как у вас может быть «постоянная ссылка» (эквивалентная указателю на константа), я не знаю, как объявить «ссылку с константой адрес» (что было бы эквивалентно постоянному указателю). И это ваша проблема: ограничение "i" в операторе asm требует константа.

Вот мой подход к указателям:

typedef volatile uint8_t * tRegister;

constexpr tRegister timer_register(tRegister a, tRegister b)
{ return MY_TIMER==0 ? a : b; }

// `const' внизу обязателен.
const tRegister OCRA = timer_register(&OCR0A, &OCR2A);

asm volatile("lds r24, %0" : : "i"(OCRA));

Конечно, тогда вам придется изменить все операторы OCRA = ...;. с *OCRA = ...;, что неудобно. Но обратите внимание, что avr-libc определяет ФТБ с помощью макросов, эквивалентных

#define OCR0A (*address_of_OCR0A)

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

,

Хорошо, спасибо, но это обходной путь. На самом деле я сделал что-то подобное, но ограничился директивами asm, так что я мог хотя бы использовать свои ссылочные псевдонимы в коде C++. Я предполагаю, что этот прекрасный пункт доктрины о разнице между ссылкой и постоянным указателем - просто еще одна маленькая дыра в ужасно запутанных спецификациях C++, в которую попали бедняги, которые реализовали встроенный ассемблер :D., @kuroi neko