проблема при создании библиотеки

Maverik
Offline
Зарегистрирован: 12.09.2012

создаю библиотеки со своими классами, для перехода от стандартной IDE в CodeBlocks

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

Вот метод, в вызове стандартной функции attachInterrupt есть обращения к методам

void my_weather_sensors::setupWeatherInts()
{ 
  pinMode(ANEMOMETER_PIN,INPUT);
  digitalWrite(ANEMOMETER_PIN,LOW);  // EXTERNAL PULL-UP 10 K USED. Turn on the internal Pull Up Resistor
  pinMode(RAIN_GAUGE_PIN,INPUT);
  digitalWrite(RAIN_GAUGE_PIN,HIGH);  // Turn on the internal Pull Up Resistor
  pinMode(VANE_PWR,OUTPUT);
  digitalWrite(VANE_PWR,LOW);
  attachInterrupt(ANEMOMETER_INT,anemometerClick,FALLING);
  attachInterrupt(RAIN_GAUGE_INT,rainGageClick,FALLING);
  interrupts();
}

void my_weather_sensors::rainGageClick()
{   long thisTime=micros()-rain_last;
    rain_last=micros();
    if(thisTime>500)   { rain_count++;}
}

void  my_weather_sensors::anemometerClick()
{ long thisTime=micros()-anem_last;
  anem_last=micros();
  if(thisTime>500)
    {  anem_count++;
       if(thisTime<anem_min)  { anem_min=thisTime;}
    }
}

получаем ошибку:

 
D:\_ARDUINO\_OUTPUT\libraries\my_weater_sensors\my_weather_sensors.cpp: In member function 'void my_weather_sensors::setupWeatherInts()':
D:\_ARDUINO\_OUTPUT\libraries\my_weater_sensors\my_weather_sensors.cpp:29: error: argument of type 'void (my_weather_sensors::)()' does not match 'void (*)()'
D:\_ARDUINO\_OUTPUT\libraries\my_weater_sensors\my_weather_sensors.cpp:30: error: argument of type 'void (my_weather_sensors::)()' does not match 'void (*)()'

 

 

 

NeiroN
NeiroN аватар
Offline
Зарегистрирован: 15.06.2013

С прерываниями такая магия не сработает - разные типы функций получаются.

В калссах надо работать напрямую с прерываниями AVR как тут

Maverik
Offline
Зарегистрирован: 12.09.2012

неужели нельзя сделать библиотеку в которой есть функции прерывания без плясок с бубном ????

Datak
Offline
Зарегистрирован: 09.10.2014

Maverik, attachInterrupt хочет получить аргумент типа "void (*)()", но weather_sensors::setupWeatherInts() ему не нравится, хотя и выглядит очень похоже на этот тип.

Просто, как у любой функции класса, у weather_sensors::setupWeatherInts() есть один скрытый аргумент - указатель на объект, т.е. на экземпляр класса.

Можно объявить методы-обработчики прерываний как static - думаю всё получится, и attachInterrupt такой указатель спокойно проглотит. Только имей в виду: обращаться из такого обработчика к другим методам и переменным класса будет уже не так легко и удобно.

 

Maverik
Offline
Зарегистрирован: 12.09.2012

не работает static

Datak
Offline
Зарегистрирован: 09.10.2014

Не работает, или не компилируется?
Работу я конечно не проверю, а скомпилировать сейчас попробовал, всё получилось.

Вот это я имел в виду:









//----------------------------------------------------

void setup( void ) { }
void loop( void ) { } 

#define ANEMOMETER_INT 1

//----------------------------------------------------

class my_weather_sensors
{
  void setupWeatherInts( );
  static void anemometerClick( );
};

void my_weather_sensors::setupWeatherInts( )
{ 
  Serial.print( "void my_weather_sensors::setupWeatherInts( )" ); 

  attachInterrupt( ANEMOMETER_INT, anemometerClick, FALLING );
  interrupts( );
}

void my_weather_sensors::anemometerClick( )
{ 
  Serial.print( "void my_weather_sensors::anemometerClick( )" ); 
}

//----------------------------------------------------

Без static будет выдаваться ошибка,  а так - всё нормально.

Maverik
Offline
Зарегистрирован: 12.09.2012

хорошо Вам, у меня не компилится.

в методах anemometerClick() и rainGageClick() используются переменные volatile, видимо поэтому все равно лезут ошибки типа такого  error: invalid use of member 'my_weather_sensors::rain_last' in static member function

если волатильные переменные ради прикола объявить еще и static , лезут ошибки множественного объявления переменной.

печалька...

Datak
Offline
Зарегистрирован: 09.10.2014

Maverik, нет, не из-за этого. Это то, о чём я писал раньше:

Цитата:
обращаться из такого обработчика к другим методам и переменным класса будет уже не так легко и удобно

