Какие Arduino поддерживают ATOMIC_BLOCK?
Существует ли окончательный список архитектур Arduino, поддерживающих макрос ATOMIC_BLOCK ()
, и список #defines
для каждой архитектуры?
Я попытался поискать ядра Arduino на GitHub, чтобы узнать, где используется ATOMIC_BLOCK. Если он не используется в ядре, я предположил, что он не поддерживается. Я сгенерировал этот список, основываясь на этом предположении, но насколько он точен?
ArduinoCore Supported Archived #define
-------------------------------------------------------
samd
avr Yes __AVR__
arc32
sam Yes
primo Yes
megaavr Yes
API
nRF528x-mbedos
mbed
Я хотел бы написать какой-нибудь код, примерно такой (отредактированный в ответ на комментарии):
#define ATOMIC_BLOCK_SUPPORTED (__AVR__ || __MEGAAVR__ || __SAM__)
.
.
.
#if ATOMIC_BLOCK_SUPPORTED
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
#endif
// Не беспокоить.
#if ATOMIC_BLOCK_SUPPORTED
}
#endif
Для архитектур , которые не поддерживают ATOMIC_BLOCK
, я хотел бы написать свой собственный код для сохранения, отключения и восстановления глобального флага прерывания, что-то вроде этого.
asm
{
// Сохранить флаг прерывания.
}
// Снимите флаг прерывания.
// Не беспокоить.
// Восстановить флаг прерывания.
Потенциальное решение?
#ifdef __AVR__
#include <util/atomic.h>
#elif defined(__ARCH2__)
#define ATOMIC_BLOCK(/* for architecture 2 */)
#elif defined(__ARCH3__)
#define ATOMIC_BLOCK(/* for architecture 3 */)
// etc
#endif
.
.
.
#ifdef ATOMIC_BLOCK
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
#endif
// Не беспокоить.
#ifdef ATOMIC_BLOCK
}
#endif
Существует ли окончательный список кода, который сохраняет, отключает и восстанавливает глобальный флаг прерывания для каждой архитектуры Arduino, который можно было бы поместить в каждую ветвь #elif?
@tim, 👍2
Обсуждение1 ответ
Лучший ответ:
Этот ответ охватывает следующие вопросы: Какие Arduino поддерживают ATOMIC_BLOCK? И как я могу дублировать эту концепцию в C с помощью __attribute__((__cleanup__(func_to_call_when_x_exits_scope)))
и в C++ с помощью конструкторов классов и деструкторов?.
Какие Arduino поддерживают
макросы ATOMIC_BLOCK?
Какие Arduino поддерживают ATOMIC_BLOCK?
Только микроконтроллеры AVR (ATmel AVR architecture) поддерживают макросы ATOMIC_BLOCK, потому что эти макросы являются частью библиотеки avr-libc, которая, как вы уже догадались, поддерживает только микроконтроллеры AVR.
Arduino на основе AVR включают те, которые основаны на микроконтроллере ATmega328 (Arduino Uno, Nano, Mini и т. Д.), ATmega2560 (Arduino Mega 2560), ATmega32U4 (Arduino Leonardo, Pro Micro) и т. Д.
Как
макросы ATOMIC_BLOCK реализованы в C с компилятором gcc, и где я могу увидеть их исходный код?
avr-libc доступен для скачивания здесь: https://www.nongnu.org/avr-libc/ -- > Ссылка "Исходный код и документация" в разделе "Загрузки". Пример: вот tar-шар для v 2.0.0: http://download.savannah.gnu.org/releases/avr-libc/avr-libc-2.0.0.tar.bz2...
Весь исходный код макросов ATOMIC_BLOCK содержится в avr-libc-2.0.0/include/util/atomic.h.
ATOMIC_BLOCK
-это гениальный макрос, написанный на языке Си и опирающийся на расширение атрибута C gcc "cleanup", которое может вызывать функцию, определенную вами, когда переменная выходит за пределы области видимости. По сути, это идентично деструктору класса C++, который вызывается, когда экземпляр класса в C++ выходит за пределы области видимости. Расширение атрибута gcc "cleanup"-это способ получить C++ - подобное поведение в C.
Вот официальная документация gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html... В нем говорится:
очистка (cleanup_function)
Атрибут очистки выполняет функцию, когда переменная выходит за пределы области действия. Этот атрибут может быть применен только к переменным области действия автоматической функции; он не может быть применен к параметрам или переменным со статической длительностью хранения. Функция должна принимать один параметр, указатель на тип, совместимый с переменной. Возвращаемое значение функции (если таковое имеется) игнорируется.
Если функция-fexceptions включена, то функция cleanup_function запускается во время разматывания стека, которое происходит во время обработки исключения. Обратите внимание, что атрибут cleanup не позволяет перехватывать исключение, а только выполнять какое-либо действие. Не определено, что происходит, если cleanup_function не возвращается нормально.
Его формат выглядит следующим образом:
int x __attribute__((__cleanup__(func_to_call_when_x_exits_scope))) = 0;
Вы используете его следующим образом:
ЗАПУСТИТЕ РЕАЛЬНЫЙ ПРИМЕР ЭТОГО КОДА НА ЯЗЫКЕ Си ОНЛАЙН ЗДЕСЬ.
static __inline__ void cleanup_my_byte(uint8_t *my_byte_p)
{
Serial.print("my_byte is going out of scope! Its value is ");
Serial.println(*my_byte_p);
}
void setup()
{
uint8_t my_byte __attribute__((__cleanup__(cleanup_my_byte))) = 7;
} // The following function call occurs automatically here as `my_byte`
// exits its scope!:
// `cleanup_my_byte(&my_byte);`
Изучая файл avr-libc-2.0.0/include/util/atomic.h, упомянутый выше, вы можете увидеть, как реализуется
макрос ATOMIC_RESTORESTATE с использованием этого атрибута. Я поместил здесь только соответствующие фрагменты из этого файла:
#include <avr/io.h>
#include <avr/interrupt.h>
#define ATOMIC_BLOCK(type) for ( type, __ToDo = __iCliRetVal(); \
__ToDo ; __ToDo = 0 )
static __inline__ uint8_t __iCliRetVal(void)
{
cli();
return 1;
}
#define ATOMIC_RESTORESTATE uint8_t sreg_save \
__attribute__((__cleanup__(__iRestore))) = SREG
static __inline__ void __iRestore(const uint8_t *__s)
{
SREG = *__s;
__asm__ volatile ("" ::: "memory");
}
Обратите внимание, что cli()
очищает (выключает) прерывания, а sei()
устанавливает (включает) прерывания.
Итак, вы видите, что ATOMIC_RESTORE_STATE
заменяется uint8_t sreg_save __атрибут__((__Толока__(__iRestore))) = регистре sreg
, которое выполняет резервное копирование прерывание состояния с АВР регистре sreg
8-разрядный регистр на sreg_save
, затем вызывает вызов этой функции, когда sreg_save
выходов своим размахом: __iRestore(&sreg_save);
. Этот вызов функции __iRestore()
затем возвращает регистр SREG
туда, где он был, восстанавливая состояние прерывания (включая его, если он был включен раньше, или ВЫКЛЮЧАЯ, если он был выключен раньше).
Официальная документация для ATOMIC_BLOCK
находится здесь: https://www.nongnu.org/avr-libc/user-manual/group__util__atomic.html... Пример использования таков:
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
my_var_copy = my_var;
}
В ATOMIC_RESTORESTATE
часть спины вверх в регистре sreg
реестр и настраивает позвонить по __iRestore(&sreg_save);
в случае, когда sreg_save
выходы этого блока сферу, как мы уже обсуждали, и ATOMIC_BLOCK
часть преобразуется в это: для ( ATOMIC_RESTORESTATE, __Тодо = __iCliRetVal(); __дел ; __Тодо = 0 )
. Это означает, что весь блок теперь представляет собой гениальный цикл for, который запускается только один раз! Перед входом в
for
блока цикла, __iCliRetVal()
вызывается один раз, снятия (выключения) прерывания, и __Тодо
имеет значение 1
, или значение true
, поэтому для
цикла выполняется как минимум один раз, и затем в конце первого запуска он установлен в 0
, или значение false
, чтобы остановить на
цикле после одного запуска! Как мы уже говорили, переменная sreg_save теперь выходит из области действия в закрывающей скобке, и вызов
__iRestore(&sreg_save);
производится через расширение gcc для восстановления состояния прерывания! Гениально!
Как вы могли бы реализовать
функциональность ATOMIC_BLOCK в Arduino на языке C++ (в отличие от версии avrlibc gcc C)?
В C++ (Arduino-это C++, поэтому мы не должны ограничиваться только C плюс ССЗ расширения, как было ограничение в avrlibc ATOMIC_BLOCK
макросы), вы можете получить подобный эффект, используя деструкторы в структуры или классы (или, просто используйте avrlibc шаблона, показанного выше, если вы любите, а в том, что gcc c код также отлично действует на gcc c++ кода!).
Вариант 1: пример создания стиля использования, аналогичного std::lock_guard C++
Это, например, реализовало бы форму ATOMIC_BLOCK(ATOMIC_FORCEON) {}
, используя простой класс в C++:
Он опирается на встроенные функции Arduino interrupts()
(такие же, как функция sei()
avrlibc) и noInterrupts()
(такие же, как функция cli()
avrlibc). Я хотел бы, чтобы Arduino также имел встроенные функции saveInterrupts()
и restoreInterrupts ()
, но, к сожалению, их нет. Кто-то должен будет подать запрос на функции и запросить их, или реализовать их самостоятельно и сделать Pull Request (PR) на GitHub, чтобы попытаться вернуть их обратно в основные ветви Arduino.
ЗАПУСТИТЕ РЕАЛЬНЫЙ ПРИМЕР ЭТОГО КОДА НА C++17 ОНЛАЙН ЗДЕСЬ.
#define ATOMIC_BLOCK_FORCEON AtomicBlockForceOn atomicBlockForceOn_
class AtomicBlockForceOn
{
public:
// Конструктор: вызывается при создании объекта
inline AtomicBlockForceOn()
{
noInterrupts(); // выключение прерываний
}
// Destructor: вызывается, когда объект уничтожен (например, goes
// вне сферы действия)
inline ~AtomicBlockForceOn()
{
interrupts(); // включение прерываний
}
};
Вот так! Теперь используйте его так:
uint16_t my_var_copy;
{
ATOMIC_BLOCK_FORCEON;
// теперь делайте здесь все, что хотите, с отключенными прерываниями; ex:
my_var_copy = my_var;
} // здесь вызывается деструктор объекта ' atomicBlockForceOn_`
// и прерывания автоматически включаются!
Довольно круто!
Это, я полагаю, вероятно, именно так, как реализован std::lock_guard в C++!
Его использование очень похоже:
void safe_increment()
{
const std::lock_guard<std::mutex> lock(g_i_mutex);
++g_i;
std::cout << std::this_thread::get_id() << ": " << g_i << '\n';
// g_i_mutex автоматически освобождается при блокировке
// выходит за рамки
}
Вариант 2: пример создания стиля использования, похожего на
макросы avrlibc ATOMIC_BLOCK
Чтобы получить стиль использования ATOMIC_BLOCK(ATOMIC_FORCEON) { }
в C++, можно определенно использовать стиль gcc C, который использует avrlibc, так как это также допустимо в C++, но давайте продолжим со стилем класса C++, показанным чуть выше, всего с несколькими настройками. Мы добавим в класс еще пару вспомогательных макросов и еще пару функций, чтобы заставить его работать. Вот он, с примером использования, показанным в конце, который идентичен тому, как можно
использовать ATOMIC_BLOCK avrlibc! Я думаю, что это тоже довольно круто.
ЗАПУСТИТЕ РЕАЛЬНЫЙ ПРИМЕР ЭТОГО КОДА НА C++17 ОНЛАЙН ЗДЕСЬ.
#define ATOMIC_BLOCK(type) for(type; type##_OBJECT_NAME.run(); \
type##_OBJECT_NAME.stop())
#define ATOMIC_FORCEON_OBJECT_NAME atomicBlockForceOn_
#define ATOMIC_FORCEON AtomicBlockForceOn ATOMIC_FORCEON_OBJECT_NAME
class AtomicBlockForceOn
{
public:
// Constructor: called when the object is created
inline AtomicBlockForceOn()
{
noInterrupts(); // turn interrupts OFF
}
// Destructor: вызывается, когда объект уничтожен (например, goes
// вне сферы действия)
inline ~AtomicBlockForceOn()
{
interrupts(); // turn interrupts ON
}
// Мы можем бежать? Возвращает true для запуска цикла `for` или
// `false` для его остановки.
inline bool run()
{
return run_now;
}
// Скажите циклу "for", чтобы он остановился
inline void stop()
{
run_now = false;
}
private:
bool run_now = true;
};
Используйте его как макрос ATOMIC_BLOCK() { }
avrlibc!
Пример использования:
uint16_t my_var_copy;
ATOMIC_BLOCK(ATOMIC_FORCEON)
{
// now do whatever you want here, with interrupts disabled; ex:
my_var_copy = my_var;
} // the `ATOMIC_FORCEON_OBJECT_NAME` (`atomicBlockForceOn_`) object's
// destructor is called here and interrupts are automatically
// turned back ON!
Дальнейшее чтение:
- Превосходная страница Ника Гэммона "Прерывания", на которой я впервые начал изучать прерывания в 2012~2014 годах или около того: https://gammon.com.au/interrupts.
- Другие ответы, которые я сделал о прерываниях, замках/охранниках и тому подобном:
- глобальная изменчивая переменная не обновляется в ISR
- Моя собственная проблема с расовым состоянием: https://stackoverflow.com/questions/36381932/c-decrementing-an-element-of-a-single-byte-volatile-array-is-not-atomic-why
- Мое собственное решение race condition, демонстрирующее 3 (или 4, в зависимости от того, как вы на это смотрите) способа применения "атомарных охранников доступа" на 8-битных Arduino на базе AVR: https://stackoverflow.com/questions/36381932/c-decrementing-an-element-of-a-single-byte-volatile-array-is-not-atomic-why/39693278#39693278
- Кстати, что это за странный
тип##_OBJECT_NAME
, который я сделал в макросе? Подробнее о конкатенации маркеров макросов с помощьюоператора препроцессора ## читайте в официальном руководстве пользователя gcc C++ здесь: https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html... Примечание: конечно, это также работает и на языке Си.
- gcc
inline
или__inline__
, атакже статические встроенные
илистатические __inline__
правила: https://gcc.gnu.org/onlinedocs/gcc/Inline.html
Спасибо. Это отличный ответ. Я ранее читал некоторые из ваших дальнейших прочтений (пункты 1 и 2i-iii), исследуя этот вопрос. Я собирался разобрать его, но вы спасли мне работу, создав отличную справку. Макрос с циклом " for "и вариант 1 и 2 с "class" - это то, что я рассматривал при реализации, поэтому спасибо за четкое объяснение их. Теперь нужно выяснить, что поместить в макрос __iRestore
и class
constructure/destructor, чтобы сохранить/восстановить глобальный флаг прерывания для каждой архитектуры., @tim
Правильный. Теперь вам нужно выяснить, как отключить/включить прерывания для каждой архитектуры, а затем поместить соответствующий код в конструктор/деструктор. [Вот как это сделать STM32](https://stm32f4-discovery.net/2015/06/how-to-properly-enabledisable-interrupts-in-arm-cortex-m/)например, который используется в линейке Arduino "professional", такой как [$103 Portenta H7](https://store.arduino.cc/usa/portenta-h7), основанный на микроконтроллере STM32H7: prim = __get_PRIMASK();
, __disable_irq();
, if (!prim) { __enable_irq(); }
. Это использование низкоуровневых библиотек CMSIS., @Gabriel Staples
Вы также можете использовать полдюжины других методов, включая библиотеки ядра STM32 и библиотеки FreeRTOS (при использовании операционной системы FreeRTOS в реальном времени). У меня есть личные заметки обо всем этом, которые мне нужно когда-нибудь опубликовать на своем сайте. 32-битные микроконтроллеры-звери. Я понятия не имею, как это сделать на 32-битных микроконтроллерах Atmel/Microchip SAM или SAMD, таких как [Zero](https://store.arduino.cc/usa/arduino-zero) или [Должный](https://store.arduino.cc/usa/due), но есть способы для всех фишек, вы просто должны понять их., @Gabriel Staples
- Используйте ISR внутри библиотеки более элегантно
- Создание библиотеки для ардуино
- Как запрограммировать ардуино на чистом C/C++?
- Почему необходимо использовать ключевое слово volatile для глобальных переменных при обработке прерываний в ардуино?
- RtcDateTime' не называет тип
- как отправить аргумент объектам ESP8266WebServer в функции
- GSM-модуль IOT-GA6 Arduino + ошибка CME 58
- Разница между массивом char и массивом unsigned char
Вы пробовали ' #ifdef ATOMIC_BLOCK`?, @user253751
Или даже:
#ifndef ATOMIC_BLOCK /* определите свой собственный ATOMIC_BLOCK здесь */ #endif
, @user253751Вы нашли кого-нибудь, кто не поддерживает это?, @user253751
Вы действительно пробовали его на всех этих архитектурах?, @user253751
Обратите внимание, что в AVR "ATOMIC_BLOCK" предоставляется не ядром Arduino, а [avr-libc](https://www.nongnu.org/avr-libc/user-manual/group__util__atomic.html)., @Edgar Bonet
Пожалуйста, обратите внимание, что ваша идея объединить символы
#define
и использовать для этого#ifdef ' не сработает. Лучше попробуйте "#define X (defined(A) | defined(B) | defined(C))
и проверьте с помощью " #if X`., @the busybeeСпасибо @edgar-bonet и @the-busybee. В настоящее время я читаю [Идентификацию...](https://forum.arduino.cc/index.php?topic=128520.0) и [Спецификация платформы](https://arduino.github.io/arduino-cli/platform-specification/). По - видимому, это вечная проблема, которую никто до сих пор целостно не рассматривал. Думая, что мне, возможно, придется соскрести данные со всех форумов Arduino в поисках "#define"..., @tim
@тим, посмотри, даст ли мой ответ то, что тебе нужно. Это тема, которую я много изучал., @Gabriel Staples