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

Famouspilot
Famouspilot аватар
Offline
Зарегистрирован: 19.01.2016

Доброго времени суток.

Пишу библиотеку для обработки сигналов (считывание длительности импульса c приемника для радиоуправления, тот же управляющий сигнал для сервопривода) c аналоговых пинов на Mega 2560. Сама библиотека основана на другой библиотеке, PinChangeInterrupt.

В чем суть - при компиляции выскакивают следующие ошибки:

C:\Program Files (x86)\arduino-1.6.6\libraries\Famouspilot/Famouspilot.h:18:17: error: expected unqualified-id before 'unsigned'

     Famouspilot(unsigned int pins, unsigned int count);

                 ^

C:\Program Files (x86)\arduino-1.6.6\libraries\Famouspilot/Famouspilot.h:18:17: error: expected ')' before 'unsigned'

C:\Program Files (x86)\arduino-1.6.6\libraries\Famouspilot/Famouspilot.h:16:19: error: an anonymous struct cannot have function members

 class Famouspilot {

                   ^

C:\Program Files (x86)\arduino-1.6.6\libraries\Famouspilot/Famouspilot.h:39:2: error: abstract declarator '<anonymous class>' used as declaration

 }; //

  ^

sketch_mar02c:3: error: expected constructor, destructor, or type conversion before '(' token

exit status 1
expected constructor, destructor, or type conversion before '(' token

Использовал следующий скетч:

#include <Famouspilot.h>
unsigned int receiverPins[3]={0, 1, 2};
Famouspilot RD(receiverPins, 3);
void setup() {
}

void loop() {
}

Хидер библиотеки:

#ifndef Famouspilot
#define Famouspilot
#define NO_PORTB_PINCHANGES
#include <Famouspilot.h>
#if defined(ARDUINO) && ARDUINO >= 100
#include <Arduino.h>
#else
#include <WProgram.h>
#endif

class Famouspilot {
  public:
    Famouspilot(unsigned int pins, unsigned int count);
    boolean Error();
    void Launch();
    void Stop();
    unsigned int GetRCvalue(unsigned int channel);
    unsigned int GetPulseDuration(unsigned int channel); 
    boolean test();
    void setNewBounds(unsigned int channel, unsigned int newLowBound, unsigned int newHighBound);
  private:
    boolean Fuse = false, workState = false;
    unsigned int receiverPinCount;
    void rising();
    void falling();
    //inline byte highlightPin();
    unsigned int *pulseDuration;
    unsigned long int *lastTime;
    unsigned int *usedPins;
    const byte pciPins[8] = {A8, A9, A10, A11, A12, A13, A14, A15};
    unsigned int *lowBound; 
    unsigned int *highBound;
    unsigned long int timeBuffer;
}; //
//
#endif

СРР:

#include <PinChangeInt.h>
#include <Famouspilot.h>
#if defined (ARDUINO) && ARDUINO >= 100
#include <Arduino.h>
#else
#include <WProgram.h>
#endif

Famouspilot::Famouspilot(unsigned int pins, unsigned int count){
  receiverPinCount = count;
  usedPins = (unsigned int*)malloc(receiverPinCount*sizeof(unsigned int));
  for(int i = 0; i<receiverPinCount; i++) usedPins[i]=pins[i]
  if(receiverPinCount<8){ 
  Fuse = true;
  for(int i = 0; i<receiverPinCount, i++) {
    if(usedPins[i]>7)  Fuse = false;
    else 
 pinMode(pciPins[usedPins[i]], INPUT_PULLUP); 
  }
}
else Fuse = false;  
if (Fuse) {
  pulseDuration = (unsigned int*)malloc(receiverPinCount*sizeof(unsigned int));
  lastTime = (unsigned long int*)malloc(receiverPinCount*sizeof(unsigned long int));
  lowBound = (unsigned int*)malloc(receiverPinCount*sizeof(unsigned int));
  highBound = (unsigned int*)malloc(receiverPinCount*sizeof(unsigned int)); 
}
}

