Arduino objektumok megírása AVR GCC-ben

Arduino objektumok megírása AVR GCC-ben
2016-04-23T01:24:41+02:00
2016-04-24T09:36:58+02:00
2022-08-10T03:00:30+02:00
TibiK
Kedves Fórumtásak!

Segítségeteket szeretnék kérni, mert nem nagyon tudok megfejteni egyes c++ dolgokat.
Eddig WinAVR/C-ben foglalkoztam AVR-ekkel. Nemrég belefutottam egy Arduino project-be, amikor felfedeztem, hogy a fordítója ugyanez a gcc, mint ami a WinAVR is, és lehet az AVR-ket c++-ban is programozni. Nagyon megtetszett a c++, sok dolog egyszerűbben kezelhető vele. Az Arduino GNU alapú, a belső dolgai is megtalálhatók a \Arduino\hardware\arduino\avr\cores\ mappában. A kódok nem felelnek meg egy az egyben nekem, de szeretnék hasonló objektumokat készíteni. Néhány dolgot nem tudok értelmezni. A LiquidCrystal (LCD kijelző kezelés) osztály (+library) alapján csináltam egy HD44780_4bit osztályt, ami Arduino rendszerben működött is, de szeretném Arduino sallangok nélkül WinAVR-ben megírni. Így néz ki a használata:


...
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
...
lcd.print("hello, world!");
...


Az osztály számomra nem érthető része:


class LiquidCrystal : public Print {
public:
...
  virtual size_t write(uint8_t);
... 
  using Print::write;
private:
...
};


A működés lényege, hogy a kiírás formattálását a Print csinája, de az LCD kezelését, a kiírást a LiquidCrystal végzi. Addig stimmel, hogy a LiquidCrystal-t a Print-ből származtatja. Definiál benne egy write függvényt, aminek egy betüt kell kiírnia. Virtual, vagyis ha jól értem, majd később egy gyerek objektumban kellene ténylegesen megírni. Azután azt mondja, hogy használd a Print::write függvényét? A using-ról azt olvastam, hogy névtér deklarálására szolgál, de egyrészt ezt nem értem még, másrészt a  mintaprogramokban a class definíciójában még nem láttam. És azután a LiquidCrystal-ban van egy write:


inline size_t LiquidCrystal::write(uint8_t value) {
  send(value, HIGH);
  return 1; // assume sucess
}


Müxik, tehát jó, de nem értem ezt a varázslatot. Azt sem, miért kell a függvényt inline deklarálni. Ide tettem még a Print osztály deklarációját, biztos segít annak aki érti. A size_t kezdetben megzavart, de a gcc egy beépített típusa, valójában a függvények azt adják vissza, hány betüt írtak ki. Az uint8_t pedig az unsigned char (8bit) álneve. Előre is köszönöm a segítségeteket/magyarázatotokat.


class Print
{
  private:
    int write_error;
    size_t printNumber(unsigned long, uint8_t);
    size_t printFloat(double, uint8_t);
  protected:
    void setWriteError(int err = 1) { write_error = err; }
  public:
    Print() : write_error(0) {}
    int getWriteError() { return write_error; }
    void clearWriteError() { setWriteError(0); }
    virtual size_t write(uint8_t) = 0;
    size_t write(const char *str) {
      if (str == NULL) return 0;
      return write((const uint8_t *)str, strlen(str));
    }
    virtual size_t write(const uint8_t *buffer, size_t size);
    size_t write(const char *buffer, size_t size) {
      return write((const uint8_t *)buffer, size);
    }
    size_t print(const __FlashStringHelper *);
    size_t print(const String &);
    size_t print(const char[]);
    size_t print(char);
    size_t print(unsigned char, int = DEC);
    size_t print(int, int = DEC);
    size_t print(unsigned int, int = DEC);
    size_t print(long, int = DEC);
    size_t print(unsigned long, int = DEC);
    size_t print(double, int = 2);
    size_t print(const Printable&);
    size_t println(const __FlashStringHelper *);
    size_t println(const String &s);
    size_t println(const char[]);
    size_t println(char);
    size_t println(unsigned char, int = DEC);
    size_t println(int, int = DEC);
    size_t println(unsigned int, int = DEC);
    size_t println(long, int = DEC);
    size_t println(unsigned long, int = DEC);
    size_t println(double, int = 2);
    size_t println(const Printable&);
    size_t println(void);
};
Mutasd a teljes hozzászólást!
Próbáld ki és olvasd el a referenciában :)

class A { public: int f() { return 0; } }; class B: public A { public: // using A::f; int f(int a) { return a + 1; } }; int main() { B b; b.f(); return 0; }
Namespaces - cppreference.comAz a lényeg, hogy a C++-os lookup szabályok szerint a B ostályban deklarált azonos nevű metódus elrejti az ős osztályban deklarált metódust, ezért fordítási hibát kapsz using nélkül.

Neki áll keresni a B-n belül az f-metódust. Meg is találja, így nem is keres tovább. Viszont amikor az argumentumokat akarja feloldani, akkor szembesül vele, hogy nincs olyan függvény.
Mutasd a teljes hozzászólást!

  • Köszönöm a iránymutatást.
    Kicsit mazsoláztam még az Arduino kódokat. A Print az alap osztály, ebből származtatnak még Stream osztályt, és ebből csinálnak még HardwareSerial...3 osztályokat/objektumokat. Vagyis kiirás string-be, soros vonalra, illetve amit előszőr néztem külön library-ként a LiquidCrystal-ra... van megírva. A gyári soros port kezelés egy kicsit bővebben van commet-ezve, és így már jobban megértettem:


    class HardwareSerial : public Stream
    ...
      public:
    ...
        virtual size_t write(uint8_t);
        inline size_t write(unsigned long n) { return write((uint8_t)n); }
        inline size_t write(long n) { return write((uint8_t)n); }
        inline size_t write(unsigned int n) { return write((uint8_t)n); }
        inline size_t write(int n) { return write((uint8_t)n); }
        using Print::write; // pull in write(str) and write(buf, size) from Print
    ...


    Megzavart a függvény overloading, meg hogy nem ismertem az using használatát ebben a variációban. Tehát általában Print write változatok kerülnek felhasználásra, kivéve azokat a variációkat, amiket megírnak az új osztályhoz, és azért kell így megoldani, hogy az új write ne feddje el az összes Print::write-ot.
    Mutasd a teljes hozzászólást!
  • Igen, pontosan úgy van, ahogy leírtad, meg ahogy oda kommentelted.
    Mutasd a teljes hozzászólást!
abcd