Метод объекта в качестве обработчика прерывания

Seriga
Offline
Зарегистрирован: 01.06.2015
Здравствуйте.
 
Есть некий клас: MyClass. В котором я реализовал нужную мне логику.
В этом классе мне необходимо работать с прерыванием: attachInterrupt(interrupt, function, mode).
Я не могу в качестве  function (обработчика прерываний) подсунуть метод своего класса MyClass который не является static.
 
аргумент типа "void (MyClass::*)()" не совместим с параметром типа "void (*)()"
 
Приходится создавать глобальную функцию или метод static класса MyClass. 
Но это приводит к тому, что солидная часть логики находится вне класса MyClass хотя все, что делает обработчик относится к классу MyClass соответственно и должно быть спрятано в нем.
Да и все последующие классы, которые будут поочерёдно использовать прерывания должны выкидывать часть соей логики во вне. Код превращается в бардак. 
Как всё-таки подсунуть в качестве обработчика прерывания метод объекта? Под мою ответственность, что этот объект будет существовать пока обрабатывается прерывание?
 
ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Seriga пишет:

Как всё-таки подсунуть в качестве обработчика прерывания метод объекта? Под мою ответственность, что этот объект будет существовать пока обрабатывается прерывание?
Никак. 
 
А как Вы это себе представляете? При вызове метода ему всегда передаётся неявный параметр this. Кто Вам его из обработчика будет передавать?
 
Так что - никак.
 
Есть разные приёмы броьбы с этим. Например, обработчик знает адрес нужного экземпляра и по этому адресу (указателю) уже спокойно вызывает нормальный метод. Код в бардак не превращается. Подумайте в этом направлении.
Logik
Offline
Зарегистрирован: 05.08.2014

Можна только метод static или завернуть в обработчике прерывания вызов метода глобального обекта.

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Можно добавить в Arduino Framework дополнительную функцию attachInterruptEx, с помощью которой было бы возможно передавать this внутрь статического метода-обертки класса.

Если не боитесь кое-чего добавить в стандартные исходные файлы, я могу показать как это сделать. При этом функционал attachInterrupt останется.

Logik
Offline
Зарегистрирован: 05.08.2014

Так дело не в том, как его передать, дело в том где его взять при вызове обработчика, учитывая что прерывание может произойти в любой точке, например внутри delay(), где про this вобще не подозревают. Вот и получается что его нужно хранить в глобальной переменной (но тогда это по смыслу уже не  совсем this, это просто глобальный обект), а обращатся к нему из обработчика не проблема.  Ну и изменять его надо аккуратно, с запретами прерывания, код класса делать потокобезопастным по хорошему надо, ведь скорей всего часть методов из основного цикла будут вызыватся. 

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Я и предлагаю добавить в исходный код дополнительный функционал, который будет знать откуда взять this. Да, это будет нечто глобальное, но не просто скаляр, а массив. По аналогии с уже имеющимся кодом. Странно, что этого не было предусмотрено изначально.

Эта проблема часто возникает в unix системах, где системные вызовы используют стиль C, а код часто пишут на C++. Там предусмотрен дополнительный параметр void * у callback функции. Ничто не мешает нам добавить в описание userfunc такой же void * и передавать методы.

Работа с дополнительным параметром будет аналогична работе с указателем на функцию пользователя, поэтому уровень "безопасности" будет такой же как в исходном варианте.

На самом деле я всё это уже проделал и провёл некоторые испытания. Вроде работает. Включу в состав виртуальный машины потом. Это полезный функционал.

Logik
Offline
Зарегистрирован: 05.08.2014

Зачем? ))) Все это легко делается в скетче, надергал код из работающего.

OneWire Termometr;
....
//прерывания по таймеру
ISR( TIMER1_OVF_vect )
{
....
 Termometr.OneWireISR();

  
} 