void Famouspilot::Launch(){
  if (Fuse) {
  for(int i = 0; i<receiverPinCount, i++) {
    PCintPort::attachInterrupt(pciPins[usedPins[i]], rising(), FALLING); 
    PCintPort::attachInterrupt(pciPins[usedPins[i]], falling(), RISING);
  }
  workState = true;
  }
}

void Famouspilot::Stop(){
  if (Fuse) {
  for(int i = 0; i<receiverPinCount, i++) {
    PCintPort::detachInterrupt(pciPins[usedPins[i]]); 
    PCintPort::detachInterrupt(pciPins[usedPins[i]]);
  }
  workState = false;
  }
}

unsigned int Famouspilot::GetRCvalue(unsigned int channel) {
  if(Fuse) {
  if (channel<=receiverPinCount)
  return constrain(map(pulseDuration[channel-1], lowBound[channel-1], highBound[channel-1], 0, 180), 0, 180);
  else return (NULL);
  }
  else return (NULL);
}

unsigned int Famouspilot::GetPulseDuration(unsigned int channel) {
  if(Fuse) {
  if (channel<=receiverPinCount)
  return (pulseDuration[channel-1]);
  else return (NULL);
  }
  else return (NULL);
}

boolean Famouspilot::Error(){
  return (!Fuse);
}

void Famouspilot::rising() {
  timeBuffer = micros();
  for (int i = 0; i<receiverPinCount; i++){
    if (pciPins[usedPins[i]] == PCintPort::arduinoPin)
    lastTime[i]=timeBuffer;    
  }
}

void Famouspilot::falling() {
  timeBuffer = micros();
  for(int i = 0; i<receiverPinCount; i++){
    if(pciPins[usedPins[i]] == PCintPort::arduinoPin)
      pulseDuration[i] = timeBuffer - lastTime[i];   
  }
}

boolean Famouspilot::test() {
  if(Fuse) {
  boolean testBody = true;
  unsigned long int currentTestBodyTime = micros();
  for(int i = 0; i<receiverPinCount; i++) {
    if(currentTestBodyTime>lastTime[i]+100000) testBody = false;
  }
  return (testBody);
  }
  else return (false);
}

void Famouspilot::setNewBounds(unsigned int channel, unsigned int newLowBound, unsigned int newHighBound) {
  if(channel<=receiverPinCount) {
    lowBound[channel - 1] = newLowBound;
    highBound[channel - 1] = newHighBound;
  }
}

Библиотеку писал (пытался) по шаблону, данному на этом же сайте. Раньше успешно запустил подобную библиотеку (правда, с "обычными" attachInterrupt), но без "класса".

В чем мои ошибки?

Прошу прощения, что в .h и .CPP слишком много (быдло)кода, по идее, он здесь не играет рояля. Если нужно, опишу что и как работает (по крайней мере, должно).

Тыкните меня лицом во все неувязки/проблемные места которые заметите, для "общего развития"

P.s. может так оказаться, что в коде какой-то элемент не используется (сырая еще).

P.p.s. в ардуино и программировании я новичок =)

alexvs
Offline
Зарегистрирован: 22.07.2014

Я конечно не большой спец в С, но  можно ли объявить входной параметр как unsigned int, а передавать массив?

Famouspilot(unsigned int pins, unsigned int count);
........................................................................
unsigned int receiverPins[3]={0, 1, 2};
Famouspilot RD(receiverPins, 3);

 

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

А что там за 4-ая строка в файле "Хидер библиотеки". Он что сам себя инклюдит?

Но проблема не в этом.

Проблема в том, что у Вас имя константы совпадает с именем класса - они подрались.

Замените в строках 1 и 2 Famouspilot на  Famouspilot_h, например, и  ошибки уйдут

Famouspilot
Famouspilot аватар
Offline
Зарегистрирован: 19.01.2016

Спасибо, исправил то, что вы указали. Но все еще выбивает такую:




sketch_mar02c:3: error: invalid conversion from 'unsigned int*' to 'unsigned int' [-fpermissive]

