Вызов attachInterrupt из объекта класса.
- Войдите на сайт для отправки комментариев
Переписываю библиотеку (переделываю DHT на прерываниях - не нравятся мне задержки при получении показаний с датчика - но не об этом речь). И столнулся с интересной проблемой.
Для наглядности приведу код (думаю, проще будет понять, что мне необходимо):
Скетч (буду удалять ненужное, чтоб не захламлять код):
#include <DHT.h> #define DHT22_PIN 2 #define DHTTYPE_22 DHT22 DHT dht(DHT22_PIN, DHTTYPE_22); void setup() { //............ delay(1000); //............ } void loop() { //............ dht.mainfunc(); // Крутится постоянно if(millis()-prevTime >= interval) { if (dht.NachIzmer(true)) //запускаем измерения prevTime=millis(); } //............ }
Ну, соответственно, класс в хидере:
class DHT { private: //.................... public //.................... void mainfunc(void); void Interrupt(void); //.................... };
И собственно, cpp:
// Основная функция void DHT::mainfunc() { //.................... // Если что-то там..., то нужно запустить прерывание if (ctoto_tam) attachInterrupt(0, Interrupt, RISING); //.................... } // Обработчик прерывания void DHT::Interrupt() { int a=1; }
Понятное дело, что не работает. Компилятор жалуется на несовпадение типов: error: argument of type 'void (DHT::)()' does not match 'void (*)()'
На буржуйском форуме нашел почти такую же тему. Начинаю понимать, что совсем не понимаю что там предлагают.
Суть проблемы, как мне видится то, что у обработчика прерывания судя по описанию не должно быть ни параметров ни возвращаемого значения. А когда вызывается эта функция объектом класса он как минимум передаёт параметром this (возможно я не прав, поэтому и создаю тему и прошу разъяснить).
Функция attachInterrupt объявляется в WInterrupts.h как
void attachInterrupt(uint32_t pin, void (*callback)(void), uint32_t mode);
И определение ее:
void attachInterrupt(uint8_t interruptNum, void (*userFunc)(void), int mode) { if(interruptNum < EXTERNAL_NUM_INTERRUPTS) { intFunc[interruptNum] = userFunc; switch (interruptNum) { //Дальше идет включение прерываний } } }
Не понятна строчка выше:
static volatile voidFuncPtr intFunc[EXTERNAL_NUM_INTERRUPTS]; // volatile static voidFuncPtr twiIntFunc;
Может не туда полез - в общем, запутался окончательно. Отказываться от такой конструкции не хотелось бы, потому как на два прерывания можно повесить два датчика (два объекта класса), однако, в обработчике прерывания хотелось бы оперировать именно с переменными данного объекта.
Надеюсь, хоть немного понятно объяснил.
Посему вопрос. Как заставить данную конструкцию работать и возможно ли это в принципе?
Прошу помидорами не кидать, а разъяснить по существу.
Спасибо.
Interrupt должна быть обычной функцией без параметров. А у Вас это метод класса (нужно помнить, что в методы и функции класса, первым параметром неявно передается указатель на объект). Вообще иногда так делают, но тогда этот метод должен быть статическим, но Вам нужен доступ к членам класса, а из статического метода к ним доступа нет, по понятной причине, статический метод не знает из какого объекта его запустили.
Самое тупое - передать в конструктор DHT указатель на эту функцию и использовать его в методе mainfunc.
Возможно в Вашем случае лучше сделать два обработчика, каждый из них будет знать только свой объект, хотя вполне возможно, что каждый обработчик будет выывать метод класса для определенного объекта.
Приблизительный скелет такой:
Понятно, что можно завести переменную (например, указатель на активный объект) и отслеживать, какой именно объект сейчас перехватывает прерывания, но это гемор еще тот, а если Вам нужно одновременно, тогда этот метод не подойдет.
А можно "живой" пример?
Учите мат.часть, изучайте примеры. Тем более компилятор Вам всё сказал корректно, а я Вам объяснил, почему он это сказал. А если вообще ничего непонятно, то может и браться не стоит?
Спасибо за напутствие. Разобрался.
Пример обработчика в виде статического метода класса, возможно кому то будет интересно. Это только скелет и код не рабочий, но компилируется.
Т.е. я правильно понял, что использовать attachInterrupt в экземпляре класа не получиться ?
Использовать можно, только особого смысла в этом нет. Если в двух словах опишите задачу, попробую пояснить конкретней
Спасибо.
в частности , используется примерно такой код
Для удобочитаемости, т.к. таких мелких функций подучения данных от разных датчиков много, я решил освоить библиотеки :) Попытался пененести это в библиотеку и столкнулся с уже обсуждаемой проблемой.
Спасибо!
Вот один из вариантов реализации. Он не оптимальный и не без "греха", потому рассматриваю его как учебный. Вот код:
Главное:
1. Храним данные об одном датчике в отдельной структуре.
2. Соответственно сколько датчиков, столько элементов в массиве.
3. Каждый класс цепляет к своему датчику внешний обработчик (указатель на который хранится там же в массиве).
4. В цикле опрашиваем счетчики каждого сенсора и выводим в Serial, другой вариант, считаем и пересчитываем как надо.
5. Делаем паузу в 1 секунду, чтобы счетчики успели набрать информацию. И переходим к п.4.
Что здесь реализовано криво:
1. В loop в цикле между считыванием данных счетчиков есть "большая" пауза для вывода в Serial, оптимальней считать все счетчики в другой моссив (сделать копию)
2. Выполнить нужные пересчеты
3. Вывести рассчитанные значения в Serial
Разумеется можно добавить в класс метод для пересчета данных по какой то общей формуле и использовать эту формулу для каждого счетчик в цикле, например:
Хорош на сегодня, вопросы есть или вообще ничего непонятно?
UPD: Код компилируется, но работает или нет - не знаю. И с 0 пином поаккуратней, я его тут использую, но может быть больно (лучше его не использовать).
UPD2: m_Counter надо объявить volatile, чтобы его компилятор не оптимизировал.
Спасибо, вроде понятно. Надо попробовать переложить в мои реалии :)
Кстати, про сруктуры как способ хранения данных датчиков.
Кроме датчиков протока, будут еще датчики тока и пару десятков температурных далласов. Я предположил что удобно хранить данные о датчиках в структорах, структуры в связанном списке и одной процедурой, перебирая список опрашивать датчики...
Т.е. задача в том, что бы не иметь 30 мелких функций , которые обрабатывают каждый отдельный датчик, а проходить список...
Вот это почти работало :) на 2560 , на нано сдыхает на 3-4 структурах в списке. При этом при компиляции вроде места остается достатоно, т.е. половина на Нано.
Подскажите, плиз, как бы мне правильно работать со структурами, что бы организовать их хранение и обработку ? Массив структур тоже пробовал. Может подход изначально не верный ? Структуры и linkedlist не самоцель, может лучше как то по другому ?
Спасибо.
Навскидку, первое, что нужно помнить при большом количестве данных, что у нас очень мало ОЗУ и намного больше Flash памяти, потому, initStruct можно выкинуть совсем и инициализировать все эти структуры статически, с распложением во Flash, определив это, например, как массив:
А также использовать поменьше классов (здесь они не всегда оправданы и нужны, но иногда можно) и динамического выделения памяти (её и так мало).
Постараться не создавать локальных (см. initStruct) переменных типа:
потому что они скорей всего пойдут в стек (если компилятор не соптимизирует, не проверял), а памяти (в т.ч. стека) маловато.
Если более подробно обратиться к этой конструкции, то мы сначала создаем в стеке структуру, заполненную данными, потом копируем её в связанный список, т.е. в какой то момент получется две копии одного и того же. Если же Вы скажете, что add в связанный список не копирует данные, то при выходе из initStruct стек будет потерян и данные в связанном списке будут содержать мусор. Уфф, несколько сумбурно, надеюсь понятно.
Безусловно обращение к Flash может быть медленней, нежели к ОЗУ, но это не всегда проблема.
Вообще эта задача уже вышла за рамки темы ;)
В Cortex с флешем получше, там просто пишешь const и всё уже там, во Flash, а здесь приходится париться с PROGMEM, а потом хитро читать. Впрочем это не стоит обсуждения :)
Кстати, еще, увидел в комментариях, конструкция типа такой:
Опасна тем, что в temp будет создана КОПИЯ объекта, лучше использовать const DS &temp = ...; или типа того, т.е. использовать ссылку из списка, а не копию объекта.
Так что, либо учить матчасть, если пользоваться списками, указателями, динамически создаваемыми массивами, ссылками, либо данные хранить во Flash, тогда и памяти хватит.
Конечно люди пишут так, как пытаетесь Вы, такие проекты в сети есть, но оправдан ли такой подход на МК? (риторический). На больших компах - да, а здесь, лучше воздержаться от хитростей большого С++.
Может вечером еще посмотрю, работы много.
kisoft, спасибо! Большое спасибо!
Ща попробую. Мне нужно осознать как правильно работать с такими данными.
Завести отдельную тоему про структуры и списки ??
P.S.>
А также использовать поменьше классов (здесь они не всегда оправданы и нужны, но иногда можно) и динамического выделения памяти (её и так мало).
т.е. не пытать ся запихать все подряд фунции в классы в либах ?? :)
Про структуры создал отдельную тему
http://arduino.ru/forum/programmirovanie/khranenie-dannkh-datchikov-stru...
С либами не всё так просто. Использование классов - это, прежде всего, удобство использования. Потому что если это будет набор функций, будет сложнее искать и понять, для чего эти функции нужны. Как правило эти объекты не создаются десятками, а имеют всего один экземпляр. Хотя, безусловно, бывают исключения.
Я так понимаю, использование либы и создание экземпляра какого либо класса из нее отнимает больше памяти, нежели просто описанные функции в коде... Я и хотел все служебные функции, типа опроса датчиков (тут кстаи интересно , датчиков тока будет 3-4, датчиков давления 2, далласов 22-25, датчиков расхода 3), приема / отправки СМС, передачи данных через GPRS и т.п. вынести в либы, чтоб глаза не мазолили...
Может отказаться о использования DallasTemperature, и просто описать свой класс, мне нужно тока получить температуру по адресу и все. И будет 25 экземпляроов, с разными адресами и по разному поименованные :) ?
Я ответил в той теме, чтобы здесь не оффтопить.
В общем, свой вопрос я решил просто :) и не красиво.
В качестве либ использую .ino фалы и заголовки .h
Все работает, включая прерывания, и это логично....