static функция "не привязана" к какому-то конкретному объекту, то есть она не знает, с каким из объектов типа "my_weather_sensors" ты хочешь работать.

Если такой объект только один, можно его явно указать при обращении к переменным и методам:





























my_weather_sensors Sensor;

//...........

void my_weather_sensors::anemometerClick(  )
{
   Sensor.anem_count++;
}

Не очень удобно, я согласен.

Ещё хуже, если объектов будет несколько. Придётся перед каждым обращением выбирать нужный объект вручную - про удобство от использования классов можно забыть. :)

Я в таких случаях делаю как-нибудь так:























//----------------------------------------------------
      
void setup( void ) { }
void loop( void ) { } 
      
#define ANEMOMETER_INT 1
      
//----------------------------------------------------
      
class my_weather_sensors
{
  void setupWeatherInts( );
  void anemometerClick( );
  
  volatile unsigned int anem_count;
 
  static void anemometerInterrupt( );
};
  
void my_weather_sensors::setupWeatherInts( )
{ 
  attachInterrupt( ANEMOMETER_INT, anemometerInterrupt, FALLING );
  interrupts( );
}
      
void my_weather_sensors::anemometerClick( )
{ 
  anem_count++;
}
 
//----------------------------------------------------
  
my_weather_sensors Sensor;
 
//----------------------------------------------------
 
void my_weather_sensors::anemometerInterrupt( )
{ 
  Sensor.anemometerClick( );
}
  
//----------------------------------------------------

Если объектов несколько, обработчик прерывания должен уметь как-то определять, с каким из них работать. Тут уж я не подскажу, каждый сам придумывает способ.



















//----------------------------------------------------
  
my_weather_sensors Sensor1;
my_weather_sensors Sensor2;
 
//----------------------------------------------------
 
void my_weather_sensors::anemometerInterrupt( )
{ 
  if( ....... ) // выбор нужного объекта
    Sensor1.anemometerClick( );
  else
    Sensor2.anemometerClick( );
}
  
//----------------------------------------------------

Удачи! Оно обязано заработать. :)

Maverik
Offline
Зарегистрирован: 12.09.2012

спасибо, скомпилировалось, но пока нет возможности проверить и терзают смутные сомнения...

я пока с классами не очень дружу, не могу сообразить.

у меня алгоритм следующий - функции "anemometerClick()" и "rainGaugeClick()"  ,скажем так, низкоуровневые, т.е. непосредственно снимают циклически показания с двух погодных датчиков. допустим, создаем экземпляр класса с названием Sensor.  для удобства, создаю экземпляр прямо в сpp файле библиотеки.

т.е. речь идет о переменных  

 Sensor.anem_count
 Sensor.anem_last 
 Sensor.anem_min 
 Sensor.rain_count 
 Sensor.rain_last

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

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

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

 

 

Datak
Offline
Зарегистрирован: 09.10.2014

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

Ой, Maverik, боюсь я тебя плохому научил. :)
Совсем не "соответственно". Такое явное указание конкретного экземпляра внутри методов класса - это вынужденная мера, и этим лучше не пользоваться, по возможности.

И совсем уж неправильно - принудительно указывать конкретный объект в не статических методах класса. Не "static" методы и без этого знают, с каким экземпляром работать - со своим, конечно. Заставить их работать с "чужим" экземпляром можно, но лучше не надо. Это неправильно, и противоречит всей идеологии работы с классами, идеологии объектно-ориентированного программирования, и т.д.
И уж, точно, приведёт к полной потере логики работы программы. :)

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

По поводу вопросов - если честно, не очень я их понял. Конечно, один экземпляр класса может опрашивать датчики сколько угодно раз. И да, конечно методы класса, если они public, можно вызывать "снаружи" - а иначе, зачем бы нам нужен такой класс, который живёт где-то там в себе и не имеет связи с внешним миром.
Соответственно, если мы хотим наоборот, спрятать метод или переменную от посторонних - мы объявляем их как private. Ну это я так, напомнил.

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

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

Maverik пишет:

не работает static

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

Maxim Z.
Offline
Зарегистрирован: 12.01.2016

Я для себя нашел 4 решения:

1. Переменную anem_count делать глобальной в скетче

2. Функцию anemometerClick() так же выносить за пределы класса, в функции записать Sensor1.anem_count++; , а саму anem_count перенести в сектор public: в классе

3. Сделать по примеру Datak

4. Сделать 3 класса: первый содержит основной код с прерываниями, второй сожержит переменные которые будут приращаться (anem_count), третий создает экземпляр первого и второго классов.

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

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

Соответсвенно процедур с приращениями и проверок в классе столько, сколько создается экземпляров класса.

Как я понял, уложить все в класс нереально :(

 

P.S. Если кто знает способ - делитесь!