C:\Program Files (x86)\arduino-1.6.6\libraries\Famouspilot/Famouspilot.h:19:5: error:   initializing argument 1 of 'Famouspilot::Famouspilot(unsigned int, unsigned int)' [-fpermissive]

     Famouspilot(unsigned int pins, unsigned int count);

     ^

exit status 1
invalid conversion from 'unsigned int*' to 'unsigned int' [-fpermissive]

Думаю, что-то намутил с указателями и/или типами переменных... Не подскажете?

shuser
Offline
Зарегистрирован: 14.01.2016
unsigned int receiverPins[3]={0, 1, 2};
Famouspilot RD(receiverPins, 3);

Передавайте адресс массива Famouspilot RD(&receiverPins[0], (sizeof(receiverPins)/sizeof(receiverPins[0])));

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

В третьей строке первый параметр - массив, а в описании функции - целое число. Разберитесь, что надо и сделайте везде одинаково.

Famouspilot
Famouspilot аватар
Offline
Зарегистрирован: 19.01.2016

Спасибо, разобрался, теперь эти ошибки не вылазят, но валятся другие:

C:\Program Files (x86)\arduino-1.6.6\libraries\Famouspilot\Famouspilot.cpp: In member function 'void Famouspilot::Launch()':

C:\Program Files (x86)\arduino-1.6.6\libraries\Famouspilot\Famouspilot.cpp:50:69: error: no matching function for call to 'PCintPort::attachInterrupt(const byte&, <unresolved overloaded function type>, int)'

     PCintPort::attachInterrupt(pciPins[usedPins[i]], rising, FALLING); 

                                                                     ^

C:\Program Files (x86)\arduino-1.6.6\libraries\Famouspilot\Famouspilot.cpp:50:69: note: candidate is:

In file included from C:\Program Files (x86)\arduino-1.6.6\libraries\Famouspilot\Famouspilot.cpp:1:0:

C:\Program Files (x86)\arduino-1.6.6\libraries\PinChangeInt/PinChangeInt.h:421:8: note: static int8_t PCintPort::attachInterrupt(uint8_t, PCIntvoidFuncPtr, int)

 int8_t PCintPort::attachInterrupt(uint8_t arduinoPin, PCIntvoidFuncPtr userFunc, int mode)

        ^

C:\Program Files (x86)\arduino-1.6.6\libraries\PinChangeInt/PinChangeInt.h:421:8: note:   no known conversion for argument 2 from '<unresolved overloaded function type>' to 'PCIntvoidFuncPtr {aka void (*)()}'

C:\Program Files (x86)\arduino-1.6.6\libraries\Famouspilot\Famouspilot.cpp:51:69: error: no matching function for call to 'PCintPort::attachInterrupt(const byte&, <unresolved overloaded function type>, int)'

     PCintPort::attachInterrupt(pciPins[usedPins[i]], falling, RISING);

                                                                     ^

C:\Program Files (x86)\arduino-1.6.6\libraries\Famouspilot\Famouspilot.cpp:51:69: note: candidate is:

In file included from C:\Program Files (x86)\arduino-1.6.6\libraries\Famouspilot\Famouspilot.cpp:1:0:

C:\Program Files (x86)\arduino-1.6.6\libraries\PinChangeInt/PinChangeInt.h:421:8: note: static int8_t PCintPort::attachInterrupt(uint8_t, PCIntvoidFuncPtr, int)

 int8_t PCintPort::attachInterrupt(uint8_t arduinoPin, PCIntvoidFuncPtr userFunc, int mode)

        ^

C:\Program Files (x86)\arduino-1.6.6\libraries\PinChangeInt/PinChangeInt.h:421:8: note:   no known conversion for argument 2 from '<unresolved overloaded function type>' to 'PCIntvoidFuncPtr {aka void (*)()}'

exit status 1
Ошибка компиляции.

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

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

Famouspilot пишет:

Может в том, что обработчики прерываний rising() и falling() являются функциями класса?

Нестатическими? ну, это точно беда. Они могут быть либо честными функциями, либо статическими методами. Методами экземпляра они не могут быть никак - где они this-то возьмут?

