использование ссылок на 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
как следует).
По определению, ссылка на статическую переменную — это постоянный адрес, известный во время компиляции, поэтому я не понимаю, почему его нельзя использовать в этом контексте.
Итак, мои вопросы:
- что там происходит?
- как связать эти регистры (без макросов), чтобы угодить встроенному ассемблеру?
@kuroi neko, 👍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)
Если вам нужно это удобство, вам, возможно, придется прибегнуть к использованию аналогичных макросы самостоятельно.
- Как писать скетчи, совместимые с makefile?
- Какие накладные расходы и другие соображения существуют при использовании структуры по сравнению с классом?
- Что лучше использовать: #define или const int для констант?
- Функции со строковыми параметрами
- Как работать с аналоговыми контактами в цикле?
- Какие есть другие IDE для Arduino?
- GCC msg "note: in definition of macro 'max'" сообщение об ошибке
- Как использовать переменные и функции в нескольких файлах .ino
Хорошо, спасибо, но это обходной путь. На самом деле я сделал что-то подобное, но ограничился директивами asm, так что я мог хотя бы использовать свои ссылочные псевдонимы в коде C++. Я предполагаю, что этот прекрасный пункт доктрины о разнице между ссылкой и постоянным указателем - просто еще одна маленькая дыра в ужасно запутанных спецификациях C++, в которую попали бедняги, которые реализовали встроенный ассемблер :D., @kuroi neko