реализация sbi() и cli()
Я часто видел cli()
и sbi()
в коде Arduino. Обычно я не обращаю на них внимания, поскольку знаю, что они делают (очищают или устанавливают бит, указанный как второй аргумент в регистре микроконтроллера, указанном как первый). Я всегда думал, что эти функции — всего лишь понятный способ выполнения простых манипуляций с битами, которые могут быть весьма подвержены ошибкам. Что-то вроде этого:
#define cli(reg,bit) (*reg &= ~(1 << bit)
и #define sbi(reg,bit) (*reg |= (1 << bit))
.
Затем я узнал, что фактическая реализация выглядит следующим образом:
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
Это как раз то, о чём я раньше думал... но с двумя дополнительными макросами. Я искал их в библиотеках Arduino:
#define _BV(bit) (1 << (bit))
и
#define _SFR_BYTE(sfr) _MMIO_BYTE(_SFR_ADDR(sfr))
который содержит
#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))
и
#if _SFR_ASM_COMPAT
#if (__SFR_OFFSET == 0x20)
#define _SFR_ADDR(sfr) _SFR_MEM_ADDR(sfr)
#elif !defined(__ASSEMBLER__)
#define _SFR_ADDR(sfr) (_SFR_IO_REG_P(sfr) ? (_SFR_IO_ADDR(sfr) + 0x20) : _SFR_MEM_ADDR(sfr))
#endif
#else /* !_SFR_ASM_COMPAT */
#define _SFR_ADDR(sfr) _SFR_MEM_ADDR(sfr)
Дальнейшее исследование привело меня к определениям, которые я не мог понять (например, потому что они объясняются в комментариях с использованием множества неизвестных мне аббревиатур). Итак, у меня есть следующие вопросы:
- Почему
sfr
приводится к*(volatile uint8_t *)
? - Что означают аббревиатуры, используемые в этом коде? (
ASM
,SFR
,MMIO
) - При каких обстоятельствах определяется
__ASSEMBLER__
и где (в каком файле)? - Почему мы просто не можем написать
#define cli(reg,bit) (*reg &= ~(1 << bit)
и#define sbi(reg,bit) (*reg |= (1 << bit))
?
@noearchimede, 👍3
1 ответ
В микроконтроллерах Atmel (теперь Microchip) AVR инструкции языка ассемблера sbi, cbi, sbis и sbic могут адресовать только 32 порта ввода-вывода по адресам от 0x20 до 0x3f. Atmega168 и 328 (использовавшиеся в Arduino до Leonardo) имеют НАМНОГО больше 32 регистров ввода-вывода, поэтому только часть из них может быть адресована sbi и cbi (набор инструкций AVR был закреплен ГОДЫ назад, когда 32 регистра ввода-вывода казались более чем достаточными).
В результате операции, устанавливающие или очищающие один бит в этих 32 регистрах ввода-вывода, могут быть выполнены одной инструкцией, которая выполняется за два такта, но те же операции с оставшимися регистрами ввода-вывода требуют полноценной последовательности загрузки-выполнения-сохранения, выполнение которой занимает не менее трех циклов.
Насколько я помню, в AVRlibC было несколько макросов, которые делали почти одно и то же, но имели разные варианты использования:
Можно было бы скомпилировать самые быстрые и эффективные инструкции, доступные для этого конкретного адреса ввода-вывода.
Один всегда компилируется в sbi/cbi и выдает ошибку компилятора, если вы пытаетесь получить доступ к регистру за пределами 0x20-0x3f
ВСЕГДА можно было бы скомпилировать в последовательность load-act-store, независимо от того, можно ли было бы использовать sbi/cbi вместо этого.
Один всегда компилируется в последовательность load-act-store, но выдает ошибку компилятора, если вы пытаетесь использовать его по адресу между 0x20 и 0x3f.
По сути, выбор позволял вам выбирать между оптимизированной (но переменной) производительностью, равномерно медленным (но детерминированным) временем выполнения или максимально быстрым (но детерминированным во время компиляции) вариантом [который требовал, чтобы программист знал, находится ли рассматриваемый регистр в пределах или выше 0x20-0x3f].
- Как преобразовать скетч примера Arduino в полный проект C++?
- Как передать нестатический член класса для обратного вызова на платформах avr?
- Конечный автомат C++ / Wpmf-конверсия
- C++ против языка Arduino?
- Как использовать SPI на Arduino?
- Какие накладные расходы и другие соображения существуют при использовании структуры по сравнению с классом?
- Ошибка: expected unqualified-id before 'if'
- Что лучше использовать: #define или const int для констант?