void loop(void) 
{

      if(Termometr.Process(&t))
      {
......

Проблема безопасности в том, что вызов Termometr.OneWireISR(); может произойти во время выполнения Termometr.Process(&t) А безобидное Termometr=t1 может вызвать крах системы, но очень изредка, если прерывание какраз на него попадет. 

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

И где здесь использование attachInterrupt()? Если нужно будет в runtime поменять обработчик на другой метод, то что будуте делать? Реализовывать свой attachInterrupt?

Logik
Offline
Зарегистрирован: 05.08.2014

Да какая разница как код обработчика привязан к прерыванию. Проблемы остаются. 

Кину больше кода

//прерывания по таймеру
ISR( TIMER1_OVF_vect )
{
  BCLR(TIMSK1,TOIE1);

 switch (InterruptTimer1ID)
  {
       case UZD_INTERRUPT_TIMER_ID: UZ.Emmiting();break;
      case SERVOPRIVOD_INTERRUPT_TIMER_ID: Serva.ClearPulse();break;
       case ONE_WARE_TIMER_ID: Termometr.OneWireISR();break;
  }
  
} 

Про замену в рантайме думаю ясно. Сделано так именно для простого обеспечения безопасности замены обработчиков. InterruptTimer1ID байтовая, операции с ней атомарные и замена "обработчика" безопасно.

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Мне ваш подход не нравится, я бы спрятал всё в класс. В конструкторе написал бы attach( 2, object.method, mode, & object ), а в деструкторе detach(2). Раз объект знает время своего существования, то он и должен динамически управлять привязкой к прерываниям.

Logik
Offline
Зарегистрирован: 05.08.2014

Код кстати илюстрирует технику "разделения" одного таймера между несколькими устройствами: уз датчиком расстояния, сервоприводом в части его отключения когда он отработал позиционирование и опроса термометра ds18b20 та самая пауза 750мсек. Предполагается что все действия не перекрываются по времени. А есть код где это ограничение снято ;)  

Logik
Offline
Зарегистрирован: 05.08.2014

uni пишет:

Мне ваш подход не нравится, я бы спрятал всё в класс. В конструкторе написал бы attach( 2, object.method, mode, & object ), а в деструкторе detach(2). Раз объект знает время своего существования, то он и должен динамически управлять привязкой к прерываниям.

А смыслу прятать верхний уровень приложения в класс? Кроме того таймер разделяется между 3-я обектами один приатачился за ним второй, нужен список приатаченых - а это лишнее.

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Про привязку нескольких обработчиков речи не шло. Это вы уже сами придумали. Это другая задача. Здесь обсуждается привязка метода класса стандартным способом через attachInterrupt().

Что такое верхний уровень и что я спрятал?

Logik
Offline
Зарегистрирован: 05.08.2014

///Про привязку нескольких обработчиков речи не шло. Это вы уже сами пирдумали. 

Ага. Весь этот код я придумал. Вы спросили "Если нужно будет в runtime поменять обработчик на другой метод, то что будуте делать?" Поменять очевидно их болееодного. Я вам из проекта вытянул код где показал что делаю когда надо обрабатывать прерывание по разному. Вы наверно не понимаете разницу ПК и  МК. Здесь Вам не юникс, на момент начала выполнения кода всегда точно известно множество обработчиков - функций и/или методов класса(-ов), "проинсталировать" новый по ходу работы МК нельзя и смысла писать код позволяющий поменять на произвольный не следует, произвольного не бывает, можна только использовать что есть и нужно делать это эффективно.

// Здесь обсуждается привязка метода класса стандартным способом через attachInterrupt().

Уже обсудили все. Без глобальной переменной, где бы она не хранилась не реализуемо. Несогласны?

Верхний уровень, то что реализовано в *.ino и считается основным функционалом приложения. Прятать его логику в класс безсмысленно.

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Первое. "Атомарность" делается специальными макросами.

Нашёл здесь одно из описаний (AVR GCC: атомарно и не атомарно исполняемые блоки кода):

#include < util/atomic.h >
...
ATOMIC_BLOCK(AT OMIC_RESTORESTA TE)
{
   // блок кода с запрещенными прерываниями
}
...

Второе. Что-то вы всё в кучу намешали. У человека есть его собственный класс MyClass и он хочет при помощи attachInterrupt() назначить метод своего класса в качестве прерывания. Есть классический способ как это делается. Нужно в callback функцию передать указатель на экземпляр класса, чей метод используется. Если вы не хотите проблем с невалидным указателем, то нужно attach и detach делать внутри класса. Это логически обосновано тем, что только экземпляр объекта знает когда он существует (начал существовать и закончил существовать). Если же ваш экземпляр создаётся не динамически, а в глобальной области видимости, то привязку метода объекта можно делать вне класса.

Я предлагаю модифицировать attach, изменив тип callback функции (добавляется параметр void *). Повторяю, это классический метод совмещения C++ кода с API операционной системы (фреймворка), написанного на C.

Третье. Я первоначально не понял вашего кода со switch, т.к. подумал, что вы хотите привязать несколько методов к одному прерыванию. Теперь понятно, что вы показали статическую привязку, т.к. выбрать можно только то, что указано в switch, т.е. три фиксированных указателя. 

Да, такой подход очень простой. Но если у меня будет выбор между статической и динамической привязкой, то я выберу динамическую, т.к. это понятнее и гибче, нет лишних сущностей.

Четвёртое.

Вы наверно не понимаете разницу ПК и  МК. Здесь Вам не юникс, на момент начала выполнения кода всегда точно известно множество обработчиков - функций и/или методов класса(-ов), "проинсталировать" новый по ходу работы МК нельзя и смысла писать код позволяющий поменять на произвольный не следует, произвольного не бывает, можна только использовать что есть и нужно делать это эффективно.

Может объясните подробней, почему из одной функции (ISR) я не могу перейти по нужному мне указателю? Это табу такое? Что тогда делать разработчиками операционных систем? Представляете, они как раз в прерывании переходят вообще на произвольный адрес - тот, на котором остановился поток до прерывания. Может быть switch использовать? Раз все указатели предопределены, то мы их запишем в таблицу и сохраним индекс текущего адреса, так?

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Logik пишет:

Верхний уровень, то что реализовано в *.ino и считается основным функционалом приложения. Прятать его логику в класс безсмысленно.

Это вы setup() и loop() имеете в виду? Я этим не ограничен. Если я буду так думать, то как мне тогда реализовать многопоточность (добавить ОС) и использовать Arduino Framework? У меня есть примеры с использованием библиотеки scmRTOS, где я создаю два потока и вывожу текст через Serial.print одновременно в каждом.

Не нужно себя самоограничивать. Про это я уже тоже говорил. Невозможное - только в голове.

Logik
Offline
Зарегистрирован: 05.08.2014

Проверил тему поиском "невозможно" встретилось один раз в Вашем последнем сообщении. Вы о чем вобще? Или Вы знаете как осуществить вызов нестатического метода без сохранения обекта в глобальных? Раскажите.

По первому-третему.  Ну Вы ж понимаете что предлагаемое Вами в каждом пункте сложней. И я Вам скажу что оно будет больше жрать ресурсрв, и требует  перепахивания стандартных либ.  Я пишу коротко и эффективно. Одним словом KISS. Спорить по этому поводу будете?

///. Если вы не хотите проблем с невалидным указателем

Где у меня такое может быть, укажите строку ))) А вобще, не в привязке к моему коду, там в начале ТС этот момент оговаривал. Он гарантирует.

///нужно attach и detach делать внутри класса. 

Нет. Класс может допускать работу и без прерываний, тогда ничего атачится не предполагается. Например тот же датчик температуры требует продолжения работы через 750мс а как она отмерена - вобще не его дело и откуда его дернут после истечения времени в общем тоже, с учетом потокобезопасности. Получаем гибкий класс без делеев внутри и не привязанный к аппаратным особенностям. Например на OrangePI он тоже заработает, например время в отдельном потоке отмерят. Ваше решение  не обеспечит такое. Я знаю, Вы предложите иерархию класов у которого базовый наследуется 2-я один с прерываниями другой без ))) Ну в добрый путь, KISS ;)  

///если у меня будет выбор между статической и динамической привязкой, то я выберу динамическую

Ну Ваши ошибки - это Ваши ошибки, я то тут причем..

///Может объясните подробней, почему из одной функции (ISR) я не могу перейти по нужному мне указателю?

Можете. Но не все что возможно делать стоит делать. Считаем просто "поверхам" Атмега328, 32КБ кода, пару сотен байт на функцию, итого по максимуму около 200 функций, т.е. перейти можна 200 указателей. Из них осмыслено (не переходить же на strcmp по вызову прерывания) - ну пару десятков. Вполне так в свич, или константный массив лягут. Как говорил классик - переход кол-ва в к-во.  

///Это табу такое?

Нет. Специфика МК определяет оптимальный вариант из всех возможных. У МК такого много - например статические переменные не считаются грехом.

///Что тогда делать разработчиками операционных систем?

С ОС, многопоточностью, умными указателями и фабриками классов сидеть на ПК и не рыпатся на МК. 

///Представляете, они как раз в прерывании переходят вообще на произвольный адрес - тот, на котором остановился поток до прерывания. Может быть switch использовать? Раз все указатели предопределены, то мы их запишем в таблицу и сохраним индекс текущего адреса, так?

Про вытесняющую многопоточность только что писал ;) А вобще по форуму холиварили много раз. Это не для МК (про rtos в курсе - скачал задумчиво почитал и уже не помню где лежит). Хотя если бы кооперативную как в Вин3.11, ИМХО,  то думаю годилась бы. 

Logik
Offline
Зарегистрирован: 05.08.2014

uni пишет:

Это вы setup() и loop() имеете в виду? Я этим не ограничен. Если я буду так думать, то как мне тогда реализовать многопоточность (добавить ОС) и использовать Arduino Framework? У меня есть примеры с использованием библиотеки scmRTOS, где я создаю два потока и вывожу текст через Serial.print одновременно в каждом.

Не нужно себя самоограничивать. Про это я уже тоже говорил. Невозможное - только в голове.

А я могу сохранить указатель на функцию в EEPROM как строку, считать его конвертировав в void*, записать его в стек и по ret перейти. Только не знаю вот нафига? Но наверно гибкость будет замечательная )))

