Почему нет скобок после процедуры прерывания внутри attachInterrupt
Мне просто любопытно, почему в конце ISR нет скобок при подключении и назначении команды прерывания?
attachInterrupt(digitalPinToInterrupt(interruptPin), blink, CHANGE);
...
void blink() {
state = !state;
}
В, attachInterrupt я ожидал, что "мигание" будет записано как "мигание()", в скобках, а не только имя функции, вроде того, как вы обычно вызываете функцию blink() из своего цикла или функций настройки (или в другом месте). Итак, что здесь происходит и существуют ли другие сценарии, в которых вы могли бы использовать функцию без скобок после нее?
@Diesel, 👍3
Обсуждение1 ответ
Лучший ответ:
Правильно, что вы ставите квадратные скобки после имени функции, если вы ее вызываете. Но с помощью attachInterrupt()
вы просто хотите сообщить ему, какую функцию вызывать в случае прерывания. С помощью скобок компилятор подумает, что вы хотите, чтобы функция была выполнена, а значение результата/возврата было указано в качестве параметра для attachInterrupt()
. Но выполнение здесь должно происходить только в случае прерывания. Функции attachInterrupt ()
/соответствующему ISR нужно знать, что вызывать в случае прерывания. Просто информация/адрес функции.
Может быть, вы уже слышали об указателях. Это переменные, которые содержат адрес другой переменной. Они указывают куда-то еще в памяти. мигание
(без скобок) - это в основном указатель на функцию. Если оставить скобки в стороне, компилятор сообщит, что вам нужен адрес функции, а не ее выполнение. Таким образом, вы предоставляете параметр функции по ссылке, указывая адрес, а не значение.
Если вам нравится/нужно, вы можете использовать этот принцип самостоятельно, определив собственную переменную указателя функции. Например, это
void (*myCallbackFunction)(int);
которая является функцией, которая принимает значение int в качестве параметра и ничего не возвращает (void). Затем вы можете установить указатель на функцию и вызвать ее как обычную функцию
void blinkXTimes(int times){
for(int i=0;i<times;i++){
digitalWrite(pin, HIGH);
delay(500);
digitalWrite(pin, LOW);
delay(500);
}
}
void setup(){
pinMode(pin, OUTPUT);
myCallbackFunction = blinkXTimes;
void loop(){
myCallbackFunction(3);
delay(5000);
}
Мы вызываем myCallbackFunction()
, но это указатель на функцию, который указывает на blinkXTimes()
, поэтому он будет выполнен.
Итак, что здесь происходит и существуют ли другие сценарии, в которых вы могли бы использовать функцию без скобок после нее?
Приведенный выше принцип очень полезен для написания библиотек с функциями обратного вызова пользователей. Другим примером (помимо attachInterrupt()
) является библиотека проводов
. Там прерывания приема и запроса должны взаимодействовать с пользователем библиотеки для доставки или запроса данных от него. Для этого библиотека Wire
позволяет вам устанавливать собственные функции обратного вызова. Обратитесь к примерам этой библиотеки.
Другим приложением было бы создание конечного автомата с указателем функции (в основном вызывающего различные функции на каждой итерации цикла в зависимости от состояния, которое представлено указателем функции).
Это два больших приложения, о которых я знаю. Первый из них встречается чаще. Хотя указатели на функции - это всего лишь инструмент. Их можно использовать для многих целей.
Это было феноменально полезно!!!, @Diesel
Просто придираюсь к мелочам, но указание указателя в качестве аргумента для параметра указателя все равно передается по значению. В C++ есть только один способ передачи по ссылке: если у вас есть ссылочный параметр, например " void f(ТИП & p);"., @the busybee
К сожалению, многие из этих терминов перегружены. Стандарт C использовал термин "ссылочный тип" для " T "в конструкции" T*", но в C++ этот термин там не используется. Однако применение унарного оператора *
к указателю по-прежнему буквально называется "де*ссылкой*" на указатель в C++. По общему признанию, эта терминология для использования в качестве передающего значения указателя более запутана в C++, чем это было/(все еще есть) в C. Но она обычно используется и понимается в контексте даже программистами C++, если в противном случае неточна. Для тех, кто еще не сталкивался с этим использованием, что ж, теперь у вас есть., @timemage
- Почему необходимо использовать ключевое слово volatile для глобальных переменных при обработке прерываний в ардуино?
- Серийное прерывание
- Невозможно преобразовать 'int (*)[size]' в 'int**': Cannot convert 'int (*)[size]' to 'int**'
- Прерывание ардуино при смене контакта
- Как удалить элемент из массива arduino?
- Влияет ли `millis()` на длинные ISR?
- Как передать несколько переменных в функцию?
- Есть ли функция Adafruit для инвертирования цветов моего экрана для языка Arduino?
Кто, черт возьми, хотел бы это закрыть? Это хороший вопрос., @Nick Gammon
Модераторы могут видеть пользователей, проголосовавших за закрытие этого вопроса., @VE7JRO