Forum: Mikrocontroller und Digitale Elektronik c++ PIN-Klassen-Templates und Vererbung


von Ralf G. (ralg)


Lesenswert?

Mahlzeit den Experten.

Diese Idee
Beitrag "Re: IO-Register als Template-Parameter"
würde ich gern mal weiterverfolgen.

Also (nur mal grob, zur Orientierung):
1
template <typename port_t,typename ddr_t,typename pin_t>
2
class IOpin_t
3
{
4
protected:
5
  port_t port;
6
  ddr_t ddr;
7
  pin_t pin;
8
  const uint8_t pin_nr;
9
private:
10
public:
11
  IOpin_t( pinInit n ) : pin_nr(n & 0x00FF)
12
  {
13
    if (n & pinOutput)
14
      ddr  |= pin_nr;
15
    if (n & pinPullUp)
16
      port |= pin_nr;
17
  };
18
};
19
// --------------------------------------------------------------------------------
dann:
1
template <typename port_t,typename ddr_t,typename pin_t>
2
class OutputPins_t : IOpin_t<port_t,ddr_t,pin_t>
3
{
4
private:
5
public:
6
  OutputPins_t( pinInit n ) : IOpin_t<port_t,ddr_t,pin_t>(pinOutput | n ) {};
7
  inline void On(uint8_t mask)
8
  {
9
    IOpin_t<port_t,ddr_t,pin_t>::port |=  (mask & IOpin_t<port_t,ddr_t,pin_t>::pin_nr);
10
  };
11
  inline void Off(uint8_t mask)
12
  {
13
    IOpin_t<port_t,ddr_t,pin_t>::port &= ~(mask & IOpin_t<port_t,ddr_t,pin_t>::pin_nr);
14
  };
15
  inline void Toggle(uint8_t mask)
16
  {
17
    IOpin_t<port_t,ddr_t,pin_t>::port ^=  (mask & IOpin_t<port_t,ddr_t,pin_t>::pin_nr);
18
  };
19
  inline uint8_t GetPins()
20
  {
21
    return IOpin_t<port_t,ddr_t,pin_t>::pin_nr;
22
  };
23
};
so ähnlich noch für 'InputPins_t'...

Jetzt fangen, die im Gegensatz zu 'gewöhnlichen Klassen' viel längeren 
Templates, schon langsam an zu nerven.
Aber es wird noch besser (für eine Tasten-Matrix):
1
template <typename port_t,typename ddr_t,typename pin_t, uint8_t Z>
2
class Matrix_t : Object_t
3
{
4
private:
5
  InputPins_t<port_t,ddr_t,pin_t>* Tasten;
6
  OutputPins_t<port_t,ddr_t,pin_t>* Zeilen;
7
  Debounce_s deb_var[Z];
8
  //Debounce_s* deb_var = NULL;
9
  uint8_t anz_deb_var = 0;
10
  uint8_t akt_deb_pos = 0;
11
public:
12
  Matrix_t( InputPins_t<port_t,ddr_t,pin_t>* t, OutputPins_t<port_t,ddr_t,pin_t>* z, Message_e m );
13
  void OnMessage(Message_e& m);
14
  uint16_t get_key_press();
15
};
Und das ist nur die Deklaration! An der Stelle habe ich aufgegeben. 
Dieses '<port_t,ddr_t,pin_t>' gehört natürlich zum Datentyp dazu.

(*) Lässt sich das aber vielleicht trotzdem irgendwie umgehen?

Gerade sehe ich: Das ist ja noch lange nicht alles! Für 'InputPins_t' 
und 'OutputPins_t' müssen ja unterschiedliche Parameter angegeben 
werden, sind ja zwei verschiedene Ports. Die Template-Liste ist jetzt ja 
fast schon einen Meter lang! [ siehe nochmal (*) ]

von Scelumbro (Gast)


Lesenswert?