Сравните  "два потока и вывожу текст через Serial.print"  с аналогичной реализацией на машинке состояний и все поймете чисто в цифрах.  

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Вот ссылка на реализацию TelnetServer. Там два потока, один из них работает по сети (сам сервер). Используется scmRTOS и всё то, чего вы осилить, судя по всему уже никогда не сможете.

Я пытался... но словоблуд тут силён больше.

Logik
Offline
Зарегистрирован: 05.08.2014

И точно, Вы правы наконецто, это ссылка! Это я типа её смотреть должен... Вроде не просил, ну ладно гляну по быстрому, например самое главное Program.cpp


void setup()
{
    Serial.begin( 115200 );

    char s1[32], s2[32];

    strncpy_P( s1, ( const char * ) CVersion::GetVersionString(), sizeof( s1 ) );
    strncpy_P( s2, ( const char * ) CVersion::GetBuildDateString(), sizeof( s2 ) );

    s1[ sizeof( s1 ) - 1 ] = '\0';
    s1[ sizeof( s2 ) - 1 ] = '\0';

    logdebug( "Telnet Server, version %s, %s", s1, s2 );

    pinMode( 10, OUTPUT );
    digitalWrite( 10, LOW );

    //pinMode( 21, OUTPUT );
    //digitalWrite( 21, HIGH );

    // Запрещаем перывания от таймера.
    OS::lock_system_timer();

    // Настраиваем таймер для scmRTOS.
    // clk/64, CTC mode.
    TIMER_CS_REG = ( 1 << WGM32 ) | ( 1 << CS31 ) | ( 1 << CS30 );

    OCR3AL = 250;

    // Разрешаем прерывания от таймера.
    OS::unlock_system_timer();

    OS::run();
}

Что вижу - меджикнамберы, приведение хрена к пальцу, работа с портом через digitalWrite, закоментированый код какойто. Его смотреть или нет? Хотя больше не хочется.

Ну реализовали вы эхо используя известные либы w5100. И че? Или вы  считаете что без потоков и ОС это не реализуемо? Или решили меня потрясти масштабом проекта. Пишите как реализуете чегонить такого уровня, чтоб хоть выглядело  похоже:

Контроллер аквариума - Промини, часы РВ, сервопривод, ШИМ освещения 4 канала, датчики температуры ds - 3шт, датчик влажности, экран что очевидно, тач. Все либы свои, включая GUI.   Все работает абсолютно одновременно, навигация по меню, с 10-к экранов на работу переферии не сказывается. Потоков нет.

Или так. STM32 тот который Мапле, енкодер, SD и экран снова. Опять таки работа энкодера и навигация на оцифровке 2МГц не влияют. И снова без потоков.

Теперь хочу чтобы картинка ожила - https://www.youtube.com/watch?v=5-Y1tt14xsA&feature=youtu.be 

Старт на OrangePi. Сейчас там еще серва управляется, на Хелоин тыкву поворачивала, и по i2c АЦП внешнее висит. И все работает с любого броузера по WinSocket, с криптографией по протоколу. Но тут уже два потока - виринг серву крутит в своем.

Так что телнетом зря удивить пытались. Вобщем как сделаете чего интересного на своей ОС - пишите. А по пустякам не раздражай здесь дядь, таких как Вы тут в месяц по три штуки с мира ПК падает, то ОСь запускают то про многопоточность печалятся, то стандартные либы дорабатывают ))) Сделайте что полезное хоть для себя, ато телнет с эхой в два потока...

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Что вижу - меджикнамберы, приведение хрена к пальцу, работа с портом через digitalWrite, закоментированый код какойто. Его смотреть или нет? Хотя больше не хочется.

Ну реализовали вы эхо используя известные либы w5100. И че?

Вы понять даже не смогли что это такое, зачем оцениваете? Вам показали работающую библиотеку для организации операционной системы на Arduino, которую можно взять и использовать, а вы что увидели? До чего опыта хватило? Смотришь в книгу - видишь фигу?

///Что тогда делать разработчиками операционных систем?

С ОС, многопоточностью, умными указателями и фабриками классов сидеть на ПК и не рыпатся на МК. 

///Представляете, они как раз в прерывании переходят вообще на произвольный адрес - тот, на котором остановился поток до прерывания. Может быть switch использовать? Раз все указатели предопределены, то мы их запишем в таблицу и сохраним индекс текущего адреса, так?

Про вытесняющую многопоточность только что писал ;) А вобще по форуму холиварили много раз. Это не для МК (про rtos в курсе - скачал задумчиво почитал и уже не помню где лежит). Хотя если бы кооперативную как в Вин3.11, ИМХО,  то думаю годилась бы. 

Может просто вы не умеете её "готовить"? И все остальные, которые про это только слышали, а разобраться самостоятельно так и не смогли.

Конечно-автоматный путь - тупиковый для подобных систем. Их никто кроме вас не будет сопровождать. В сложных проектах важна декомпозиция частей. В ОС она достигается (можно видеть в моём коде, где setup() и loop() у каждого потока свои и они работают параллельно, т.е. вечный цикл в одном потоке не останавливает второй). Платой является сложность при работе с потоками и разделение доступа к ресурсам.

Это прототип для другой задумки: ПЛК (Arduino Mega 2560 + W5100). В виду её сложности я перешёл на использование ОС, т.к. знаю, что по-другому это реализовать практически невозможно.

Я делаю это на avr, т.к. мне нужны подобные модули в множественном количестве (десятки). Чем дешевле вычислительно/управляющий блок получится, тем больше я смогу их использовать. Любой может взять даже роутер и сделать на нём то же самое, но у этого есть цена в прямом и переносном смысле, что ограничивает использование такого подхода массово.

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

uni пишет:

Конечно-автоматный путь - тупиковый 

Мне казалось, что Ваше оппонент говорил не конечных авоматах, а об автоматном программировании. Вы не видите разницы? Или считаете, что он говорил именно о конечных автоматах?

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Я считаю, что не нужно останавливаться на достигнутом!

Мне вот, в разработке, очень не хватает стандартного fork().

Причем важно, чтобы работал он на 328-ом контроллере, так как 2560 - это капиталистические излишества. Согласны?

-----------------

Uni! Прошу не обижаться на шутку, просто не удержался.

Не мне Вас судить, я на таймер-хуки в детстве даже для DOS 6.22 некий вариант многозадачности  (скорее это был мультитред ;) ) вешал и был очень горд собой, очень удивлялся, почему это коллеги-программисты не дали мне сразу канистру спирта и премию ;).

Развлекайтесь.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

wdrakula пишет:

Мне вот, в разработке, очень не хватает стандартного fork().

Да, чего мелочиться, заодно уж и MPI сделать - можно будет кластеры из 100500 ардуин лепить

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

ЕвгенийП пишет:

 Или считаете, что он говорил именно о конечных автоматах?

Если быть математически точным то есть всего ТРИ типа автоматов:

конечный, стековый (в русскоязычной литературе "автоматы с магазинной памятью" - мне всегда бьло интересно - из какого магазина? ;) ) и бесконечный - он же машина Тьюринга.

Каждый из них  может быть как детерминированным так и недетерминированным.

.....

