Как красиво использовать прерывание в классе?
- Войдите на сайт для отправки комментариев
Чт, 29/06/2017 - 23:47
Возникла необходимость для каждого экземпляра класса использовать свой набор прерываний (Arduino Due).
Но, как и следовало ожидать, метод класса не подходит в качестве обработчика прерывания.
заголовочный файл:
#ifndef ENCODERS_H
#define ENCODERS_H
#include "Arduino.h"
class tEncoder
{
public:
tEncoder(int8_t pin_A, int8_t pin_B);
~tEncoder();
void begin();
long getCounter();
private:
void intrpt_A();
void intrpt_B();
volatile long counter;
int8_t pinA;
int8_t pinB;
bool interrupt;
};
#endif
реализация:
#include "encoders.h"
tEncoder::tEncoder(int8_t pin_A, int8_t pin_B)
{
interrupt = false;
pinA = pin_A;
pinB = pin_B;
}
tEncoder::~tEncoder()
{
if(interrupt) {
detachInterrupt(pinA);
detachInterrupt(pinB);
}
}
void tEncoder::begin()
{
counter = 0;
attachInterrupt(pinA, intrpt_A, CHANGE);
attachInterrupt(pinB, intrpt_B, CHANGE);
interrupt = true;
}
long tEncoder::getCounter()
{
return counter;
}
void tEncoder::intrpt_A() {
if(digitalRead(pinA)) {
if(digitalRead(pinB)) {
counter--;
} else {
counter++;
}
} else {
if(digitalRead(pinB)) {
counter++;
} else {
counter--;
}
}
}
void tEncoder::intrpt_B() {
if(digitalRead(pinB)) {
if(digitalRead(pinA)) {
counter++;
} else {
counter--;
}
} else {
if(digitalRead(pinA)) {
counter--;
} else {
counter++;
}
}
}
и закономерный результат:
c:\Tmp\build4599856423478963012.tmp\encoders.cpp: In member function 'void tEncoder::begin()':
c:\Tmp\build4599856423478963012.tmp\encoders.cpp:29:41: error: cannot convert 'tEncoder::intrpt_A' from type 'void (tEncoder::)()' to type 'void (*)()'
attachInterrupt(pinA, intrpt_A, CHANGE);
^
c:\Tmp\build4599856423478963012.tmp\encoders.cpp:30:41: error: cannot convert 'tEncoder::intrpt_B' from type 'void (tEncoder::)()' to type 'void (*)()'
attachInterrupt(pinB, intrpt_B, CHANGE);
^
Error compiling.
В принципе, почему - понятно.
Непонятно, как это красиво обойти. Хочется, чтобы экзампляры класса были простыми переменными, а не элементами массива (собственно, в последнем случае класс и не нужен).
Ну начинать надо естестевенно с начала , а точнее от сюда.http://arduino.ua/ru/prog/AttachInterrupt
Читаем это
и это должно быть в setup() программы индивидуально для каждого представителя класса.
Итого надо создать метод класса void Cl_class::setup (byte pin,void (*function)(), byte mode) {attachInterrupt(pin, function, mode);}
ПС: Может я не в тему всунулся. А зачем прерывания на оба пина энкодера вешать. У энкодера один CLK, а DIR (напрвление) может гулять без прерывания.
ППС: Прерывние внешнее, так что функция должна быть паблик. Опять же прерывание это встроеная функция системы, а это значит, что функция не должна быть методом класса. А вот во внешней функции можно дать вызов конкретного метода конкретного класса. Что я применял в своих программах.
рептилоид, запости скетч, которым ты генеришь ошибку своего класса
Может быть, в таблицу прерываний как-нибудь вручную вписать адрес функции в созданном объекте класса? http://avrprog.blogspot.ru/2013/03/interrupts.html
Не уверен, что это возможно в процессе исполнения, но всё же...
возможно, нужно купить новый хрустальный шар и дать рептилоиду им по голове.
памойму, статический метод класса можно в прерывания цеплять? Для обычного метода класса, в него неявно передается скрытый параметр this, про который, естесственно, обработчик прерывания ничего не знает. А статическому методу класса этот параметр не нужен, он туда и не передается. Правда, смысла цеплять туда прерывания я не вижу, конкретный экземпляр класса неизвестен.
Пусть уважаемый ЕвгенийП нам расскажет подробнее.
Ну или не менее уважаемый кастрюлеголовый своим хрустальным шаром по голове мне даст. :)
Клоп, чтобы получить приведенную диагностику, достаточно такого:
void setup() { // put your setup code here, to run once: } void loop() { // put your main code here, to run repeatedly: }но хотелось бы использовать примерно так:
#include "encoders.h" tEncoder first(25,27); tEncoder left(28,30); tEncoder green(31,33); void setup() { // put your setup code here, to run once: first.begin(); left..begin(); green.begin(); } void loop() { // put your main code here, to run repeatedly: Serial.write("First: "); Serial.write(first.getCounter()); Serial.write("Left: "); Serial.write(left.getCounter()); Serial.write("Green: "); Serial.writeln(green.getCounter()); delay(500); }если классы статические, то ничего этому не мешает.
мой TTimerList тоже на прерывания завязан
Ну начинать надо естестевенно с начала , а точнее от сюда.http://arduino.ua/ru/prog/AttachInterrupt
Читаем это
и это должно быть в setup() программы индивидуально для каждого представителя класса.
Размещение attachInterrupt() в setup() нарушает принцип инкапсуляции. Хотелось бы найти способ, не противоречащий концепции ООП.
Итого надо создать метод класса void Cl_class::setup (byte pin,void (*function)(), byte mode) {attachInterrupt(pin, function, mode);}
Но вот эта функция function() должна быть у каждого экземпляра своя, по крайней мере, она должна уметь работать с private переменными конкретного экземпляра класса.
ПС: Может я не в тему всунулся. А зачем прерывания на оба пина энкодера вешать. У энкодера один CLK, а DIR (напрвление) может гулять без прерывания.
Естественно, для максимизации разрешения.
ППС: Прерывние внешнее, так что функция должна быть паблик. Опять же прерывание это встроеная функция системы, а это значит, что функция не должна быть методом класса. А вот во внешней функции можно дать вызов конкретного метода конкретного класса. Что я применял в своих программах.
Вот что-то я и не могу сообразить, как заставить эту внешнюю функцию работать с внутренними переменными нужного экземпляра класса.
Все правы, всё правильно. Статический метод - без проблем, метод экземпляра - хренушки, кто же ему this-то передаст.
Что до вопроса из заголовка топика, то понятие "красиво" неформализуемо и зависит от эстетических пристрастий каждого. По мне (по моим эстетическим пристрастиям) красивого решения здесь нет.
Некрасивое крутится вокруг общей идеи: класс содержит статический метод - диспетчер, который назначен на все прерывания и умеет отличать одно от другого (т.е. знает какое именно прерывание произошло). Каждый экземпляр прямо в конструкторе регистриуется в некой статической же структуре данных и по сути говорит: "такое-то прерывание - моё" и оставляет в этой статической структуре свой this. Когда прерывание происходит, диспетчер смотрит что там за прерывание, вытаскивает правильный this и в его контексте вызывает метод-обработчик. Сделать можно сотней способов, но общая идея всё равно такая или подобная.
спасибо. я примерно так представлял, обработчик один, подписчики разные
Навскидку. Пул прерываний в виде отдельного класса. Кому надо прерывания, запрашивают у него, фактически регистрируя/связывая свой this для нужного прерывания. Для всех классов, что хотят прерывания, есть общий интерфейс, описывающий нужный коллбэк для вызова из пула прерываний. При вызове обработчика this будет известен. Вроде должно работать. А нет, так вы тут все еще чего придумаете :)
Вот что-то я и не могу сообразить, как заставить эту внешнюю функцию работать с внутренними переменными нужного экземпляра класса.
#include "encoders.h" tEncoder first(25, 27); tEncoder left(28, 30); tEncoder green(31, 33); void func_first{ first.Do; }; void func_left{ left.Do; }; void func_green{ green.Do; }; void setup() { first.begin(); left..begin(); green.begin(); attachInterrupt(pin, func_first, mode) attachInterrupt(pin, func_left, mode) attachInterrupt(pin, func_green, mode) } void loop() { Serial.write("First: "); Serial.write(first.getCounter()); Serial.write("Left: "); Serial.write(left.getCounter()); Serial.write("Green: "); Serial.writeln(green.getCounter()); delay(500); }Ну а дальше это безобразие просто спрятать в void connect()
Спасибо, qwone, но в коде, как мне представляется:
1. Строки 6-14 и 20-22 являются идеологически лишеими и должны быть упрятаны внутрь класса.
2. На каждый объект нужно по 2 прерывания, следовательно, количество "лишнего" кода удваивается.
Я вот что думаю...
У нас есть класс как таковой и несколько экземпляров этого класса.
При этом могут быть, как минимум, следующие типы переменных и функций класса:
Переменная типа 0 - в единственном экземпляре для всего класса.
Переменная типа 1 - создается в каждом экзкмпляре класса.
Функция типа 0 - единственная для всего класса, может работать с переменными типа 0 (с типом 1 - не может).
Функция типа 1 - создается в каждом экземпляре класса - может работать с любыми переменными класса.
Функция типа 2 (хитрая) - единственная для всего класса, но в нее неявным образом передается некоторый идентификатор экземпляра, что прозволяет ей работать также с любыми переменными класса.
Так вот, static переменная - это тип 0, обычная переменная класса - это тип 1, static функция класса - это тип 0, обычная функция класса - это (хитрый) тир 2. А где же функции типа 1? Я думаю, для решения моей проблемы они бы подошли как нельзя кстати.
Обычный метод класса - это тип 1.
Обычный метод класса - это тип 1.
Тогда зачем ей this? Она способна обращаться к переменным экземпляра класса безо всяких костылей.
Вам надо написать свой внутриклассовый attachInterrupt(pin, func_first, mode) Иначе ничего не выйдет. А вот с помощью чего вы напишете. Это Си будет или ассемблер я не скажу.
Вам надо написать свой внутриклассовый attachInterrupt(pin, func_first, mode) Иначе ничего не выйдет. А вот с помощью чего вы напишете. Это Си будет или ассемблер я не скажу.
Не думаю, что на Ассемблере можно написать что-то, чего нельзя на С. (кстати, вопрос в С++, с С в данном контексте проблемы нет по определению)
Но вот системным программирование сейчас заниматья не хочу. Как-то есть более интересная конкретная прикладная задача.
Ну, если С++ действительно не поддерживают функции 1-го типа (по моей классификации), значит, придется обойтись без ООП. Делов то... Не хотелось просто 10 раз повторять код обработчика, но раз ООП от этого не спасает, какой в нем смысл?
Обычный метод класса - это тип 1.
Тогда зачем ей this? Она способна обращаться к переменным экземпляра класса безо всяких костылей.
Метод класса (не статический) привязан к конкретному экземепляру. И эта привязка реализуется через этот самый this. Физически код один и тот же для всех экземпляров. Но как раз при помощи this он обращается к свойствам именно своего экземпляра, а не какого-то другого.
Если несколько упрощённо представить себе реализацию класса (без статических свойств и методов), то это структура. соовтвествующая всем нестатическим свойствам и набор функций - методов. При создании экземпляра, заводится в отдельной памяти эта структура. Адрес этой структуры и есть this. Любой метод, получая параматером this, "понимает" что в данный момент он действует как метод данного (this) экземпляра и работает со свойствами данного экземпляра, находящимися по адресу this. Правильные слова здесь "метод вызван в контексте экземпляра".
Чтобы убедиться в этом опишите любой класс с некоторым количество свойств и любым количеством методов и напечатайте его sizeof. Получите суммарную память для всех свойств (той самой структуры). А коды, кончено же физически одни и те же для всех экземпляров и только передаваемый this задаёт контекст конкретного экземпляра.
Например:
class MyClass { public: MyClass(void) { /* la-la-la */ } MyClass(const int) { /* la-la-la */ } ~MyClass(void) { /* la-la-la */ } void CoolMethod(int) { /* la-la-la */ } // ещё сколько угодно методов private: int m_a, m_b; // Два ibt'а = 4 байта }; void setup(void) { Serial.begin(115200); Serial.println(sizeof(MyClass)); } void loop(void) {} // // РЕЗУЛЬТАТ: 4Т.е. каждый экземпляр занимает в памяти столько места, сколько занимают все нестатические свойтсва класса.
Есть правда одно исключение. Если вовсе не опишете ни одного свойства, то экземпляр будет всё равно занимать хотя бы один байт (от реализации зависит, но не менее одного). Т.е. экземпляр не может вообще не занимать памяти. Так сделано потому, что экезмпляр по-любому обязан иметь адрес (тот самый this).
Любая функция , как и метод должна оперировать переменными. Если просто функция оперирует с переменной по адресу (привет неявно заданый указатель, то есть ссылка на вновь созданую переменую, но не в куче). То метод оперирует переменной находящейся по адресу (this+приращение в очередности перемнной в классе). Статическая фунция это "дружеская" функция класса. Правда эволюционно из статич функции класса появилась функция друг класса.
Метод класса (не статический) привязан к конкретному экземепляру. И эта привязка реализуется через этот самый this. Физически код один и тот же для всех экземпляров. Но как раз при помощи this он обращается к свойствам именно своего экземпляра, а не какого-то другого.
Правильно.
Но именно поэтому это никак не может быть типом 1. Ибо функция типа 1, как и переменная типа 1, создается для конкретного экземпляра класса (физически разный код). И только поэтому она имеет возможность обращаться к любой переменной класса (как 0, так и 1 типа) без всякого this. При ее создании (в процессе выполнения - так же, как и создание переменных) она сразу настраивается на адреса всех переменных, к которым должна обращаться, и т.к. она обслуживает только переменные своего экземпляра (+статические), никакой this ей не нужен.
А классический метод класса (в точности так, как Вы его описали) - это совершенно характерный тип 2 (хитрый).
Вот только, вероятнее всего, функции типа 1 в С++ не предусмотрены. Потому как они позволяют красиво решить мою задачу, а красивого решения, как Вы утверждаете, на С++ нет.
О вей'з мир! ("горе мне" - на идиш).
Я хочу внести предложение - запретить Паскаль и его производные, как идеологически вредные! ;).
Хотя мой первый язык и был Паскаль.... На примере Андриано мы видим вредное влияние ПаскалЯ (ударение на Я) ;) .
Ну, скажем так, я, возможно, не очень понимаю Вашей классификации, но методов экземлпяра не зависящих от this не бывает - всё, что связано с экземпляром, реализуется через this.
Ну, скажем так, я, возможно, не очень понимаю Вашей классификации, но методов экземлпяра не зависящих от this не бывает - всё, что связано с экземпляром, реализуется через this.
Давайте уж уточним: под "не бывает" Вы подразумеваете "нет в С++", но никак не "не может быть".
Реализация "через this" отличается от моего типа 1:
1. Необходимостью этого самого this, откуда проистиекает неуниверсальность: не все, что можно было сделать в процедурном стиле, возможно сделать в ООП.
2. Существенной экономией памяти - нет нужды дублировать код методов в каждый экземпляр объекта.
О вей'з мир! ("горе мне" - на идиш).
Я хочу внести предложение - запретить Паскаль и его производные, как идеологически вредные! ;).
Хотя мой первый язык и был Паскаль.... На примере Андриано мы видим вредное влияние ПаскалЯ (ударение на Я) ;) .
Посмотрел время отправки. Ну да: 23.08 - пора переходить на личности.
Давайте уж уточним: под "не бывает" Вы подразумеваете "нет в С++", но никак не "не может быть".
А мы что, говорим о каких-то ещё языках?
Поскольку я всё равно не понимаю Ваших типов, был бы признателен, если бы Вы не вводили типизацию, а просто сказаил "как в таком-то языке" - мне бы это было гораздо понятнее.
А так-то, что значит "не может быть" ... "может быть" всё. Сарра, как известно, в 90 лет родила.
1. Необходимостью этого самого this, откуда проистиекает неуниверсальность: не все, что можно было сделать в процедурном стиле, возможно сделать в ООП.
Тут что-то не то. Не вижу ничего, что можно было бы там, и нельзя было бы здесь.
Если для Си есть стандарт, для С++ есть стандарт. А вот для ООП нет стандарта. Тем более обычно путают "стандарт" ООП с "пожеланиями " в С++.
ПС: Во многих книгах ООП это маркетинговый ход и не больше.
И да если все же захотите узнать чем отличается С++ от ООП , то это наследование (делегирование) данных от родителя (владельца) потомку (субструктуре).
Ох, Евгений...
Я тут фантазирую: хочу, мол, автомобиль, который бы плавал по воде и летал по воздуху, а Вы - как дилер в автоцентре: "Приведите конкретную модель конкретного производителя". :(
Ох, Евгений...
Я тут фантазирую: хочу, мол, автомобиль, который бы плавал по воде и летал по воздуху, а Вы - как дилер в автоцентре: "Приведите конкретную модель конкретного производителя". :(
:)
Мне вот, например, в местном С++ не хватает свойств и делегатов.
Мне вот, например, в местном С++ не хватает свойств и делегатов.
Так сделайте.Но скорее вам не хватает оболочки Builder-a у которой развитая система компонентов. Вот только Builder это и есть настоящий ООП, а не С++
Можно встряну в ваш высоконаучный разговор тоже? :)
Когда-то очень давно, обучая детишек программированию (Паскакаль конечно же, но сам был ярым сионистом, разъяснял в переложении к С++), нашел как мне казалось полезную аналогию:
"Объект" - это некий "комод" с ящичками (свойствами), каждый из которых имеет свою "форму" (тип хранимых данных) и способы (методы) как туда что-то можно сложить или достать или преобразовать совместно с другими ящичками иных комодов.
"Класс" - это некая документация по изговтовлению таких "комодов". И она тоже является сама "комодом" в части статических свойств и методов.
В ящике комода (объекта) хранится нечто - значение и оно может быть у каждого объекта "своим". В ящичке класса тоже может что-то хранится и оно будет "общим" для всех комодов этого класса.
То, что Вы называете "функция типа1" - попытка найти аналогию со значением в части методов объекта: некий "свой" метод, индивидуальный к конкретному экземпляру класса. Это нарушение одного из принуипов ООП "в целом" - класс = инструкция по производству ОДИНАКОВЫХ комодов в части действий.
Решение возможно в области "свойство - указатель на метод". С описанием набора заранее подготовленных методов. В конструкторе можно "настраивать" объект на свой индивидуальный метод. Но вот насколько оно будет "красиво" .. на вкус и цвет ..
Второй момент:
Прерывание - есть некое независимое событие и в терминах классового программирования должно расписываться отдельным классом, типа "Event" .. Оюъект получатель - может принадлежать и описываться своим классом. И весь вопрос сводится к .. диспетчеризации событий и получателей.
Скорее уж С#. В билдере мощная библиотека VCL, написанная не на билдере, и притянутые из-за нее уродливые расширения языка, естесственно, нестандартные, но иначе VCL не используешь. Шарп - язык более стройный и непротиворечивый. Если бы его мелкософт не запоганил.
Arhat109-2. Здесь все намного проще. Есть двор и в нем живут много хозяев. Каждый хозяин набирал воду с одного колодца. Разумеется если дома одинаковы и одноэтажны, то носить воду трудно, но можно. Потом кто-то(к примеру решил), убрать все дома и построить многоэтажку. Но ему не хочется возится с водопроводом в доме. Пусть жильцы бегают за водой как раньше. Но тягать воду ведрами на 9-15 этаж без лифта по лестнице, тогда смысла в строительстве многоэтажного дома нет. Нужны специалисты по водопроводам в многоэтажных домах. А проектировщику это или лень или ну его на фиг. Будем строить дома по старинке.
По теме. ТС не хочет ковыряться в программном ядре Ардуино, только и всего. А так это все реально.
Возьмите файл Arduino.h
/* Arduino.h - Main include file for the Arduino SDK Copyright (c) 2005-2013 Arduino Team. All right reserved. 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef Arduino_h #define Arduino_h #include <stdlib.h> #include <stdbool.h> #include <string.h> #include <math.h> #include <avr/pgmspace.h> #include <avr/io.h> #include <avr/interrupt.h> #include "binary.h" #ifdef __cplusplus extern "C"{ #endif void yield(void); #define HIGH 0x1 #define LOW 0x0 #define INPUT 0x0 #define OUTPUT 0x1 #define INPUT_PULLUP 0x2 #define PI 3.1415926535897932384626433832795 #define HALF_PI 1.5707963267948966192313216916398 #define TWO_PI 6.283185307179586476925286766559 #define DEG_TO_RAD 0.017453292519943295769236907684886 #define RAD_TO_DEG 57.295779513082320876798154814105 #define EULER 2.718281828459045235360287471352 #define SERIAL 0x0 #define DISPLAY 0x1 #define LSBFIRST 0 #define MSBFIRST 1 #define CHANGE 1 #define FALLING 2 #define RISING 3 #if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) #define DEFAULT 0 #define EXTERNAL 1 #define INTERNAL1V1 2 #define INTERNAL INTERNAL1V1 #elif defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) #define DEFAULT 0 #define EXTERNAL 4 #define INTERNAL1V1 8 #define INTERNAL INTERNAL1V1 #define INTERNAL2V56 9 #define INTERNAL2V56_EXTCAP 13 #else #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) #define INTERNAL1V1 2 #define INTERNAL2V56 3 #else #define INTERNAL 3 #endif #define DEFAULT 1 #define EXTERNAL 0 #endif // undefine stdlib's abs if encountered #ifdef abs #undef abs #endif #define min(a,b) ((a)<(b)?(a):(b)) #define max(a,b) ((a)>(b)?(a):(b)) #define abs(x) ((x)>0?(x):-(x)) #define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt))) #define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5)) #define radians(deg) ((deg)*DEG_TO_RAD) #define degrees(rad) ((rad)*RAD_TO_DEG) #define sq(x) ((x)*(x)) #define interrupts() sei() #define noInterrupts() cli() #define clockCyclesPerMicrosecond() ( F_CPU / 1000000L ) #define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() ) #define microsecondsToClockCycles(a) ( (a) * clockCyclesPerMicrosecond() ) #define lowByte(w) ((uint8_t) ((w) & 0xff)) #define highByte(w) ((uint8_t) ((w) >> 8)) #define bitRead(value, bit) (((value) >> (bit)) & 0x01) #define bitSet(value, bit) ((value) |= (1UL << (bit))) #define bitClear(value, bit) ((value) &= ~(1UL << (bit))) #define bitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit) : bitClear(value, bit)) // avr-libc defines _NOP() since 1.6.2 #ifndef _NOP #define _NOP() do { __asm__ volatile ("nop"); } while (0) #endif typedef unsigned int word; #define bit(b) (1UL << (b)) typedef bool boolean; typedef uint8_t byte; void init(void); void initVariant(void); int atexit(void (*func)()) __attribute__((weak)); void pinMode(uint8_t, uint8_t); void digitalWrite(uint8_t, uint8_t); int digitalRead(uint8_t); int analogRead(uint8_t); void analogReference(uint8_t mode); void analogWrite(uint8_t, int); unsigned long millis(void); unsigned long micros(void); void delay(unsigned long); void delayMicroseconds(unsigned int us); unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout); unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout); void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val); uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder); void attachInterrupt(uint8_t, void (*)(void), int mode); void detachInterrupt(uint8_t); void setup(void); void loop(void); // Get the bit location within the hardware port of the given virtual pin. // This comes from the pins_*.c file for the active board configuration. #define analogInPinToBit(P) (P) // On the ATmega1280, the addresses of some of the port registers are // greater than 255, so we can't store them in uint8_t's. extern const uint16_t PROGMEM port_to_mode_PGM[]; extern const uint16_t PROGMEM port_to_input_PGM[]; extern const uint16_t PROGMEM port_to_output_PGM[]; extern const uint8_t PROGMEM digital_pin_to_port_PGM[]; // extern const uint8_t PROGMEM digital_pin_to_bit_PGM[]; extern const uint8_t PROGMEM digital_pin_to_bit_mask_PGM[]; extern const uint8_t PROGMEM digital_pin_to_timer_PGM[]; // Get the bit location within the hardware port of the given virtual pin. // This comes from the pins_*.c file for the active board configuration. // // These perform slightly better as macros compared to inline functions // #define digitalPinToPort(P) ( pgm_read_byte( digital_pin_to_port_PGM + (P) ) ) #define digitalPinToBitMask(P) ( pgm_read_byte( digital_pin_to_bit_mask_PGM + (P) ) ) #define digitalPinToTimer(P) ( pgm_read_byte( digital_pin_to_timer_PGM + (P) ) ) #define analogInPinToBit(P) (P) #define portOutputRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_output_PGM + (P))) ) #define portInputRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_input_PGM + (P))) ) #define portModeRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_mode_PGM + (P))) ) #define NOT_A_PIN 0 #define NOT_A_PORT 0 #define NOT_AN_INTERRUPT -1 #ifdef ARDUINO_MAIN #define PA 1 #define PB 2 #define PC 3 #define PD 4 #define PE 5 #define PF 6 #define PG 7 #define PH 8 #define PJ 10 #define PK 11 #define PL 12 #endif #define NOT_ON_TIMER 0 #define TIMER0A 1 #define TIMER0B 2 #define TIMER1A 3 #define TIMER1B 4 #define TIMER1C 5 #define TIMER2 6 #define TIMER2A 7 #define TIMER2B 8 #define TIMER3A 9 #define TIMER3B 10 #define TIMER3C 11 #define TIMER4A 12 #define TIMER4B 13 #define TIMER4C 14 #define TIMER4D 15 #define TIMER5A 16 #define TIMER5B 17 #define TIMER5C 18 #ifdef __cplusplus } // extern "C" #endif #ifdef __cplusplus #include "WCharacter.h" #include "WString.h" #include "HardwareSerial.h" #include "USBAPI.h" #if defined(HAVE_HWSERIAL0) && defined(HAVE_CDCSERIAL) #error "Targets with both UART0 and CDC serial not supported" #endif uint16_t makeWord(uint16_t w); uint16_t makeWord(byte h, byte l); #define word(...) makeWord(__VA_ARGS__) unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L); unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L); void tone(uint8_t _pin, unsigned int frequency, unsigned long duration = 0); void noTone(uint8_t _pin); // WMath prototypes long random(long); long random(long, long); void randomSeed(unsigned long); long map(long, long, long, long, long); #endif #include "pins_arduino.h" #endifСтрока 150
voidattachInterrupt(uint8_t,void(*)(void),intmode);Вот такой же метод и надо создать для метода класса внутри описания классаПС: Боюсь я тоже не знаю где это хозяйство лежит и как это все организовать. :(А метод класса по умолчанию другую сигнатуру имеет. Если он не статический.
Чтото вроде void Method(TThisClass *this), хотя сам метод объявлен как void Method(void).
Сами-то пробовали? Боюсь, что нет.
Попробуйте и расскажете нам, что вышло :)
Понятно, что мой поиск привел к файлу.WInterrupts.c
А конкретно к такой функции.
void attachInterrupt(uint8_t interruptNum, void (*userFunc)(void), int mode) { if(interruptNum < EXTERNAL_NUM_INTERRUPTS) { intFunc[interruptNum] = userFunc; // 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 attachInterrupt(uint8_t interruptNum, void (*userFunc)(void), int mode) { if(interruptNum < EXTERNAL_NUM_INTERRUPTS) { intFunc[interruptNum] = userFunc; <--- массиву функции отработки прерываний.Вот она выше объявляется
static volatile voidFuncPtr 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 };И что бы окончательно добавить "вишенку в торт" рекомедую изучить этой файл wiring_private.h
qwone, а чего Вы ищите то?
Про прерывания - они связаны с совершенно статическими сущностями. Хоть пин или таймер, хоть вектор прерывания хоть его обработчик при вызове - по природе своей статика, существующая только в одном экземпляре и доступная из любого места кода. Тут нечего обсуждать. И без статики прерывание не раскрутится. Если нужен доступ к не статическим полям и методам - прийдется статически хранить this. Это не сложно, возможны варианты, по простому так.
class PinInt { static void Run(void); virtual void exec(void){}; public: PinInt(byte pin, byte mode); }; PinInt* IntThis; void PinInt::Run(void){IntThis->exec();}; PinInt::PinInt(byte pin, byte mode) { IntThis=this; attachInterrupt(pin, &Run ,mode); } class PinIntMy : public PinInt { boolean b; virtual void exec(void){b=!b;digitalWrite(13,b);}; public: PinIntMy(byte pin, byte mode):PinInt(pin, mode){}; }; void setup(void) { new PinIntMy(0, CHANGE); pinMode(13,OUTPUT); } void loop(void){ }Код проверен, лампа мигает, первые 15 строк можна и в либу сунуть. Все наследники PinInt будут получать вызовы exec при прерываниях. Кому хочется может деструкторы подописывать.
Но такой подход - не гуд, ресурсы "на ветер". Но для желающих подрочить на тему "все в обектах" - самое оно.
ПС. У меня дежавю, или эта тема уже обсуждалась недавно? Там еще хотели ардуиновские либы перерабатывать для хранения в них this...
Logik. Вобще-то я ничего не ищу. Я просто показываю (а) как это организовано в Ардуине (б) каковы вы не использовати парадигмы программирования (Си,Си++,ОПП, или другие языки) все равно приходится использовать программные переходники с одного языка на другой, которые уже кривые изначально в рамках конкретного языка (в) статические динамические . Проще говоря если у вас к примеру два прерывания, а классов три или больше, то многим классам может банально прерываний не хватить. И разумеется если объекты классов можно создавать и уничтожать , меняя тем самым их количество. То если прерываний в МК два, то больше их не получится. Или даже можно так сказать так, вы можете динамически создавать сколько угодно прерываний, но до тех пор пока их колличество не больше количества реальных аппаратных.
ПС: Да и чего вы боитесть всяких this . Это банальный указатель. Ну сколько там в нем байт потратится. 2-3 байта. Да и массив прерываний . Ну будет вместо переменной указателя на void func , void this->func. И все.
ППС: Да и забейте вы на малую память . перейдете на МЕГА, DUE и прочее. Главное уметь правильно распорядится этими ресурсами
Я не боюсь this, что подтверждаю его использованием в коде выше. Я боюсь результатов програмирования такими спецами, которые "забейте вы на малую память . перейдете на" Им всегда ресурсов не хватит немного, и на мегах и на гигах. А пользоватся такими прогами - адское занятие. А уж когда в исходники смотрю - рыдаю.
//Проще говоря если у вас к примеру два прерывания, а классов три или больше, то многим классам может банально прерываний не хватить.
Да кол-во обработчиков понятно ограничено. Но ничто не мешает из обработчика вызывать соответствующие методы любого числа классов. Снова прийдется иметь чего статического чтоб их хранить. Опять же, чтоб Вы не подумали что я боюсь статических переменных, их у меня в коде МК всегда много есть (а в ПК их почти нету у меня). И срать я хотел на нормы ООП. Но если тема про прерывание в классе, да еще чтоб красиво, то важно чтоб ООП-исты понимали, что механизм прерываний без статики не реализуем в принципе. Вопрос только где и как хранить статические данные.
Вообще-то статика это часный случай динамики. Так переменные представителя класса "статичны" в пределах жизни этого представителя. Да и вообще не существуют абсолютно статические данные. Скорее это данные срок жизни которых равен сроку жизни программы. Да в Ардуине программа находится на флешке. Но ведь мы наблюдаем системы в которые программы заливаются на ОЗу. Вот и ответьте куда попадают статические данные программы, когда эту программу удаляют из системы. Вот такая выходит теория относительности для программирования. На нижних уровнях больших компьютеров уже не определишь где софт, где хард и где между ними граница. А вы вздыхаете где хранить статические данные.
А про нормы ООП. Так нет таких норм. Еще не появился "Срауструпп ООП", который написал как надо и как не надо писать. Все идет на уровне догадок и проб. Но думаю если все же книга и написана, то хранится где-то в транснациональной компании, так как это очень сильная ступенька в освоении суперпроцессоров. Почище ядерного оружия.
Вот и ответьте куда попадают статические данные программы, когда эту программу удаляют из системы.
В софтовый рай или хардовый ад. Или наоборот ;)
Вам "а поговорить?" хочется?
И что бы окончательно добавить "вишенку в торт" рекомедую
Давайте Вы сначала покажете нам как использовать Вашу замечательную конструкцию
А потом уж будете что-то рекоммендовать. Ладно? :)
Всё уже написано до вас: Метод объекта в качестве обработчика прерывания . К сожалению, понять только этого никто из местных так и не смог (насколько я вижу) в отличие от зарубежных форумчан.
Всё уже написано до вас: Метод объекта в качестве обработчика прерывания . К сожалению, понять только этого никто из местных так и не смог (насколько я вижу) в отличие от зарубежных форумчан.
Вы знаете, я тогда посмотрел что там за библиотека. Боюсь, что это Вы не поняли того, что Вам тогда говорил Logic. Там (в той библиотеке) написано именно то, о чём он Вам говорил, и о чём я говорил в этой теме в посте #9
Другое дело, что ТС ведь просил "красивое решение", я то решение я красивым не считаю.
Я развил свою идею до полностью динамической привязки различных методов к прерываниям. Мне это нужно для модульности программы мк. Я могу с таким подходом хранить во внешнем файле привязку какого-то модуля к конкретному прерыванию. Например, привязать modbus slave к любому из доступных usart. Программа стартует, считывает файл настроек и назначает mb slave на нужный порт. Но это уже сложнее. Я также могу в процессе работы программы изменять обработчики прерываний. TList там используется.
Как по мне, "красиво" - это когда код делает за тебя большинство операций, автоматизируя рутинную работу и скрывая сложную реализацию.
Ну, о понятиях красиво/некрасиво мы спорить не будем, правда? :)))
Ну как жеж "не будем"? На фкус и цывет ..
По мне, так "красиво", когда в программе нет ни одной лишней команды. Всё далется четко и ровно без "костылей ленивого программера". :)
Ну как жеж "не будем"?
Так забанють же-ж, как злостных флудерастов .... ну, так и быть, для затравки срача расставьте по предпочтению: Милен Демонжо, Эмануэль Беар и Брижит Бардо (разумеется, все три - в лучшие их годы).
:))))