CallBack Function с параметром
- Войдите на сайт для отправки комментариев
Ср, 23/05/2018 - 19:50
Доброго времени суток. Задачка следующая.
Есть библиотека (могу привести полный код). Кусок меня интересующий ниже.
Суть такова, что создается объект типа AlarmClass, с параметрами: время и функция обратного вызова, но проблема в том, что она (эта функция) идет без параметров, а мне нужно передать в нее параметр.
Сейчас вызов идет вот так
Alarm.timerOnce(10, OnceOnly);
void OnceOnly() {
Serial.println("Test");
}
А хочется вот так
Alarm.timerOnce(10, OnceOnly(123));
void OnceOnly(data) {
Serial.println(data);
}
Я полез в библиотеку и поменял typedef void (*OnTick_t)(byte); // alarm callback function typedef
Но получаю ошибку "invalid use of void expression"
Подскажите, как правильно оформить передачу параметра.
Код библы
typedef void (*OnTick_t)(); // alarm callback function typedef
//typedef void (*OnTick_t)(byte); // alarm callback function typedef
// class defining an alarm instance, only used by dtAlarmsClass
class AlarmClass
{
public:
AlarmClass();
OnTick_t onTickHandler;
void updateNextTrigger();
time_t value;
time_t nextTrigger;
AlarmMode_t Mode;
};
// class containing the collection of alarms
class TimeAlarmsClass
{
private:
AlarmClass Alarm[dtNBR_ALARMS];
void serviceAlarms();
uint8_t isServicing;
uint8_t servicedAlarmId; // the alarm currently being serviced
AlarmID_t create(time_t value, OnTick_t onTickHandler, uint8_t isOneShot, dtAlarmPeriod_t alarmType);
public:
TimeAlarmsClass();
// functions to create alarms and timers
// trigger once at the given time in the future
AlarmID_t triggerOnce(time_t value, OnTick_t onTickHandler) {
if (value <= 0) return dtINVALID_ALARM_ID;
return create(value, onTickHandler, true, dtExplicitAlarm);
}
// trigger once at given time of day
AlarmID_t alarmOnce(time_t value, OnTick_t onTickHandler) {
if (value <= 0 || value > SECS_PER_DAY) return dtINVALID_ALARM_ID;
return create(value, onTickHandler, true, dtDailyAlarm);
}
AlarmID_t alarmOnce(const int H, const int M, const int S, OnTick_t onTickHandler) {
return alarmOnce(AlarmHMS(H,M,S), onTickHandler);
}
Перечитывал, я там ошибся. Хочется вот так
Alarm.timerOnce(10, OnceOnly(123)); void OnceOnly(byte data) { Serial.println(data); }Смотри, какое дело: то, что ты делаешь (Alarm.timerOnce(10, OnceOnly(123));) - это ВЫЗОВ функции OnceOnly с параметром 123.
Если тебе надо, чтобы вызывающая сторона умела передавать какой-либо дополнительный параметр, то:
typedef void (*PHandler)(byte param); class SomeClass { private: byte param; PHandler handler; public: void timerOnce(PHandler h, byte p) { if(!h) return; handler = h; param = p; delay(10000); handler(param); } }; void MyHandler(byte param) { Serial.println(param); } SomeClass some; void setup() { some.timerOnce(MyHandler,123); } void loop() { }delay там чисто для прикола, я просто показал принцип, как сделать.
Круто, спасибо!!!
Получается придется конкретно библу переколбасить, протащить параметр в конструктор... и как-то поменять вызов.
Буду пробывать
Как вариант просто сделать добавку, что бы и так и так.
/**/ typedef void (*PHandlerA)(byte param); typedef void (*PHandlerB)(); //--------------------------------- class SomeClass { private: byte param; PHandlerB handler; public: void timerOnce(PHandlerA h, byte p) { if (!h) return; h(p); handler = (PHandlerB)h; param = p; delay(10000); } void timerOnce(PHandlerB h) { if (!h) return; h(); handler = h; delay(10000); } }; //--------------------------------------- void MyHandlerA(byte param) { Serial.println(param); } void MyHandlerB() { Serial.println("none"); } SomeClass someA; SomeClass someB; //--------------------------------------- void setup() { someA.timerOnce(MyHandlerA, 123); someB.timerOnce(MyHandlerB); } void loop() { } /*Скетч использует 1778 байт (5%) памяти устройства. Всего доступно 30720 байт. Глобальные переменные используют 196 байт (9%) динамической памяти, оставляя 1852 байт для локальных переменных. Максимум: 2048 байт. */Спасибо большое за пример! Именно так и сделаю.
Только я не совсем понял, не надо разве в секцию private задать PHandlerA handler;
Спасибо большое за пример! Именно так и сделаю.
Только я не совсем понял, не надо разве в секцию private задать PHandlerA handler;
Надо, квон просто забыл ;)
А зачем много тратить памяти. Я просто сделал приведение к беспараметному виду. Если надо запустить снова, то приведите к типу с параметром и запустите.
/**/ typedef void (*pDoByte)(byte param); typedef void (*pDo)(); //--------------------------------- class SomeClass { private: byte param; pDo handler; public: void timerOnce(pDoByte h, byte p) { if (!h) return; handler = (pDo)h; // < -запись с беспараметный тип param = p; delay(10000); pDoByte Do = (pDoByte)handler; //<- приводение в параметный вид Do(param); } void timerOnce(pDo h) { if (!h) return; h(); handler = h; delay(10000); } }; //--------------------------------------- void MyHandlerA(byte param) { Serial.println(param); } void MyHandlerB() { Serial.println("none"); } SomeClass someA; SomeClass someB; //--------------------------------------- void setup() { someA.timerOnce(MyHandlerA, 123); someB.timerOnce(MyHandlerB); } void loop() { } /*Скетч использует 1786 байт (5%) памяти устройства. Всего доступно 30720 байт. Глобальные переменные используют 196 байт (9%) динамической памяти, оставляя 1852 байт для локальных переменных. Максимум: 2048 байт. */Чего-то у меня не сростается.
Завел новый тип с параметром OnTickByte_t
В описании класса AlarmClass ввел переменные OnTickByte_t onTickByteHandler; byte param;
В описании класс TimeAlarmsClass ввел новый конструктор AlarmID_t createbyte(time_t value, OnTickByte_t onTickByteHandler, uint8_t isOneShot, dtAlarmPeriod_t alarmType, byte param);
В конструкторе сохраняю значение Alarm[id].param = param;
И в процедуре void TimeAlarmsClass::serviceAlarms() ввел новый вызов.
typedef void (*OnTick_t)(); // alarm callback function typedef typedef void (*OnTickByte_t)(byte param); class AlarmClass { public: AlarmClass(); OnTick_t onTickHandler; OnTickByte_t onTickByteHandler; void updateNextTrigger(); time_t value; time_t nextTrigger; AlarmMode_t Mode; byte param; }; class TimeAlarmsClass { private: AlarmClass Alarm[dtNBR_ALARMS]; void serviceAlarms(); uint8_t isServicing; uint8_t servicedAlarmId; // the alarm currently being serviced AlarmID_t create(time_t value, OnTick_t onTickHandler, uint8_t isOneShot, dtAlarmPeriod_t alarmType); AlarmID_t createbyte(time_t value, OnTickByte_t onTickByteHandler, uint8_t isOneShot, dtAlarmPeriod_t alarmType, byte param); public: TimeAlarmsClass(); AlarmID_t timerOnce(time_t value, OnTick_t onTickHandler) { if (value <= 0) return dtINVALID_ALARM_ID; return create(value, onTickHandler, true, dtTimer); } AlarmID_t timerOnce(time_t value, OnTickByte_t onTickByteHandler, byte param) { if (value <= 0) return dtINVALID_ALARM_ID; return createbyte(value, onTickByteHandler, true, dtTimer, param); } }; AlarmID_t TimeAlarmsClass::create(time_t value, OnTick_t onTickHandler, uint8_t isOneShot, dtAlarmPeriod_t alarmType) { if ( ! ( (dtIsAlarm(alarmType) && now() < SECS_PER_YEAR) || (dtUseAbsoluteValue(alarmType) && (value == 0)) ) ) { // only create alarm ids if the time is at least Jan 1 1971 for (uint8_t id = 0; id < dtNBR_ALARMS; id++) { if (Alarm[id].Mode.alarmType == dtNotAllocated) { // here if there is an Alarm id that is not allocated Alarm[id].onTickHandler = onTickHandler; Alarm[id].Mode.isOneShot = isOneShot; Alarm[id].Mode.alarmType = alarmType; Alarm[id].value = value; enable(id); return id; // alarm created ok } } } return dtINVALID_ALARM_ID; // no IDs available or time is invalid } AlarmID_t TimeAlarmsClass::createbyte(time_t value, OnTickByte_t onTickByteHandler, uint8_t isOneShot, dtAlarmPeriod_t alarmType, byte param) { if ( ! ( (dtIsAlarm(alarmType) && now() < SECS_PER_YEAR) || (dtUseAbsoluteValue(alarmType) && (value == 0)) ) ) { // only create alarm ids if the time is at least Jan 1 1971 for (uint8_t id = 0; id < dtNBR_ALARMS; id++) { if (Alarm[id].Mode.alarmType == dtNotAllocated) { // here if there is an Alarm id that is not allocated Alarm[id].onTickByteHandler = onTickByteHandler; Alarm[id].param = param; Alarm[id].Mode.isOneShot = isOneShot; Alarm[id].Mode.alarmType = alarmType; Alarm[id].value = value; enable(id); return id; // alarm created ok } } } return dtINVALID_ALARM_ID; // no IDs available or time is invalid } void TimeAlarmsClass::serviceAlarms() { if (!isServicing) { isServicing = true; for (servicedAlarmId = 0; servicedAlarmId < dtNBR_ALARMS; servicedAlarmId++) { if (Alarm[servicedAlarmId].Mode.isEnabled && (now() >= Alarm[servicedAlarmId].nextTrigger)) { OnTick_t TickHandler = Alarm[servicedAlarmId].onTickHandler; OnTickByte_t TickByteHandler = Alarm[servicedAlarmId].onTickByteHandler; byte param = Alarm[servicedAlarmId].param; if (Alarm[servicedAlarmId].Mode.isOneShot) { free(servicedAlarmId); // free the ID if mode is OnShot } else { Alarm[servicedAlarmId].updateNextTrigger(); } if (TickHandler != NULL) { (*TickHandler)(); // call the handler } if (TickByteHandler != NULL) { (*TickByteHandler)(param); // call the handler } } } isServicing = false; } }Все нашел!!!!
У меня не стартовал таймер, там тоже проверка хэндла была. Добавил свой и все заработало.
void TimeAlarmsClass::enable(AlarmID_t ID) { if (isAllocated(ID)) { if (( !(dtUseAbsoluteValue(Alarm[ID].Mode.alarmType) && (Alarm[ID].value == 0)) ) && ((Alarm[ID].onTickHandler != NULL) || (Alarm[ID].onTickByteHandler != NULL)) ) { // only enable if value is non zero and a tick handler has been set // (is not NULL, value is non zero ONLY for dtTimer & dtExplicitAlarm // (the rest can have 0 to account for midnight)) Alarm[ID].Mode.isEnabled = true; Alarm[ID].updateNextTrigger(); // trigger is updated whenever this is called, even if already enabled } else { Alarm[ID].Mode.isEnabled = false; } } }Огромное всем спасибо!!!!
Когда у тебя переменная - указатель на функцию, разыменовывать через звездочку ее не надо, gcc и так пропускает, а читабельность получше маленько. Хотя, чоэтоя, где С, а где читабельность...
Вот так тоже прокатит
098if( !TickByteHandler ) TickByteHandler(param);Когда у тебя переменная - указатель на функцию, разыменовывать через звездочку ее не надо, gcc и так пропускает, а читабельность получше маленько. Хотя, чоэтоя, где С, а где читабельность...
Вот так тоже прокатит
098if( !TickByteHandler ) TickByteHandler(param);Деда, убери !, а то у тоби NULL разыменуется :)
Да. ! лишний. Ну это я не иначе, с пахмелью. Простите.
DIYMan, спасибо, что разглядел.
Добрый день. Ситуация у меня похожая. Arduino due, 15 кнопок. По нажатию на каждую из них выполняется однотипный код. В развернутом виде все работает, но переписывание одной и той же функции 15 раз... Прочитал ветку. Решил сделать "оптимизацию". Но при компилировании получаю ошибку в строках 31 и 40: invalid use of void expression. Что я не так делаю? И вообще возможно ли это в прерываниях?
#include <Arduino.h> const int pins[10] = {30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; volatile boolean isPressed[10] = {0,0,0,0,0,0,0,0,0,0}; typedef void (*PHandler)(byte param); class FunClass { private: byte param; PHandler handler; public: void intFunction(PHandler h, byte p) { if (!h) return; handler = h; param = p; handler(param); } }; FunClass funClass; void interruptFunction(byte id) { detachInterrupt(pins[id]); isPressed[id] = digitalRead(pins[id]); attachInterrupt(pins[id], funClass.intFunction(interruptFunction, id), CHANGE); } void setup() { for (int i = 0; i < 10; i++) { pinMode(pins[i], INPUT_PULLUP); attachInterrupt(pins[i], funClass.intFunction(interruptFunction, i), CHANGE); } } void loop() { // }атачить в прерывания тока статическую ф-ию и без параметров, делайте обертку. А воще подход какойто мутный. Нафига там класс? И так ли нужны прерывания на кнопки при пустом лупе.
атачить в прерывания тока статическую ф-ию и без параметров, делайте обертку. А воще подход какойто мутный. Нафига там класс? И так ли нужны прерывания на кнопки при пустом лупе.
Луп там не пустой. Я этот пример написал отдельно и облегченно. В кратце - станок, 4 шаговых движка, 5 пневмоцилиндров порядка 12 датчиков и тому подобное. от delay и прочих тяжелых вещей я отказался. зачем например в лупе перечитывать состояние всей периферии, если есть возможность точечно получить инфу о событии. плюс с защитой от дребезга. Благо Due это позволяет. Как я и писал, код рабочий, но очень длинный. вот и пытаюсь в виду (стандартаная фраза на этом форуме) не совсем сильных познаниях в с++, научиться и сделать некоторую оптимизацию кода. Статическая функция и без параметров в обертке это примерно как? Что то типа глобальной переменной, обработка которой уже внутри стандартной Callback функции? Типа вот так:
#include <Arduino.h> const int pins[10] = {30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; volatile boolean isPressed[10] = {0,0,0,0,0,0,0,0,0,0}; volatile byte id; void interruptFunction() { detachInterrupt(pins[id]); isPressed[id] = digitalRead(pins[id]); attachInterrupt(pins[id], interruptFunction, CHANGE); } void setup() { for (int i = 0; i < 10; i++) { pinMode(pins[i], INPUT_PULLUP); id = i; attachInterrupt(pins[i], interruptFunction, CHANGE); } } void loop() { // }похоже. Но работать не будет, при вызове прерывания id откуда возмется?
Если луп не пустой и прокручивается довольно быстро, не хуже сотни мсек то чтото вроде такого
for (int i = 0; i < 10; i++) { isPressed[i]= digitalRead(pins[i]); }избавит от гемора и сильно сократит код.
похоже. Если луп не пустой и прокручивается довольно быстро, не хуже сотни мсек то чтото вроде такого
for (int i = 0; i < 10; i++) { isPressed[i]= digitalRead(pins[i]); }избавит от гемора и сильно сократит код.
Это я пробовал, но тогда получаю неравномерность работы ШД. (использую AccelStepper) и если в датчиках ПЦ - два на цилиндр нет дребезга, то на кнопках дребезг есть, несмотря на шунтирование кнопок кондерами
id - ну да...
при атаче - понятно, а при вызове прерывания?
Время отработки приведеного кода порядка десятков мксек, он быстрый, он не может повлиять на ШД. Если надо быстрей - digitalRead меняем на прямую работу с регистром, int i на byte i, разворачиваем цикл, можна до нескольких мксек дооптимизироапть.
4 ШД, требуемая частота импульсов для каждого разная один от 2400/сек до 4800/сек период соответственно от 415 мкСек до 208 мкСек. ну и так далее. Приведенный код да, время не большое. А дребезг?
А че дребезг? С ним хоть через прерывание, хоть напрямую - ситуация одинаковая, даже через прерывание хуже т.к. каждый фронт дребезга будет вызывать обработчик а это затраты времени в пустую. А алгоритм подавления дребезга, если он вобще есть и если вобще нужен - одинаковый.
4 ШД, требуемая частота импульсов для каждого разная один от 2400/сек до 4800/сек период соответственно от 415 мкСек до 208 мкСек. ну и так далее. Приведенный код да, время не большое. А дребезг?
Ага. Станцию подавления дребезга монтировать, если ума нет код писать.