Эх молодость! Сейчас не удержусь - приму 150 грамм и пойду Великую теорему Гёделя доказывать... например собаке, так как жена уже не может больше слушать это доказательство ;).

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

wdrakula,

но здесь-то как я понимаю, речь шла об автоматном программировании, а это воплне устоявшийся термин и к автоматоам в метаматеическом сымсле он имеет, скажем так, косвенное отношение.

Logik
Offline
Зарегистрирован: 05.08.2014

uni пишет:

Вот ссылка на реализацию TelnetServer

Вы понять даже не смогли что это такое, зачем оцениваете? Вам показали работающую библиотеку для организации операционной системы на Arduino, которую можно взять и использовать, а вы что увидели? До чего опыта хватило? Смотришь в книгу - видишь фигу?

Вот первая ссылка https://mysvn.ru/avr/TelnetServer/ 

вот вторя https://mysvn.ru/avr/TelnetServer/lib/scmRTOS/

Д-з: криворукость в запущеной стадии. Давайте третю попытку делайте, что хотите показать.  Приложение или либу? Кстати про то что rtos видел и вероятно много ранее вашего я писал там..

 

 

uni пишет:

Может просто вы не умеете её "готовить"?

Канешно. Десятилетиями на хлеб себе разработкой ПО зарабатывал и не умею. Хамиш парниша. 

 

uni пишет:

 Их никто кроме вас не будет сопровождать. 

Мой домашний код кроме меня никто не сопровождает.

uni пишет:

В сложных проектах важна декомпозиция частей. В ОС она достигается (можно видеть в моём коде, где setup() и loop() у каждого потока свои и они работают параллельно, т.е. вечный цикл в одном потоке не останавливает второй). 

 

Совсем в бред завалился. Декомпозици в проекте - разделение на модули, классы, библиотеки и т.д.  выполняющие функционально завершенные части проекта. Учи матчасть потоки к декомпозиции и сопровождению кода никаким боком.

 

uni пишет:

Это прототип для другой задумки: ПЛК (Arduino Mega 2560 + W5100)

Задумку не обсуждаю. Здесь таких задумок десяток в день, меня нак все нехвати. Сделаете хоть шото работающее и полезное - продемонстрируете свои способности в деле тогда и поговорим. Непристало спорить с выпускниками курсов "с++ за две недели" уверенных что все должно быть в классах и потоках. Наберитесь опыта в программировании МК - сами поймете.

uni пишет:

В виду её сложности я перешёл на использование ОС, т.к. знаю, что по-другому это реализовать практически невозможно.

Оно как нет понимания - все сложно, а когда научитесь, выкинете ОС то и в атмегу1280 влезете. А может и в 328.

uni пишет:

Чем дешевле вычислительно/управляющий блок получится, тем больше я смогу их использовать. Любой может взять даже роутер и сделать на нём то же самое, но у этого есть цена в прямом и переносном смысле, что ограничивает использование такого подхода массово.

Так это Вы должны обяснять почему ОС и многопоточность раздувают код и аовышают стоимость проекта, а не я.

Глянул ссылку "Сервер для Arduino Mega и Ethernet Shield W5100 с SD картой с полноценным (местами даже продвинутым :) интерфейсом, поддержкой Ajax и без ограничений на размер и количество файлов." Четыре года как задумка... 8))))

  Вы там видео, выше ссылка была, смотрели про оранжПи? Так вот это называется стартовый проект. Ну типа хелоуворд, но чуть лучше.  Здесь втыкать подробней.

http://arduino.ru/forum/otvlechennye-temy/orange-pi-one-nuzhen-start?page=1#comment-221181

Там все, загрузка HTML "без ограничений на размер и количество файлов", ну FTP разумеется, только вот вместо аякса - вебсокет. Это потому что аякс - отстой, он дает слишком большой трафик, пару килобайт на сообщение из за чего начинается тормозня. Вебсокет имеет небольшую избыточность и легко, без левых либ, работает из JS. И скорость проверял, там на видео приходят циферки отправляемые каждые 30мс. На видео оно все уже работает, там все видно. 

Вы вобщем понимаете что выходит: ваша 4-й год задумка нереализуемая без потоков и ОС - очень похожа на мой стартовый проект на новой платформе, сделаный не спеша за пару недель (это с поисками разема, пайкой светодиода и установкой Дума;), плата пришла 15 августа, скрин доставки с али могу показать. 

Пожалуй я зря с Вами в дискусию вступил. )))

ПС. Да  ПЛК (Arduino Mega 2560 + W5100).  вполне на 328+SD+w5100 делается.

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Вы может быть удивитесь, но Microsoft до сих пор продаёт DOS для панельных ПК. И их используют по той причине, что загружается такой ПК быстро, программе доступно всё и сразу и кода для него в своё время было наработано много. Для программ типа осциллографов - самое то. На одной из моих работ такой вот ПК использовался (продавался) в качестве графического регистратора.

Сейчас я пишу код на uClinux и fork'а там нет, т.к. нет менеджера памяти и всё находится в одном адресном пространстве, а если вы переходите на полноценные ОС, то получаете длительную загрузку и большой головняк в виде кросс-компиляции.

-----------------------------------------------------------------------------

Вернёмся к первоначальному вопросу. Итак, как же "привязывать" метод экземпляра класса в качестве обработчика прерывания?

Для этого модифицируем немного исходники Arduino Framework'а. Мы не будем ничего удалять, а только добавим функционала для сохранения совместимости. Сначала я расскажу что поменять, потом дам пример и расскажу как это работает. В качестве благодарности можете протестировать и поискать ошибки/предложить свою реализаци.

Шаг 1. Файл wiring_private.h. Ищем строчку:

typedef void (*voidFuncPtr)(void);

и добавляем ниже описание нового типа:

typedef void (*voidFuncPtr)(void);
typedef void (*voidFuncPtrEx)(void *);

Шаг 2. Файл WInterrupts.c. Добавляем массив, некоторые функции и кое-чего внутри attachInterruptEx():

/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */

/*
  Part of the Wiring project - http://wiring.uniandes.edu.co

  Copyright (c) 2004-05 Hernando Barragan

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General
  Public License along with this library; if not, write to the
  Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  Boston, MA  02111-1307  USA
  
  Modified 24 November 2006 by David A. Mellis
  Modified 1 August 2010 by Mark Sproul
*/

#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <stdio.h>

#include "wiring_private.h"

static void nothing(void * data) {
}

static void * intData[EXTERNAL_NUM_INTERRUPTS] = {
#if EXTERNAL_NUM_INTERRUPTS > 8
    #warning There are more than 8 external interrupts. Some callbacks may not be initialized.
    NULL,
#endif
#if EXTERNAL_NUM_INTERRUPTS > 7
    NULL,
#endif
#if EXTERNAL_NUM_INTERRUPTS > 6
    NULL,
#endif
#if EXTERNAL_NUM_INTERRUPTS > 5
    NULL,
#endif
#if EXTERNAL_NUM_INTERRUPTS > 4
    NULL,
#endif
#if EXTERNAL_NUM_INTERRUPTS > 3
    NULL,
#endif
#if EXTERNAL_NUM_INTERRUPTS > 2
    NULL,
#endif
#if EXTERNAL_NUM_INTERRUPTS > 1
    NULL,
#endif
#if EXTERNAL_NUM_INTERRUPTS > 0
    NULL,
#endif
};