Famouspilot
Famouspilot аватар
Offline
Зарегистрирован: 19.01.2016

Прошу прощенния, в ооп я не силен)

Как это можно решить? 

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

Сейчас поздно, давайте завтра или в выходной (не уверен, что завтра смогу).

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

Давайте для начала немного теории.

Внутри класса можно объявлять переменные (называются «свойства») и функции (называются «методы»). Причём и то и другое можно объявлять как со словом static (статические свойства и методы), так и без него (свойства и методы экземпляра).

Когда Вы объявляете в программе переменную с типом – имя Вашего класса,

МyClass a, b;

такая переменная называется экземпляром класса.

Так вот, статические свойства и методы не привязаны к экземпляру, а существуют в единственном числе для всего класса. А свойства и методы экземпляров – привязаны к своим экземплярам.

Давайте для примера посмотрим на простенький класс для rgb-светодиода.

class RGBLed {
public:
	inline RGBLed(const int8_t pR, const int8_t pG, const int8_t pB) {
		pinRed = pR;
		pinGreen = pG;
		pinBlue = pB;
	}
	inline void setColor(const uint8_t r, const uint8_t g, const uint8_t b) const { 
		analogWrite(pinRed, colorConvert(r));
		analogWrite(pinGreen, colorConvert(g));
		analogWrite(pinBlue, colorConvert(b));
	}

	static uint8_t colorConvert(const uint8_t c) {
		return (uint8_t) (((int) c) * 100 / ((int) pwmResolution));
	}
	
private:
	uint8_t pinRed, pinGreen, pinBlue;
	static uint8_t pwmResolution;
};
uint8_t RGBLed::pwmResolution = 255;

void setup() {
  RGBLed led1(9,10,11);
  RGBLed led2(3,5,6);

  led1.setColor(20, 40, 100);
  led2.setColor(50, 50, 50);
  uint8_t c = RGBLed::colorConvert(80);
}

В классе свойства pinRed, pinGreen, pinBlue объявлены как свойства экземпляра (строка 21), а свойство pwmResolution (строка 22) – статическое для всего класса.

В строках 27 и 28 мы создаём два экземпляра класса: led1 и led2.

Таким образом, у нас в памяти будет существовать одна на всех переменная pwmResolution и два независимых комплекта переменных pinRed, pinGreen, pinBlue – по одному комплекту на каждый созданный экземпляр.

Поэтому pinRed, pinGreen, pinBlue будут свои для led1 и свои для  led2, а вот pwmResolution будет одна на всех.

Теперь о методах.

Метод setColor привязан к экземпляру. Поэтому, вызывать его можно только в контексте экземпляра (строки 28 и 29). При этом при вызове в строке 28, метод будет использовать (в строках 9-11) переменные pinRed, pinGreen, pinBlue экземпляра led1, а при вызове в контексте экземпляра led2 (строка 29) в строка 9-11 будут использоваться pinRed, pinGreen, led2. Метод setColor нельзя вызвать вне контекста экземпляра, т.к. он просто не будет знать с какими именно переменными pinRed, pinGreen, pinBlue ему работать, т.к. в памяти есть два комплекта этих переменных.

Метод же colorConvert – статический. Он один и не привязан к экземпляру. Его необходимо вызывать вне всякого контекста (строка 30). Он не имеет права обращаться к переменным, pinRed, pinGreen, pinBlue  т.к. он сам не привязан к экземпляру и потому понятия не имеет каким из двух комплектов этих переменных пользоваться.

И последнее, как именно организован вызов метода «в контексте экземпляра»? Очень просто. При вызове нестатического метода, ему всегда передаётся скрытый параметр this – указатель на экземпляр в контексте которого его вызывают.

Надеюсь, это понятно? Если нет, спрашивайте про непонятные места.

Теперь Вы готовы понять, почему нельзя использовать метод экземпляра класса в качестве обработчика прерываний. Вектор обработки прерывания – это просто адрес программы в памяти. Сам-то адрес метода можно запихать в вектор, только вот this-то ему кто передаст? Система обработки прерываний ничего ни про какие this’ы не знает.

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

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