Mal ein Vorschlag von meiner Seite
1
#define GPIOB_BASE  0x23
2
#define GPIOC_BASE  0x26
3
#define GPIOD_BASE  0x2B
4
#define GPIOE_BASE  0x2C
5
#define  GPIOF_BASE  0x2F
6
7
typedef struct
8
{
9
  uint8_t pin;
10
  uint8_t ddr;
11
  uint8_t port;
12
  
13
} GPIO_t;
14
15
template <uint8_t baseaddr> struct AVRgpioBank
16
{
17
  GPIO_t * reg;
18
  
19
  AVRgpioBank()
20
  {
21
    reg = reinterpret_cast<GPIO_t *>(baseaddr);
22
    
23
  }
24
};
25
26
template<uint8_t baseaddr, uint8_t num> struct AVRgpioPin
27
{
28
  AVRgpioBank<baseaddr> port;
29
  
30
  void setDir(const bool value)
31
  {
32
    if(value)
33
    {
34
      port.reg ->ddr |= (1<<num);
35
    }
36
  }
37
  bool get() const
38
  {
39
    return ((port.reg ->pin) && (1<<num));
40
  }
41
  void set(const bool value)
42
  {
43
    port.reg ->port |= (1<<num);
44
  }
45
  
46
  bool operator =(const bool &value)
47
  {
48
    set(value);
49
    return value;
50
  }
51
  bool operator == (const bool &value)
52
  {
53
    return (get() == value);
54
    
55
  }
56
};
57
58
int main(void)
59
{
60
61
  AVRgpioPin<GPIOC_BASE, 0> led;
62
  led.setDir(true);
63
  led = true;
64
  while(1)
65
  {
66
    //TODO:: Please write your application code
67
  }
68
}

Am Ende schnurrt das ganze zu zwei sbi zusammen :)

von Dr. Sommer (Gast)


Lesenswert?

Ralf G. schrieb:
> Lässt sich das aber vielleicht trotzdem irgendwie umgehen?
Typ-Aliase sind dein Freund. Schreib am Anfang deiner OutputPins_t
1
using B = IOpin_t<port_t,ddr_t,pin_t>;
Danach kannst du statt "IOpin_t<port_t,ddr_t,pin_t>" kurz "B" schreiben.

Ralf G. schrieb:
> template <typename port_t,typename ddr_t,typename pin_t>
> class IOpin_t
> {
> protected:
>   port_t port;
>   ddr_t ddr;
>   pin_t pin;
Was soll das denn? Du übergibst einen (vermutlich integer-) Typ und 
legst Variablen an, initialisierst sie nicht, schreibst hinein, 
transferierst die Werte aber nie in die Peripherie Register?

Und was ist das für ein komischer Namensstil, Klassen -Namen auf _t 
enden zu lassen?!

von Ralf G. (ralg)


Lesenswert?

Scelumbro schrieb:
> Mal ein Vorschlag von meiner Seite
Versuch' ich mal zu verstehen.

Dr. Sommer schrieb:
> Danach kannst du statt "IOpin_t<port_t,ddr_t,pin_t>" kurz "B" schreiben.
Stimmt, hätte ich auch selber drauf kommen können. Das ist was, was ich 
noch überschauen kann.

Dr. Sommer schrieb:
> Und was ist das für ein komischer Namensstil
Cool, was ;-)
(Ist rein privat, einfach nicht hinsehen!)