static volatile voidFuncPtrEx intFunc[EXTERNAL_NUM_INTERRUPTS] = {
#if EXTERNAL_NUM_INTERRUPTS > 8
    #warning There are more than 8 external interrupts. Some callbacks may not be initialized.
    nothing,
#endif
#if EXTERNAL_NUM_INTERRUPTS > 7
    nothing,
#endif
#if EXTERNAL_NUM_INTERRUPTS > 6
    nothing,
#endif
#if EXTERNAL_NUM_INTERRUPTS > 5
    nothing,
#endif
#if EXTERNAL_NUM_INTERRUPTS > 4
    nothing,
#endif
#if EXTERNAL_NUM_INTERRUPTS > 3
    nothing,
#endif
#if EXTERNAL_NUM_INTERRUPTS > 2
    nothing,
#endif
#if EXTERNAL_NUM_INTERRUPTS > 1
    nothing,
#endif
#if EXTERNAL_NUM_INTERRUPTS > 0
    nothing,
#endif
};
// volatile static voidFuncPtr twiIntFunc;

static void _helper( void * data )
{
  if ( data == NULL ) return;

  voidFuncPtr userFunc = ( voidFuncPtr ) data;

  userFunc(); 	
}


void attachInterrupt(uint8_t interruptNum, void (*userFunc)(void), int mode) {

  return attachInterruptEx( interruptNum, _helper, mode, userFunc );
}

void attachInterruptEx(uint8_t interruptNum, void (*userFunc)(void *), int mode, void * data ) {
  if(interruptNum < EXTERNAL_NUM_INTERRUPTS) {
    intFunc[interruptNum] = userFunc;
    intData[interruptNum] = data;


    // Configure the interrupt mode (trigger on low input, any change, rising
    // edge, or falling edge).  The mode constants were chosen to correspond
    // to the configuration bits in the hardware register, so we simply shift
    // the mode into place.
      
    // Enable the interrupt.
      
    switch (interruptNum) {
#if defined(__AVR_ATmega32U4__)
	// I hate doing this, but the register assignment differs between the 1280/2560
	// and the 32U4.  Since avrlib defines registers PCMSK1 and PCMSK2 that aren't 
	// even present on the 32U4 this is the only way to distinguish between them.
    case 0:
	EICRA = (EICRA & ~((1<<ISC00) | (1<<ISC01))) | (mode << ISC00);
	EIMSK |= (1<<INT0);
	break;
    case 1:
	EICRA = (EICRA & ~((1<<ISC10) | (1<<ISC11))) | (mode << ISC10);
	EIMSK |= (1<<INT1);
	break;	
    case 2:
        EICRA = (EICRA & ~((1<<ISC20) | (1<<ISC21))) | (mode << ISC20);
        EIMSK |= (1<<INT2);
        break;
    case 3:
        EICRA = (EICRA & ~((1<<ISC30) | (1<<ISC31))) | (mode << ISC30);
        EIMSK |= (1<<INT3);
        break;
    case 4:
        EICRB = (EICRB & ~((1<<ISC60) | (1<<ISC61))) | (mode << ISC60);
        EIMSK |= (1<<INT6);
        break;
#elif defined(EICRA) && defined(EICRB) && defined(EIMSK)
    case 2:
      EICRA = (EICRA & ~((1 << ISC00) | (1 << ISC01))) | (mode << ISC00);
      EIMSK |= (1 << INT0);
      break;
    case 3:
      EICRA = (EICRA & ~((1 << ISC10) | (1 << ISC11))) | (mode << ISC10);
      EIMSK |= (1 << INT1);
      break;
    case 4:
      EICRA = (EICRA & ~((1 << ISC20) | (1 << ISC21))) | (mode << ISC20);
      EIMSK |= (1 << INT2);
      break;
    case 5:
      EICRA = (EICRA & ~((1 << ISC30) | (1 << ISC31))) | (mode << ISC30);
      EIMSK |= (1 << INT3);
      break;
    case 0:
      EICRB = (EICRB & ~((1 << ISC40) | (1 << ISC41))) | (mode << ISC40);
      EIMSK |= (1 << INT4);
      break;
    case 1:
      EICRB = (EICRB & ~((1 << ISC50) | (1 << ISC51))) | (mode << ISC50);
      EIMSK |= (1 << INT5);
      break;
    case 6:
      EICRB = (EICRB & ~((1 << ISC60) | (1 << ISC61))) | (mode << ISC60);
      EIMSK |= (1 << INT6);
      break;
    case 7:
      EICRB = (EICRB & ~((1 << ISC70) | (1 << ISC71))) | (mode << ISC70);
      EIMSK |= (1 << INT7);
      break;
#else		
    case 0:
    #if defined(EICRA) && defined(ISC00) && defined(EIMSK)
      EICRA = (EICRA & ~((1 << ISC00) | (1 << ISC01))) | (mode << ISC00);
      EIMSK |= (1 << INT0);
    #elif defined(MCUCR) && defined(ISC00) && defined(GICR)
      MCUCR = (MCUCR & ~((1 << ISC00) | (1 << ISC01))) | (mode << ISC00);
      GICR |= (1 << INT0);
    #elif defined(MCUCR) && defined(ISC00) && defined(GIMSK)
      MCUCR = (MCUCR & ~((1 << ISC00) | (1 << ISC01))) | (mode << ISC00);
      GIMSK |= (1 << INT0);
    #else
      #error attachInterrupt not finished for this CPU (case 0)
    #endif
      break;

    case 1:
    #if defined(EICRA) && defined(ISC10) && defined(ISC11) && defined(EIMSK)
      EICRA = (EICRA & ~((1 << ISC10) | (1 << ISC11))) | (mode << ISC10);
      EIMSK |= (1 << INT1);
    #elif defined(MCUCR) && defined(ISC10) && defined(ISC11) && defined(GICR)
      MCUCR = (MCUCR & ~((1 << ISC10) | (1 << ISC11))) | (mode << ISC10);
      GICR |= (1 << INT1);
    #elif defined(MCUCR) && defined(ISC10) && defined(GIMSK) && defined(GIMSK)
      MCUCR = (MCUCR & ~((1 << ISC10) | (1 << ISC11))) | (mode << ISC10);
      GIMSK |= (1 << INT1);
    #else
      #warning attachInterrupt may need some more work for this cpu (case 1)
    #endif
      break;
    
    case 2:
    #if defined(EICRA) && defined(ISC20) && defined(ISC21) && defined(EIMSK)
      EICRA = (EICRA & ~((1 << ISC20) | (1 << ISC21))) | (mode << ISC20);
      EIMSK |= (1 << INT2);
    #elif defined(MCUCR) && defined(ISC20) && defined(ISC21) && defined(GICR)
      MCUCR = (MCUCR & ~((1 << ISC20) | (1 << ISC21))) | (mode << ISC20);
      GICR |= (1 << INT2);
    #elif defined(MCUCR) && defined(ISC20) && defined(GIMSK) && defined(GIMSK)
      MCUCR = (MCUCR & ~((1 << ISC20) | (1 << ISC21))) | (mode << ISC20);
      GIMSK |= (1 << INT2);
    #endif
      break;
#endif
    }
  }
}

