Метод объекта в качестве обработчика прерывания
- Войдите на сайт для отправки комментариев
Втр, 29/11/2016 - 13:55
Здравствуйте.
Есть некий клас: MyClass. В котором я реализовал нужную мне логику.
В этом классе мне необходимо работать с прерыванием: attachInterrupt(interrupt, function, mode).
Я не могу в качестве function (обработчика прерываний) подсунуть метод своего класса MyClass который не является static.
аргумент типа "void (MyClass::*)()" не совместим с параметром типа "void (*)()"
Приходится создавать глобальную функцию или метод static класса MyClass.
Но это приводит к тому, что солидная часть логики находится вне класса MyClass хотя все, что делает обработчик относится к классу MyClass соответственно и должно быть спрятано в нем.
Да и все последующие классы, которые будут поочерёдно использовать прерывания должны выкидывать часть соей логики во вне. Код превращается в бардак.
Как всё-таки подсунуть в качестве обработчика прерывания метод объекта? Под мою ответственность, что этот объект будет существовать пока обрабатывается прерывание?
Можна только метод static или завернуть в обработчике прерывания вызов метода глобального обекта.
Можно добавить в Arduino Framework дополнительную функцию attachInterruptEx, с помощью которой было бы возможно передавать this внутрь статического метода-обертки класса.
Если не боитесь кое-чего добавить в стандартные исходные файлы, я могу показать как это сделать. При этом функционал attachInterrupt останется.
Так дело не в том, как его передать, дело в том где его взять при вызове обработчика, учитывая что прерывание может произойти в любой точке, например внутри delay(), где про this вобще не подозревают. Вот и получается что его нужно хранить в глобальной переменной (но тогда это по смыслу уже не совсем this, это просто глобальный обект), а обращатся к нему из обработчика не проблема. Ну и изменять его надо аккуратно, с запретами прерывания, код класса делать потокобезопастным по хорошему надо, ведь скорей всего часть методов из основного цикла будут вызыватся.
Я и предлагаю добавить в исходный код дополнительный функционал, который будет знать откуда взять this. Да, это будет нечто глобальное, но не просто скаляр, а массив. По аналогии с уже имеющимся кодом. Странно, что этого не было предусмотрено изначально.
Эта проблема часто возникает в unix системах, где системные вызовы используют стиль C, а код часто пишут на C++. Там предусмотрен дополнительный параметр void * у callback функции. Ничто не мешает нам добавить в описание userfunc такой же void * и передавать методы.
Работа с дополнительным параметром будет аналогична работе с указателем на функцию пользователя, поэтому уровень "безопасности" будет такой же как в исходном варианте.
На самом деле я всё это уже проделал и провёл некоторые испытания. Вроде работает. Включу в состав виртуальный машины потом. Это полезный функционал.
Зачем? ))) Все это легко делается в скетче, надергал код из работающего.
Проблема безопасности в том, что вызов Termometr.OneWireISR(); может произойти во время выполнения Termometr.Process(&t) А безобидное Termometr=t1 может вызвать крах системы, но очень изредка, если прерывание какраз на него попадет.
И где здесь использование attachInterrupt()? Если нужно будет в runtime поменять обработчик на другой метод, то что будуте делать? Реализовывать свой attachInterrupt?
Да какая разница как код обработчика привязан к прерыванию. Проблемы остаются.
Кину больше кода
Про замену в рантайме думаю ясно. Сделано так именно для простого обеспечения безопасности замены обработчиков. InterruptTimer1ID байтовая, операции с ней атомарные и замена "обработчика" безопасно.
Мне ваш подход не нравится, я бы спрятал всё в класс. В конструкторе написал бы attach( 2, object.method, mode, & object ), а в деструкторе detach(2). Раз объект знает время своего существования, то он и должен динамически управлять привязкой к прерываниям.
Код кстати илюстрирует технику "разделения" одного таймера между несколькими устройствами: уз датчиком расстояния, сервоприводом в части его отключения когда он отработал позиционирование и опроса термометра ds18b20 та самая пауза 750мсек. Предполагается что все действия не перекрываются по времени. А есть код где это ограничение снято ;)
Мне ваш подход не нравится, я бы спрятал всё в класс. В конструкторе написал бы attach( 2, object.method, mode, & object ), а в деструкторе detach(2). Раз объект знает время своего существования, то он и должен динамически управлять привязкой к прерываниям.
А смыслу прятать верхний уровень приложения в класс? Кроме того таймер разделяется между 3-я обектами один приатачился за ним второй, нужен список приатаченых - а это лишнее.
Про привязку нескольких обработчиков речи не шло. Это вы уже сами придумали. Это другая задача. Здесь обсуждается привязка метода класса стандартным способом через attachInterrupt().
Что такое верхний уровень и что я спрятал?
///Про привязку нескольких обработчиков речи не шло. Это вы уже сами пирдумали.
Ага. Весь этот код я придумал. Вы спросили "Если нужно будет в runtime поменять обработчик на другой метод, то что будуте делать?" Поменять очевидно их болееодного. Я вам из проекта вытянул код где показал что делаю когда надо обрабатывать прерывание по разному. Вы наверно не понимаете разницу ПК и МК. Здесь Вам не юникс, на момент начала выполнения кода всегда точно известно множество обработчиков - функций и/или методов класса(-ов), "проинсталировать" новый по ходу работы МК нельзя и смысла писать код позволяющий поменять на произвольный не следует, произвольного не бывает, можна только использовать что есть и нужно делать это эффективно.
// Здесь обсуждается привязка метода класса стандартным способом через attachInterrupt().
Уже обсудили все. Без глобальной переменной, где бы она не хранилась не реализуемо. Несогласны?
Верхний уровень, то что реализовано в *.ino и считается основным функционалом приложения. Прятать его логику в класс безсмысленно.
Первое. "Атомарность" делается специальными макросами.
Нашёл здесь одно из описаний (AVR GCC: атомарно и не атомарно исполняемые блоки кода):
Второе. Что-то вы всё в кучу намешали. У человека есть его собственный класс MyClass и он хочет при помощи attachInterrupt() назначить метод своего класса в качестве прерывания. Есть классический способ как это делается. Нужно в callback функцию передать указатель на экземпляр класса, чей метод используется. Если вы не хотите проблем с невалидным указателем, то нужно attach и detach делать внутри класса. Это логически обосновано тем, что только экземпляр объекта знает когда он существует (начал существовать и закончил существовать). Если же ваш экземпляр создаётся не динамически, а в глобальной области видимости, то привязку метода объекта можно делать вне класса.
Я предлагаю модифицировать attach, изменив тип callback функции (добавляется параметр void *). Повторяю, это классический метод совмещения C++ кода с API операционной системы (фреймворка), написанного на C.
Третье. Я первоначально не понял вашего кода со switch, т.к. подумал, что вы хотите привязать несколько методов к одному прерыванию. Теперь понятно, что вы показали статическую привязку, т.к. выбрать можно только то, что указано в switch, т.е. три фиксированных указателя.
Да, такой подход очень простой. Но если у меня будет выбор между статической и динамической привязкой, то я выберу динамическую, т.к. это понятнее и гибче, нет лишних сущностей.
Четвёртое.
Вы наверно не понимаете разницу ПК и МК. Здесь Вам не юникс, на момент начала выполнения кода всегда точно известно множество обработчиков - функций и/или методов класса(-ов), "проинсталировать" новый по ходу работы МК нельзя и смысла писать код позволяющий поменять на произвольный не следует, произвольного не бывает, можна только использовать что есть и нужно делать это эффективно.
Может объясните подробней, почему из одной функции (ISR) я не могу перейти по нужному мне указателю? Это табу такое? Что тогда делать разработчиками операционных систем? Представляете, они как раз в прерывании переходят вообще на произвольный адрес - тот, на котором остановился поток до прерывания. Может быть switch использовать? Раз все указатели предопределены, то мы их запишем в таблицу и сохраним индекс текущего адреса, так?
Верхний уровень, то что реализовано в *.ino и считается основным функционалом приложения. Прятать его логику в класс безсмысленно.
Это вы setup() и loop() имеете в виду? Я этим не ограничен. Если я буду так думать, то как мне тогда реализовать многопоточность (добавить ОС) и использовать Arduino Framework? У меня есть примеры с использованием библиотеки scmRTOS, где я создаю два потока и вывожу текст через Serial.print одновременно в каждом.
Не нужно себя самоограничивать. Про это я уже тоже говорил. Невозможное - только в голове.
Проверил тему поиском "невозможно" встретилось один раз в Вашем последнем сообщении. Вы о чем вобще? Или Вы знаете как осуществить вызов нестатического метода без сохранения обекта в глобальных? Раскажите.
По первому-третему. Ну Вы ж понимаете что предлагаемое Вами в каждом пункте сложней. И я Вам скажу что оно будет больше жрать ресурсрв, и требует перепахивания стандартных либ. Я пишу коротко и эффективно. Одним словом KISS. Спорить по этому поводу будете?
///. Если вы не хотите проблем с невалидным указателем
Где у меня такое может быть, укажите строку ))) А вобще, не в привязке к моему коду, там в начале ТС этот момент оговаривал. Он гарантирует.
///нужно attach и detach делать внутри класса.
Нет. Класс может допускать работу и без прерываний, тогда ничего атачится не предполагается. Например тот же датчик температуры требует продолжения работы через 750мс а как она отмерена - вобще не его дело и откуда его дернут после истечения времени в общем тоже, с учетом потокобезопасности. Получаем гибкий класс без делеев внутри и не привязанный к аппаратным особенностям. Например на OrangePI он тоже заработает, например время в отдельном потоке отмерят. Ваше решение не обеспечит такое. Я знаю, Вы предложите иерархию класов у которого базовый наследуется 2-я один с прерываниями другой без ))) Ну в добрый путь, KISS ;)
///если у меня будет выбор между статической и динамической привязкой, то я выберу динамическую
Ну Ваши ошибки - это Ваши ошибки, я то тут причем..
///Может объясните подробней, почему из одной функции (ISR) я не могу перейти по нужному мне указателю?
Можете. Но не все что возможно делать стоит делать. Считаем просто "поверхам" Атмега328, 32КБ кода, пару сотен байт на функцию, итого по максимуму около 200 функций, т.е. перейти можна 200 указателей. Из них осмыслено (не переходить же на strcmp по вызову прерывания) - ну пару десятков. Вполне так в свич, или константный массив лягут. Как говорил классик - переход кол-ва в к-во.
///Это табу такое?
Нет. Специфика МК определяет оптимальный вариант из всех возможных. У МК такого много - например статические переменные не считаются грехом.
///Что тогда делать разработчиками операционных систем?
С ОС, многопоточностью, умными указателями и фабриками классов сидеть на ПК и не рыпатся на МК.
///Представляете, они как раз в прерывании переходят вообще на произвольный адрес - тот, на котором остановился поток до прерывания. Может быть switch использовать? Раз все указатели предопределены, то мы их запишем в таблицу и сохраним индекс текущего адреса, так?
Про вытесняющую многопоточность только что писал ;) А вобще по форуму холиварили много раз. Это не для МК (про rtos в курсе - скачал задумчиво почитал и уже не помню где лежит). Хотя если бы кооперативную как в Вин3.11, ИМХО, то думаю годилась бы.
Это вы setup() и loop() имеете в виду? Я этим не ограничен. Если я буду так думать, то как мне тогда реализовать многопоточность (добавить ОС) и использовать Arduino Framework? У меня есть примеры с использованием библиотеки scmRTOS, где я создаю два потока и вывожу текст через Serial.print одновременно в каждом.
Не нужно себя самоограничивать. Про это я уже тоже говорил. Невозможное - только в голове.
А я могу сохранить указатель на функцию в EEPROM как строку, считать его конвертировав в void*, записать его в стек и по ret перейти. Только не знаю вот нафига? Но наверно гибкость будет замечательная )))
Сравните "два потока и вывожу текст через Serial.print" с аналогичной реализацией на машинке состояний и все поймете чисто в цифрах.
Вот ссылка на реализацию TelnetServer. Там два потока, один из них работает по сети (сам сервер). Используется scmRTOS и всё то, чего вы осилить, судя по всему уже никогда не сможете.
Я пытался... но словоблуд тут силён больше.
И точно, Вы правы наконецто, это ссылка! Это я типа её смотреть должен... Вроде не просил, ну ладно гляну по быстрому, например самое главное Program.cpp
Что вижу - меджикнамберы, приведение хрена к пальцу, работа с портом через digitalWrite, закоментированый код какойто. Его смотреть или нет? Хотя больше не хочется.
Ну реализовали вы эхо используя известные либы w5100. И че? Или вы считаете что без потоков и ОС это не реализуемо? Или решили меня потрясти масштабом проекта. Пишите как реализуете чегонить такого уровня, чтоб хоть выглядело похоже:
Контроллер аквариума - Промини, часы РВ, сервопривод, ШИМ освещения 4 канала, датчики температуры ds - 3шт, датчик влажности, экран что очевидно, тач. Все либы свои, включая GUI. Все работает абсолютно одновременно, навигация по меню, с 10-к экранов на работу переферии не сказывается. Потоков нет.
Или так. STM32 тот который Мапле, енкодер, SD и экран снова. Опять таки работа энкодера и навигация на оцифровке 2МГц не влияют. И снова без потоков.
Теперь хочу чтобы картинка ожила - https://www.youtube.com/watch?v=5-Y1tt14xsA&feature=youtu.be
Старт на OrangePi. Сейчас там еще серва управляется, на Хелоин тыкву поворачивала, и по i2c АЦП внешнее висит. И все работает с любого броузера по WinSocket, с криптографией по протоколу. Но тут уже два потока - виринг серву крутит в своем.
Так что телнетом зря удивить пытались. Вобщем как сделаете чего интересного на своей ОС - пишите. А по пустякам не раздражай здесь дядь, таких как Вы тут в месяц по три штуки с мира ПК падает, то ОСь запускают то про многопоточность печалятся, то стандартные либы дорабатывают ))) Сделайте что полезное хоть для себя, ато телнет с эхой в два потока...
Что вижу - меджикнамберы, приведение хрена к пальцу, работа с портом через digitalWrite, закоментированый код какойто. Его смотреть или нет? Хотя больше не хочется.
Ну реализовали вы эхо используя известные либы w5100. И че?
Вы понять даже не смогли что это такое, зачем оцениваете? Вам показали работающую библиотеку для организации операционной системы на Arduino, которую можно взять и использовать, а вы что увидели? До чего опыта хватило? Смотришь в книгу - видишь фигу?
///Что тогда делать разработчиками операционных систем?
С ОС, многопоточностью, умными указателями и фабриками классов сидеть на ПК и не рыпатся на МК.
///Представляете, они как раз в прерывании переходят вообще на произвольный адрес - тот, на котором остановился поток до прерывания. Может быть switch использовать? Раз все указатели предопределены, то мы их запишем в таблицу и сохраним индекс текущего адреса, так?
Про вытесняющую многопоточность только что писал ;) А вобще по форуму холиварили много раз. Это не для МК (про rtos в курсе - скачал задумчиво почитал и уже не помню где лежит). Хотя если бы кооперативную как в Вин3.11, ИМХО, то думаю годилась бы.
Может просто вы не умеете её "готовить"? И все остальные, которые про это только слышали, а разобраться самостоятельно так и не смогли.
Конечно-автоматный путь - тупиковый для подобных систем. Их никто кроме вас не будет сопровождать. В сложных проектах важна декомпозиция частей. В ОС она достигается (можно видеть в моём коде, где setup() и loop() у каждого потока свои и они работают параллельно, т.е. вечный цикл в одном потоке не останавливает второй). Платой является сложность при работе с потоками и разделение доступа к ресурсам.
Это прототип для другой задумки: ПЛК (Arduino Mega 2560 + W5100). В виду её сложности я перешёл на использование ОС, т.к. знаю, что по-другому это реализовать практически невозможно.
Я делаю это на avr, т.к. мне нужны подобные модули в множественном количестве (десятки). Чем дешевле вычислительно/управляющий блок получится, тем больше я смогу их использовать. Любой может взять даже роутер и сделать на нём то же самое, но у этого есть цена в прямом и переносном смысле, что ограничивает использование такого подхода массово.
Конечно-автоматный путь - тупиковый
Мне казалось, что Ваше оппонент говорил не конечных авоматах, а об автоматном программировании. Вы не видите разницы? Или считаете, что он говорил именно о конечных автоматах?
Я считаю, что не нужно останавливаться на достигнутом!
Мне вот, в разработке, очень не хватает стандартного fork().
Причем важно, чтобы работал он на 328-ом контроллере, так как 2560 - это капиталистические излишества. Согласны?
-----------------
Uni! Прошу не обижаться на шутку, просто не удержался.
Не мне Вас судить, я на таймер-хуки в детстве даже для DOS 6.22 некий вариант многозадачности (скорее это был мультитред ;) ) вешал и был очень горд собой, очень удивлялся, почему это коллеги-программисты не дали мне сразу канистру спирта и премию ;).
Развлекайтесь.
Мне вот, в разработке, очень не хватает стандартного fork().
Да, чего мелочиться, заодно уж и MPI сделать - можно будет кластеры из 100500 ардуин лепить
Или считаете, что он говорил именно о конечных автоматах?
Если быть математически точным то есть всего ТРИ типа автоматов:
конечный, стековый (в русскоязычной литературе "автоматы с магазинной памятью" - мне всегда бьло интересно - из какого магазина? ;) ) и бесконечный - он же машина Тьюринга.
Каждый из них может быть как детерминированным так и недетерминированным.
.....
Эх молодость! Сейчас не удержусь - приму 150 грамм и пойду Великую теорему Гёделя доказывать... например собаке, так как жена уже не может больше слушать это доказательство ;).
wdrakula,
но здесь-то как я понимаю, речь шла об автоматном программировании, а это воплне устоявшийся термин и к автоматоам в метаматеическом сымсле он имеет, скажем так, косвенное отношение.
Вот ссылка на реализацию TelnetServer.
Вы понять даже не смогли что это такое, зачем оцениваете? Вам показали работающую библиотеку для организации операционной системы на Arduino, которую можно взять и использовать, а вы что увидели? До чего опыта хватило? Смотришь в книгу - видишь фигу?
Вот первая ссылка https://mysvn.ru/avr/TelnetServer/
вот вторя https://mysvn.ru/avr/TelnetServer/lib/scmRTOS/
Д-з: криворукость в запущеной стадии. Давайте третю попытку делайте, что хотите показать. Приложение или либу? Кстати про то что rtos видел и вероятно много ранее вашего я писал там..
Может просто вы не умеете её "готовить"?
Канешно. Десятилетиями на хлеб себе разработкой ПО зарабатывал и не умею. Хамиш парниша.
Их никто кроме вас не будет сопровождать.
В сложных проектах важна декомпозиция частей. В ОС она достигается (можно видеть в моём коде, где setup() и loop() у каждого потока свои и они работают параллельно, т.е. вечный цикл в одном потоке не останавливает второй).
Совсем в бред завалился. Декомпозици в проекте - разделение на модули, классы, библиотеки и т.д. выполняющие функционально завершенные части проекта. Учи матчасть потоки к декомпозиции и сопровождению кода никаким боком.
Это прототип для другой задумки: ПЛК (Arduino Mega 2560 + W5100).
В виду её сложности я перешёл на использование ОС, т.к. знаю, что по-другому это реализовать практически невозможно.
Чем дешевле вычислительно/управляющий блок получится, тем больше я смогу их использовать. Любой может взять даже роутер и сделать на нём то же самое, но у этого есть цена в прямом и переносном смысле, что ограничивает использование такого подхода массово.
Так это Вы должны обяснять почему ОС и многопоточность раздувают код и аовышают стоимость проекта, а не я.
Вы там видео, выше ссылка была, смотрели про оранжПи? Так вот это называется стартовый проект. Ну типа хелоуворд, но чуть лучше. Здесь втыкать подробней.
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 делается.
Вы может быть удивитесь, но Microsoft до сих пор продаёт DOS для панельных ПК. И их используют по той причине, что загружается такой ПК быстро, программе доступно всё и сразу и кода для него в своё время было наработано много. Для программ типа осциллографов - самое то. На одной из моих работ такой вот ПК использовался (продавался) в качестве графического регистратора.
Сейчас я пишу код на uClinux и fork'а там нет, т.к. нет менеджера памяти и всё находится в одном адресном пространстве, а если вы переходите на полноценные ОС, то получаете длительную загрузку и большой головняк в виде кросс-компиляции.
-----------------------------------------------------------------------------
Вернёмся к первоначальному вопросу. Итак, как же "привязывать" метод экземпляра класса в качестве обработчика прерывания?
Для этого модифицируем немного исходники Arduino Framework'а. Мы не будем ничего удалять, а только добавим функционала для сохранения совместимости. Сначала я расскажу что поменять, потом дам пример и расскажу как это работает. В качестве благодарности можете протестировать и поискать ошибки/предложить свою реализаци.
Шаг 1. Файл wiring_private.h. Ищем строчку:
и добавляем ниже описание нового типа:
Шаг 2. Файл WInterrupts.c. Добавляем массив, некоторые функции и кое-чего внутри attachInterruptEx():
Сравните его содержимое со своим файлом и сделайте изменения, либо просто замените его содержимое (на свой страх и риск конечно).
Шаг 3. Добавляем описание функции в заголовочник Arduino.h:
Вот вроде и всё, демонстрационный пример:
Код:
нужен для формирования программно прерывания INT0. Правда срабатывает он только один раз при старте почему-то. Но для теста в симуляторе этого достаточно.
Как оно работает. В примере показано, что старый функционал остался, т.е. attachInterrupt() должен работать как прежде (или почти ;) ). Новая функция attachInterruptEx() делает то же, но принимает расширенный набор параметров. Во-первых, тип прототип функции содержит параметр void *, во-вторых, нам нужно передать зачение этого параметра. Сохраняется этот параметр также как и указатель на callback функцию в массиве. Для каждого типа прерывания сохраняется значение своего параметра. Это тот самый глобальный параметр, но в отличие от его явного наличия в коде пользователя, здесь работа с ним сокрыта в Arduino Framework и вам не нужно думать про это, используя функцию привязки.
Когда вы вызываете attachInterruptEx() во внутренних массивах сохраняется указатель на метод и дополнительный параметр. Если этим параметром указать this, то внутри статического метода-обёртки (он является вспомогательным) вы можете восстановить этот указатель и вызвать нестатический его обработчик (в примере этого нет). Фактически мы получим вызов метода конкретного указанного экземпляра класса.
Пробуйте.
Щас я полезу wiring_private.h править, уже разогнался ;)
"Работает - не трогай!"
Щас я полезу wiring_private.h править, уже разогнался ;)
"Работает - не трогай!"
Код не для вас, а для того, кто спрашивал. Мы пойдём вперёд, а вы оставайтесь при своём мнении.
Шо за хрень Вы написали ))
Строку 50 заменяем на class1.MyISR(... и код работает без каких либо изменений wiring_private.h. Это просто писец... )))
Хороше что это не для меня писали )))
Я - пас господа! Окружающие косятся, понять немогут чего ржу...
Шо за хрень Вы написали ))
Строку 50 заменяем на class1.MyISR(... и код работает без каких либо изменений wiring_private.h. Это просто писец... )))
Хороше что это не для меня писали )))
Я - пас господа! Окружающие косятся, понять немогут чего ржу...
"Дядя Петь, ты дурак?" (с)
Так тебя Петром значить кличут. Так чегож, Петя за четыре года, с передовыми методами так и не реализовал Arduino Mega Server?
Так тебя Петром значить кличут. Так чегож, Петя за четыре года, с передовыми методами так и не реализовал Arduino Mega Server?
я запретил.
Так тебя Петром значить кличут. Так чегож, Петя за четыре года, с передовыми методами так и не реализовал Arduino Mega Server?
Потому что AMS - это статический велосипед. Там обработка ведётся статично в коде, а скрипты на ПК верхнего уровня. Меня это не устраивает, я хочу иметь скрипты, которые позволят динамически управлять без верхнего уровня. В этом большая разница, которую почему-то трудно понять.
По теме, вот пример как обёртывать системную C-функцию при помощи C++ класса. У меня сделано то же самое.
Не Петь, без верхнего уровня - никак невозможно. От представь. Софт до доработки, так, есть какойто верхний уровень. Дорабатываеш его, Петь, убираеш верхний уровень, и что - уровень который был вторым сверху автоматически становится верхним! Миссия не выполнима!
Но теперь я понимаю путь развитие Вашего, Петь проекта.
ПС. Петь, не пропадай, я ща поделам, вернусь еще поржу.
Не Петь, без верхнего уровня - никак невозможно. От представь. Софт до доработки, так, есть какойто верхний уровень. Дорабатываеш его, Петь, убираеш верхний уровень, и что - уровень который был вторым сверху автоматически становится верхним! Миссия не выполнима!
Но теперь я понимаю путь развитие Вашего, Петь проекта.
Вам к доктору может обратиться? Самому себе пишите? О своём, о личном думу думаете? Когда вы гримасничаете по поводу стандартов в промышленной автоматике, вы выглядите как пациент. Чем дальше, тем хуже.
Спасибо всем за помощь.
Попробовал сделать как советовал “ЕвгенийП”. К остальным предложенным вариантом я даже и не знаю, как подступится.
Создал статический указатель, в который конструктор передавал адрес созданного объекта.
Код начал обрастать “костылями”.
Например, переменная класса обозначенная как volatile наотрез отказалась изменятся в обработчике события через созданный мною указатель на объект. Так-же когда я попытался обратится к массивам через указатель в обработчике события то получил совершенно не то что было в этом массиве. Да и наверно и еще что-то чего я уже не вспомню.
В общем фокус не удался. Решил оставить как есть.
Создал статический указатель, в который конструктор передавал адрес созданного объекта.
))) Вы все совсем не так поняли. Лезть в конструктор не нужно.
Например, переменная класса обозначенная как volatile наотрез отказалась изменятся в обработчике события через созданный мною указатель на объект. Так-же когда я попытался обратится к массивам через указатель в обработчике события то получил совершенно не то что было в этом массиве. Да и наверно и еще что-то чего я уже не вспомню.
Проще сказать, что указатель вышел невалидный. Если надумаете дальше боротся - код в студию.
Держите пример.
Даже проверил. Все просто и понятно. Никаких правок ядра (которые надо будет применять к каждой новой версии IDE, и отломается совместимость с другими разработчиками), неявных привязок в конструкторе и мутных разименовангий, класс одинаково рабочий как через прерывание так и без. Специально пример сделал с наследованием и виртуальностю, все корректно.
После заливки тыкаете железным предметом в пин2. Светодиод начинает моргать, у меня даже без контакта с расстояния в 5мм, ну это от интелекта зависит ;) Годится быть генератором случайных чисел, типа орел или решка )))
По коду. В глобальной class1 наш обект обработчик прерывания blink "обертка" для вызова метода, сделал его даже виртуальным, чтоб уж точно не статический.
ПС. uni проилюстрировал поговорку "Хуже дурака только дурак с инициативой", засрал тему, в результате ТС уже готов отказатся от замысла.
О! Этот Петя теперь меня минусует! А шо такое, Петя? Ну я хоть знаю что он код видел ;)
ППС. Ща снова какуюто дурость заумную начнет писать про убирание "верхнего уровня", и постить непровереный код. Потом окажется что он не компилируется без правок системных библиотек, и вобще такие вещи по 4 года делаются, главное рассово верный код, а результат не важен ))))
Используя мой метод, в вашем коде не будет лишних глобальных идентификаторов. Если нужно, можно вообще всё всё спрятать в класс:
Либо назначать методы других объектов, вот так:
Никаких лишних сущностей вне классов нет.
П.С. Если есть большое желание, можно даже привязывать разные методы одного класса. Выглядело бы это примерно так:
В общем, в зависимости от потребностей, C++ позволяет уйти от процедурного подхода, но платой будет сложность.
В общем, в зависимости от потребностей, C++ позволяет уйти от процедурного подхода, но платой будет сложность.
Это, если готовить не уметь, а есл и уметь, то ООП подход проще и понятнее.
Я сделал предложение на форуме arduino.cc включить мой патч в Arduino Framework, посмотрим что скажут.
Думаю, что не примут.
Тащить ООП на низкий уровень - вроде как не комильфо. Может я просто старый?
Ничего конкретного, в руках опытного программиста, но в руках новичка - будет жопа.
Тут, на форуме, уже были кадры, которые из обработчика прерывания Serial.print () вызывали и удивлялись, что не работает.
Идеалогия Arduino IDE в том, чтобы ламер не мог сам себя содомировать пяткой через ухо. Поэтому и перегрузка простых функций проверками и многое другое. А тем, кто знает что нужно делать, нет проблем найти 100500 решений. Но это чисто ИМХО.
Тут, на форуме, уже были кадры, которые из обработчика прерывания Serial.print () вызывали и удивлялись, что не работает.
Хмм… странно но вроде как работает. Даже в обработчиках где счет времени между командами критичен в пределах десятков микросекунд. Получал с помощью Serial.print отладочную информацию. Понятия не имею как это все работает. Но если серия команд Serial.print небольшая и отправлять не строки, а скажем byte или int все вполне корректно работает.
Упс… Прошу прощения. Это касается ESP8266 на 160Мгц. Но наверно и на Arduino из обработчика Serial.print будет работать с более широкими допусками по времени.
Использовал я Serial.println() из обработчика прерывания. Естественно, со всевозможными предосторожностями:
- единственное целое число или один символ,
- только по условию (т.е. далеко не на каждом входе в прерывание),
- на скорости Serial 115200,
- обработчик вызывался 977 раз в секунду.
В общем, с гарантией, что операция будет закончена до следующего прерывания и не возникнет переполнения буфера. И могу подтвердить, что это вполне успешно работает.
Думаю что предосторожности только снизят частоту проявления. Давайте прикинем какие траблы возможны (надо бы глянуть реализацию Serial.println() но сейчас влом ;)
1. За время обработки нашего прерывания установлен флаг завершения отправки очередного байта но наш обработчик повлиял так, чтоэто прерывания не возникнет. А система его ждет и не отправляет следующий байт. Буфер отправки переполнился и усьо.
2. В есть код, разрешающие прерывания, в том числе и наше, из-за чего имеем повторное вхождение в обработчик, к чему естественно не готовы.
дабл
Думаю что предосторожности только снизят частоту проявления.
В каких-то случаях (когда прерывания поступают нерегулярно) оно, конечно, так. Но если:
- прерывания поступают от таймера - строго периодически,
- в обработчике производится вполне конкретный объем работы,
может быть 3 ситуации:
1. Обработчик за отведенное время успевает все - система работает без проблем.
2. Обработчик не укладывается в отведенное время - система встает колом. Сразу.
3. Обработчик в среднем укладывается, но не всегда - возможны проблемы.
И, думаю, в третьем варианте в подавляющем большинстве случаев проблемы не заставят себя ждать.
1. За время обработки нашего прерывания установлен флаг завершения отправки очередного байта но наш обработчик повлиял так, чтоэто прерывания не возникнет. А система его ждет и не отправляет следующий байт. Буфер отправки переполнился и усьо.
2. В есть код, разрешающие прерывания, в том числе и наше, из-за чего имеем повторное вхождение в обработчик, к чему естественно не готовы.
PS. Вообще-то я лишь хотел сказать, что Serial.print() из обработчика прерывания работает. А вот нужно ли его туда помещать - это каждый для себя решает сам.
Лежало в Инете красивое решение по регистрации метода класса в качестве callback вместе с собственно функциями - вроде это то, что Вам нужно. И было оно сделано через шаблоны - как именно, не помню, но точно красиво.
И было оно сделано через шаблоны - как именно, не помню, но точно красиво.
На практике достаточно глобального объекта или, в случае, если надо объекты переключать - то глобального указателя.
uni хочет спрятать те же глобальные указатели в массив в системном файле. Зачем, неясно, типа от чайников реализацию скрыть, Зато глобальный массив придётся постоянно хранить в памяти - не важно нужно это в конкретном скетче или нет. Подход хорош, когда лишние 10 байтов - карманная мелочь, а когда приходится и их экономить - слишком расточительно.
Вот и холиварят о том, что лучше, не договорившись предварительно о шкале по которой лучшесть измерять, как Цезарь с Цицероном.