Scelumbro schrieb:
> Am Ende schnurrt das ganze zu zwei sbi zusammen :)
Leider immer nur bei solchen Minimal-Beispielen :(

Ach ja, noch was:
Dr. Sommer schrieb:
> Was soll das denn?
siehe:
http://www.mikrocontroller.net/topic/goto_post/1733891

: Bearbeitet durch User
von Scelumbro (Gast)


Lesenswert?

Ralf G. schrieb:
> Scelumbro schrieb:
>> Mal ein Vorschlag von meiner Seite
> Versuch' ich mal zu verstehen.

Das ganze ist noch sehr roh (set, setDir sind unvollständig), aber 
kompiliert.

Und warum sollte das ganze nicht auch für komplexere Projekte 
funktionieren? Solange alles zur Compile-Zeit bekannt ist kann sich der 
Compiler schön austoben.

von Ralf G. (ralg)


Lesenswert?

Ralf G. schrieb:
> (*) Lässt sich das aber vielleicht trotzdem irgendwie umgehen?

Kommando zurück! Der Aufwand lohnt nicht.
In den Fällen, wo der Compiler das komplette Programm sieht, ist 
zwischen der oben zitierten Variante ('frisieren' der io.h), der 
Übergabe von PORT/DDR/PIN als Pointer ('const' oder ganz normal) oder 
als Referenz (mit Compiler-Warnung) kein Unterschied im Ergebnis!

Dazu reicht es schon bei größeren Projekten die 'weniger erwünschte 
Variante' ;-) zu nehmen: Statt sauber alles in Modulen zu halten und dem 
Linker das Zusammenfügen zu überlassen, einfach die "*.cpp's" statt der 
Header vor der 'main()' includieren.

von Dr. Sommer (Gast)


Lesenswert?

Ralf G. schrieb:
> In den Fällen, wo der Compiler das komplette Programm sieht, ist
> zwischen der oben zitierten Variante ('frisieren' der io.h), der
> Übergabe von PORT/DDR/PIN als Pointer ('const' oder ganz normal) oder
> als Referenz (mit Compiler-Warnung) kein Unterschied im Ergebnis!
Was für eine Warnung?
Aber in der Tat werden Pointer und Referenzen quasi gleich behandelt.

Ralf G. schrieb:
> Statt sauber alles in Modulen zu halten und dem
> Linker das Zusammenfügen zu überlassen, einfach die "*.cpp's" statt der
> Header vor der 'main()' includieren.
Was soll das bringen? Warum nicht LTO verwenden?

von Ralf G. (ralg)


Lesenswert?

Dr. Sommer schrieb:
> Was für eine Warnung?
1
../Test.cpp:7:20: warning: ignoring packed attribute because of unpacked non-POD field 'volatile uint8_t& IOpin_t::port' [enabled by default]
2
../Test.cpp:8:20: warning: ignoring packed attribute because of unpacked non-POD field 'volatile uint8_t& IOpin_t::ddr' [enabled by default]
3
../Test.cpp:9:20: warning: ignoring packed attribute because of unpacked non-POD field 'volatile uint8_t& IOpin_t::pin' [enabled by default]

Dr. Sommer schrieb:
> Warum nicht LTO verwenden?
Hab' ich bis jetzt nur was davon gehört und weiß so ungefähr was es 
bewirken soll. Weiter bin ich noch nicht.

von TriHexagon (Gast)


Lesenswert?

Ralf G. schrieb:
> Dr. Sommer schrieb:
>> Und was ist das für ein komischer Namensstil
> Cool, was ;-)
> (Ist rein privat, einfach nicht hinsehen!)

Ist der Suffix "_t" nicht eigentlich für die C stdlib vorbehalten? Kann 
das wer bestätigen?

von Klaus W. (mfgkw)


Lesenswert?

TriHexagon schrieb:
> Ist der Suffix "_t" nicht eigentlich für die C stdlib vorbehalten? Kann
> das wer bestätigen?

Nein, warum?
In C ist es häufig usus, alle Typen mit _t zu garnieren.
Warum sollte das für die stdlib reserviert sein?

von TriHexagon (Gast)


Lesenswert?

Klaus Wachtler schrieb:
> TriHexagon schrieb:
>> Ist der Suffix "_t" nicht eigentlich für die C stdlib vorbehalten? Kann
>> das wer bestätigen?
>
> Nein, warum?
> In C ist es häufig usus, alle Typen mit _t zu garnieren.
> Warum sollte das für die stdlib reserviert sein?

Hab ich irgendwo aufgeschnappt. War mir deshalb nicht sicher. Aber danke 
für die Berichtigung.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.