void detachInterrupt(uint8_t interruptNum) {
  if(interruptNum < EXTERNAL_NUM_INTERRUPTS) {
    // Disable the interrupt.  (We can't assume that interruptNum is equal
    // to the number of the EIMSK bit to clear, as this isn't true on the 
    // ATmega8.  There, INT0 is 6 and INT1 is 7.)
    switch (interruptNum) {
#if defined(__AVR_ATmega32U4__)
    case 0:
        EIMSK &= ~(1<<INT0);
        break;
    case 1:
        EIMSK &= ~(1<<INT1);
        break;
    case 2:
        EIMSK &= ~(1<<INT2);
        break;
    case 3:
        EIMSK &= ~(1<<INT3);
        break;	
    case 4:
        EIMSK &= ~(1<<INT6);
        break;	
#elif defined(EICRA) && defined(EICRB) && defined(EIMSK)
    case 2:
      EIMSK &= ~(1 << INT0);
      break;
    case 3:
      EIMSK &= ~(1 << INT1);
      break;
    case 4:
      EIMSK &= ~(1 << INT2);
      break;
    case 5:
      EIMSK &= ~(1 << INT3);
      break;
    case 0:
      EIMSK &= ~(1 << INT4);
      break;
    case 1:
      EIMSK &= ~(1 << INT5);
      break;
    case 6:
      EIMSK &= ~(1 << INT6);
      break;
    case 7:
      EIMSK &= ~(1 << INT7);
      break;
#else
    case 0:
    #if defined(EIMSK) && defined(INT0)
      EIMSK &= ~(1 << INT0);
    #elif defined(GICR) && defined(ISC00)
      GICR &= ~(1 << INT0); // atmega32
    #elif defined(GIMSK) && defined(INT0)
      GIMSK &= ~(1 << INT0);
    #else
      #error detachInterrupt not finished for this cpu
    #endif
      break;

    case 1:
    #if defined(EIMSK) && defined(INT1)
      EIMSK &= ~(1 << INT1);
    #elif defined(GICR) && defined(INT1)
      GICR &= ~(1 << INT1); // atmega32
    #elif defined(GIMSK) && defined(INT1)
      GIMSK &= ~(1 << INT1);
    #else
      #warning detachInterrupt may need some more work for this cpu (case 1)
    #endif
      break;
      
    case 2:
    #if defined(EIMSK) && defined(INT2)
      EIMSK &= ~(1 << INT2);
    #elif defined(GICR) && defined(INT2)
      GICR &= ~(1 << INT2); // atmega32
    #elif defined(GIMSK) && defined(INT2)
      GIMSK &= ~(1 << INT2);
    #elif defined(INT2)
      #warning detachInterrupt may need some more work for this cpu (case 2)
    #endif
      break;       
#endif
    }
      
    intFunc[interruptNum] = nothing;
    intData[interruptNum] = NULL;
  }
}

/*
void attachInterruptTwi(void (*userFunc)(void) ) {
  twiIntFunc = userFunc;
}
*/

#define IMPLEMENT_ISR(vect, interrupt) \
  ISR(vect) { \
    intFunc[interrupt]( intData[interrupt] ); \
  }

#if defined(__AVR_ATmega32U4__)

IMPLEMENT_ISR(INT0_vect, EXTERNAL_INT_0)
IMPLEMENT_ISR(INT1_vect, EXTERNAL_INT_1)
IMPLEMENT_ISR(INT2_vect, EXTERNAL_INT_2)
IMPLEMENT_ISR(INT3_vect, EXTERNAL_INT_3)
IMPLEMENT_ISR(INT6_vect, EXTERNAL_INT_4)

#elif defined(EICRA) && defined(EICRB)

IMPLEMENT_ISR(INT0_vect, EXTERNAL_INT_2)
IMPLEMENT_ISR(INT1_vect, EXTERNAL_INT_3)
IMPLEMENT_ISR(INT2_vect, EXTERNAL_INT_4)
IMPLEMENT_ISR(INT3_vect, EXTERNAL_INT_5)
IMPLEMENT_ISR(INT4_vect, EXTERNAL_INT_0)
IMPLEMENT_ISR(INT5_vect, EXTERNAL_INT_1)
IMPLEMENT_ISR(INT6_vect, EXTERNAL_INT_6)
IMPLEMENT_ISR(INT7_vect, EXTERNAL_INT_7)

#else

IMPLEMENT_ISR(INT0_vect, EXTERNAL_INT_0)
IMPLEMENT_ISR(INT1_vect, EXTERNAL_INT_1)

#if defined(EICRA) && defined(ISC20)
IMPLEMENT_ISR(INT2_vect, EXTERNAL_INT_2)
#endif

#endif

/*
ISR(TWI_vect) {
  if(twiIntFunc)
    twiIntFunc();
}
*/

Сравните его содержимое со своим файлом и сделайте изменения, либо просто замените его содержимое (на свой страх и риск конечно).

Шаг 3. Добавляем описание функции в заголовочник Arduino.h:

void attachInterrupt(uint8_t, void (*)(void), int mode);
void attachInterruptEx(uint8_t interruptNum, void (*userFunc)(void *), int mode, void * data );
void detachInterrupt(uint8_t);

Вот вроде и всё, демонстрационный пример:

 

#include <Arduino.h>
#include <compat/deprecated.h>


class MyClass
{
private:
	int a;
	
public:
	static void MyISR( void * data )
	{
		MyClass * self = reinterpret_cast< MyClass * >( data );
		
		self->a += 1;
	}
	
public:
	MyClass() {}
	~MyClass() {}
};

long old_ms = 0;
MyClass class1;


void blink(void);

void setup() 
{
	attachInterrupt( 2, blink, RISING );
	
	//attachInterruptEx( 2, MyClass::MyISR, RISING, ( void * ) & class1 );	
}


void loop() 
{
	if ( millis() - old_ms > 1000 )
	{
		old_ms = millis();
		
		sbi( PORTD, PD0 );
		cbi( PORTD, PD0 );
	}
}

void blink(void) 
{
	MyClass::MyISR( ( void * ) & class1 );
}

Код:

	if ( millis() - old_ms > 1000 )
	{
		old_ms = millis();
		
		sbi( PORTD, PD0 );
		cbi( PORTD, PD0 );
	}

нужен для формирования программно прерывания INT0. Правда срабатывает он только один раз при старте почему-то. Но для теста в симуляторе этого достаточно.

Как оно работает. В примере показано, что старый функционал остался, т.е. attachInterrupt() должен работать как прежде (или почти ;) ). Новая функция attachInterruptEx() делает то же, но принимает расширенный набор параметров. Во-первых, тип прототип функции содержит параметр void *, во-вторых, нам нужно передать зачение этого параметра. Сохраняется этот параметр также как и указатель на callback функцию в массиве. Для каждого типа прерывания сохраняется значение своего параметра. Это тот самый глобальный параметр, но в отличие от его явного наличия в коде пользователя, здесь работа с ним сокрыта в Arduino Framework и вам не нужно думать про это, используя функцию привязки.

Когда вы вызываете attachInterruptEx() во внутренних массивах сохраняется указатель на метод и дополнительный параметр. Если этим параметром указать this, то внутри статического метода-обёртки (он является вспомогательным) вы можете восстановить этот указатель и вызвать нестатический его обработчик (в примере этого нет). Фактически мы получим вызов метода конкретного указанного экземпляра класса.

Пробуйте.

Logik
Offline
Зарегистрирован: 05.08.2014

Щас я полезу wiring_private.h править, уже разогнался ;) 

"Работает - не трогай!"

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Logik пишет:

Щас я полезу wiring_private.h править, уже разогнался ;) 

"Работает - не трогай!"

Код не для вас, а для того, кто спрашивал. Мы пойдём вперёд, а вы оставайтесь при своём мнении.

Logik
Offline
Зарегистрирован: 05.08.2014

Шо за хрень Вы написали ))

Строку 50 заменяем на class1.MyISR(...  и код работает без каких либо изменений  wiring_private.h. Это просто писец... )))

Хороше что это не для меня писали ))) 

Я - пас господа! Окружающие косятся, понять немогут чего ржу...

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Logik пишет:

Шо за хрень Вы написали ))

Строку 50 заменяем на class1.MyISR(...  и код работает без каких либо изменений  wiring_private.h. Это просто писец... )))

Хороше что это не для меня писали ))) 

Я - пас господа! Окружающие косятся, понять немогут чего ржу...

"Дядя Петь, ты дурак?" (с)

Logik
Offline
Зарегистрирован: 05.08.2014

Так тебя Петром значить кличут. Так чегож, Петя за четыре года, с передовыми методами  так и не реализовал Arduino Mega Server?

 

Клапауций 234
Offline
Зарегистрирован: 24.10.2016

Logik пишет:

Так тебя Петром значить кличут. Так чегож, Петя за четыре года, с передовыми методами  так и не реализовал Arduino Mega Server?

я запретил.

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Logik пишет:

Так тебя Петром значить кличут. Так чегож, Петя за четыре года, с передовыми методами  так и не реализовал Arduino Mega Server?

Потому что AMS - это статический велосипед. Там обработка ведётся статично в коде, а скрипты на ПК верхнего уровня. Меня это не устраивает, я хочу иметь скрипты, которые позволят динамически управлять без верхнего уровня. В этом большая разница, которую почему-то трудно понять.

По теме, вот пример как обёртывать системную C-функцию при помощи C++ класса. У меня сделано то же самое.

Logik
Offline
Зарегистрирован: 05.08.2014

Не Петь, без верхнего уровня - никак невозможно. От представь. Софт до доработки, так, есть какойто верхний уровень. Дорабатываеш его, Петь, убираеш верхний уровень, и что - уровень который был вторым сверху автоматически становится верхним! Миссия не выполнима! 

Но теперь я понимаю путь развитие Вашего, Петь проекта.

ПС. Петь, не пропадай, я ща поделам, вернусь еще поржу.

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Logik пишет:

Не Петь, без верхнего уровня - никак невозможно. От представь. Софт до доработки, так, есть какойто верхний уровень. Дорабатываеш его, Петь, убираеш верхний уровень, и что - уровень который был вторым сверху автоматически становится верхним! Миссия не выполнима! 

Но теперь я понимаю путь развитие Вашего, Петь проекта.

Вам к доктору может обратиться? Самому себе пишите? О своём, о личном думу думаете? Когда вы гримасничаете по поводу стандартов в промышленной автоматике, вы выглядите как пациент. Чем дальше, тем хуже.

Seriga
Offline
Зарегистрирован: 01.06.2015

Спасибо всем за помощь.

Попробовал сделать как советовал “ЕвгенийП”. К остальным предложенным вариантом я даже и не знаю, как подступится.

Создал статический указатель, в который конструктор передавал адрес созданного объекта.

Код начал обрастать “костылями”.

Например, переменная класса обозначенная как volatile наотрез отказалась изменятся в обработчике события через созданный мною указатель на объект. Так-же когда я попытался обратится к массивам через указатель в обработчике события то получил совершенно не то что было в этом массиве.  Да и наверно и еще что-то чего я уже не вспомню.

В общем фокус не удался. Решил оставить как есть.

Logik
Offline
Зарегистрирован: 05.08.2014

Seriga пишет:

Создал статический указатель, в который конструктор передавал адрес созданного объекта.

))) Вы все совсем не так поняли. Лезть в конструктор не нужно.

Seriga пишет:

Например, переменная класса обозначенная как volatile наотрез отказалась изменятся в обработчике события через созданный мною указатель на объект. Так-же когда я попытался обратится к массивам через указатель в обработчике события то получил совершенно не то что было в этом массиве.  Да и наверно и еще что-то чего я уже не вспомню.

Проще сказать, что указатель вышел невалидный. Если надумаете дальше боротся - код в студию.

Logik
Offline
Зарегистрирован: 05.08.2014

Держите пример.


class MyClass
{
  private:
  	int a;
  protected:
        int GetCnt(){return a++;}
  public:
	virtual void MyISR(void){}

	MyClass() {}
	~MyClass() {}
};

class MyBlClass : MyClass
{
  public:
    void MyISR(void){digitalWrite(13, GetCnt()&1);}
};


MyBlClass class1;


void blink(void);

void setup() 
{
	attachInterrupt( 0, blink, RISING );
}


void loop() {}

void blink(void) 
{
   class1.MyISR();
}

Даже проверил. Все просто и понятно. Никаких правок ядра (которые надо будет применять к каждой новой версии IDE, и отломается совместимость с другими разработчиками), неявных привязок в конструкторе и мутных разименовангий, класс одинаково рабочий как через прерывание так и без. Специально пример сделал с наследованием и виртуальностю, все корректно.

После заливки тыкаете железным предметом в пин2. Светодиод начинает моргать, у меня даже без контакта с расстояния в 5мм, ну это от интелекта зависит ;)  Годится быть генератором случайных чисел, типа орел или решка )))

По коду. В глобальной class1 наш обект обработчик прерывания blink "обертка" для вызова метода, сделал его даже виртуальным, чтоб уж точно не статический.

ПС. uni проилюстрировал поговорку "Хуже дурака только дурак с инициативой", засрал тему, в результате ТС уже готов отказатся от замысла.

О! Этот Петя теперь меня минусует! А шо такое, Петя? Ну я хоть знаю что он код видел ;)

ППС. Ща снова какуюто дурость заумную начнет писать про убирание "верхнего уровня", и постить непровереный код. Потом окажется что он не компилируется без правок системных библиотек, и вобще такие вещи по 4 года делаются, главное рассово верный код, а результат не важен ))))

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Используя мой метод, в вашем коде не будет лишних глобальных идентификаторов. Если нужно, можно вообще всё всё спрятать в класс:

#include <Arduino.h>

class BasicClass
{
private:
    bool _attached;
	int _interrupt;
	
protected:
    void AttachInterrupt( int interrupt ) 
    {
        _interrupt = interrupt;
        
        attachInterruptEx( interrupt, ISRHelper, RISING, this );	
        
        _attached = true;
    };
    
    void MyISR()
    {
        // ...
    }
    
public:
	static void ISRHelper( void * data )
	{
		BasicClass * self = reinterpret_cast< BasicClass * >( data );

        self->MyISR();
	}
	
public:
	BasicClass() 
    { 
        _attached = false;
        _interrupt = -1;
    }
    
	~BasicClass() 
    { 
        if ( _attached ) 
        {
            detachInterrupt( _interrupt ); 
            _interrupt = -1;
        }
    }
};


BasicClass class1;


void setup() 
{
    class1.AttachInterrupt(2);
}


void loop() {}

Либо назначать методы других объектов, вот так:

class BasicClass
{
public:
	static void ISRHelper( void * data )
	{
		BasicClass * self = reinterpret_cast< BasicClass * >( data );

        self->MyISR();
	}
   
    void MyISR()
    {
        // ...
    }
	
public:
	BasicClass() {}
};


BasicClass class1;
BasicClass class2;


void setup() 
{
    // Назначаем метод class1.MyISR() в качестве обработчика прерывания 2.
    attachInterruptEx( 2, class1.ISRHelper, RISING, ( void * ) & class1 );
    
    // ...
    
    // Назначаем метод class2.MyISR() в качестве обработчика прерывания 2.
    attachInterruptEx( 2, class2.ISRHelper, RISING, ( void * ) & class2 );
}


void loop() {}

Никаких лишних сущностей вне классов нет.

П.С. Если есть большое желание, можно даже привязывать разные методы одного класса. Выглядело бы это примерно так:

class1.AttachInterrupt( 2, class1.MyISR1 );
class1.AttachInterrupt( 2, class1.MyISR2 );

В общем, в зависимости от потребностей, C++ позволяет уйти от процедурного подхода, но платой будет сложность.

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

uni пишет:

В общем, в зависимости от потребностей, C++ позволяет уйти от процедурного подхода, но платой будет сложность.

Это, если готовить не уметь, а есл и уметь, то ООП подход проще и понятнее.

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Я сделал предложение на форуме arduino.cc включить мой патч в Arduino Framework, посмотрим что скажут.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Думаю, что не примут.

Тащить ООП на низкий уровень - вроде как не комильфо. Может я просто старый?

Ничего конкретного, в руках опытного программиста, но в руках новичка - будет жопа.

Тут, на форуме, уже были кадры, которые из обработчика прерывания  Serial.print () вызывали и удивлялись, что не работает.

Идеалогия Arduino IDE в том, чтобы ламер не мог сам себя содомировать пяткой через ухо. Поэтому и перегрузка простых функций проверками и многое другое. А тем, кто знает что нужно делать, нет проблем найти 100500 решений. Но это чисто ИМХО.

 

Seriga
Offline
Зарегистрирован: 01.06.2015

wdrakula пишет:

Тут, на форуме, уже были кадры, которые из обработчика прерывания  Serial.print () вызывали и удивлялись, что не работает.

Хмм… странно но вроде как работает. Даже в обработчиках где счет времени между командами критичен в пределах десятков микросекунд. Получал с помощью Serial.print отладочную информацию.  Понятия не имею как это все работает. Но если серия команд Serial.print небольшая и отправлять не строки, а скажем byte или int все вполне корректно работает. 

Упс… Прошу прощения. Это касается ESP8266 на 160Мгц. Но наверно и на Arduino из обработчика Serial.print будет работать с более широкими допусками по времени.  

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Использовал я Serial.println() из обработчика прерывания. Естественно, со всевозможными предосторожностями:

- единственное целое число или один символ,

- только по условию (т.е. далеко не на каждом входе в прерывание),

- на скорости Serial 115200,

- обработчик вызывался 977 раз в секунду.

В общем, с гарантией, что операция будет закончена до следующего прерывания и не возникнет переполнения буфера. И могу подтвердить, что это вполне успешно работает.

Logik
Offline
Зарегистрирован: 05.08.2014

Думаю что предосторожности только снизят частоту проявления. Давайте прикинем какие траблы возможны (надо бы глянуть реализацию Serial.println() но  сейчас влом ;

1. За время обработки нашего прерывания установлен флаг завершения отправки очередного байта но наш обработчик повлиял так, чтоэто прерывания не возникнет. А система его ждет и не отправляет следующий байт. Буфер отправки переполнился и усьо.

2. В есть код, разрешающие прерывания, в том числе и наше, из-за чего имеем повторное вхождение в обработчик, к чему естественно не готовы.

 

Logik
Offline
Зарегистрирован: 05.08.2014

дабл

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Logik пишет:

Думаю что предосторожности только снизят частоту проявления.

В каких-то случаях (когда прерывания поступают нерегулярно) оно, конечно, так. Но если:

- прерывания поступают от таймера - строго периодически,

- в обработчике производится вполне конкретный объем работы,

может быть 3 ситуации:

1. Обработчик за отведенное время успевает все - система работает без проблем.

2. Обработчик не укладывается в отведенное время - система встает колом. Сразу.

3. Обработчик в среднем укладывается, но не всегда - возможны проблемы.

И, думаю, в третьем варианте в подавляющем большинстве случаев проблемы не заставят себя ждать.

Цитата:
Давайте прикинем какие траблы возможны (надо бы глянуть реализацию Serial.println() но  сейчас влом ;

1. За время обработки нашего прерывания установлен флаг завершения отправки очередного байта но наш обработчик повлиял так, чтоэто прерывания не возникнет. А система его ждет и не отправляет следующий байт. Буфер отправки переполнился и усьо.

2. В есть код, разрешающие прерывания, в том числе и наше, из-за чего имеем повторное вхождение в обработчик, к чему естественно не готовы.

Ну это все либо 2 либо 3. Для варианта 1 проблем быть не должно.

 

PS. Вообще-то я лишь хотел сказать, что Serial.print() из обработчика прерывания работает. А вот нужно ли его туда помещать - это каждый для себя решает сам.

GarryC
Offline
Зарегистрирован: 08.08.2016

Лежало в Инете красивое решение по регистрации метода класса в качестве callback вместе с собственно функциями - вроде это то, что Вам нужно. И было оно сделано через шаблоны - как именно, не помню, но точно красиво.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

GarryC пишет:

 И было оно сделано через шаблоны - как именно, не помню, но точно красиво.

Да, принцип-то там один, как ни делай.

На практике достаточно глобального объекта или, в случае, если надо объекты переключать - то глобального указателя.

uni хочет спрятать те же глобальные указатели в массив в системном файле. Зачем, неясно, типа от чайников реализацию скрыть, Зато глобальный массив придётся постоянно хранить в памяти  - не важно нужно это в конкретном скетче или нет. Подход хорош, когда лишние 10 байтов - карманная мелочь, а когда приходится и их экономить - слишком расточительно.

Вот и холиварят о том, что лучше, не договорившись предварительно о шкале по которой лучшесть измерять, как Цезарь с Цицероном.