Forum: Compiler & IDEs Flanken erkennung verbessern.vielleicht mit C++?


von struppi (Gast)


Lesenswert?

Guten Tag,
ich weiß nicht ob es Euch auch immer wieder so geht das ihr in Euren 
Programmen eine Flanken Erkennung braucht?
Ich benötige das immer wieder.
Im Prinzip ist dies kein Problem. Aber damit nicht jedes mal wieder das 
Rad neu erfunden wird und vor Allem damit die Lesbarkeit gegeben ist 
habe ich mir ein kleines Makro gebastelt.
1
#define RISING_AGE( DATA, BIT )            \
2
({                  \
3
  static uint8_t  mem;\
4
  uint8_t ret = 0;\
5
  if ( (DATA & (1<<BIT))&&(mem==0))\
6
  {\
7
    mem=1;\
8
    ret=1;\
9
  }\
10
  else\
11
  {\
12
    mem=0;\
13
    ret=0;\
14
  }\
15
  ret;/* return value of Macro */  \
16
})
17
#define FALLING_AGE( DATA, BIT )            \
18
({                  \
19
  static uint8_t  mem;\
20
  uint8_t ret = 0;\
21
  if ( (!(DATA & (1<<BIT)))&&(mem==0))\
22
  {\
23
    mem=1;\
24
    ret=1;\
25
  }\
26
  else\
27
  {\
28
    mem=0;\
29
    ret=0;\
30
  }\
31
  ret;/* return value of Macro */  \
32
})

Der Aufruf ist dann ganz Simple
1
if (RISING_AGE(pdo_Power[2],0))
2
    {
3
      // mach was.....
4
    }


Nun zu meiner Frage.
Könnte man so was in c++ besser machen? Wäre es da möglich einfach eine 
Funktion zu instanziieren?
Wäre das Ressourcen schonender?

Oder gibt es eine bessere Möglichkeit um die Lesbarkeit aufrecht zu 
erhalten?
Lg

von nicht"Gast" (Gast)


Lesenswert?

Ersetz mal das "Age" durch ein "Edge" :) Dann ist das zawr nicht so 
lustig, aber richtig

von Boäh (Gast)


Lesenswert?

> if (RISING_AGE(pdo_Power[2],0))

Lass mal dein Makro expandieren und schau dir den Code dann an. Versuche 
darin zu Debuggen und Fehler zu finden.

Endlich sind die Letzten von ASM runter und jetzt so was?

von struppi (Gast)


Lesenswert?

Boäh schrieb:
> Lass mal dein Makro expandieren und schau dir den Code dann an. Versuche
> darin zu Debuggen und Fehler zu finden.
>
> Endlich sind die Letzten von ASM runter und jetzt so was?

Kannst du mir das näher erklären?
Was meinst du mit expandieren?

Lg

von Boäh (Gast)


Lesenswert?

Das define wird per preprocessor in den Quelltext an der Aufrufstelle 
direkt eingefügt. Wie sieht die if BEDINGUNG dann wohl aus?

von struppi (Gast)


Lesenswert?

Die Frage war ja wie kann man es schöner machen?

von Wilhelm M. (wimalopaan)


Lesenswert?

Nur mal so als Idee ...
1
using PortC     = Port<MCU::PortRegister, C>;
2
using buttonPin = Pin<PortC, 1>;
3
using button    = ActiveLow<buttonPin, Input>;
4
using ledPin    = Pin<PortC, 1>;
5
using led       = ActiveHigh<ledPin, Output>;
6
using edgeDetector = EdgeDetector<button>;
7
8
int main() {
9
    edgeDetector::init();
10
    led::init();
11
12
    auto trigger = []{
13
        led::on();
14
    };
15
    auto release = []{
16
        led::off();
17
    };
18
   
19
    while(true) {
20
        edgeDetector::sample(trigger, release);
21
    }
22
}

P.S.: Auch in C keine Macros schreiben, die wie Funktionen aussehen ...
P.P.S: ... mach eine inline Funktion draus und vertrau dem Compiler 
(ggf. zusammen mit LTO).

von Sheeva P. (sheevaplug)


Lesenswert?

struppi schrieb:
> Die Frage war ja wie kann man es schöner machen?

Das war Deine Frage, ja. Mir persönlich wäre es aber viel wichtiger, die 
Sache nicht nur schön, sondern vor allem richtig zu machen. Deine Makros 
haben nämlich leider ein paar Designfehler, und können so nicht in jedem 
Fall korrekt funktionieren.

In C++ könnte man das etwa so machen (ungetestet):
1
class Edge {
2
private:
3
    uint8_t* port;
4
    uint8_t  bit;
5
    bool     oldstate;
6
public:
7
    Edge(uint8_t* port, uint8_t bit):
8
        port(port), bit(bit) {
9
        if( (*port & (1<<bit)) ) {
10
            oldstate = true;
11
        } else {
12
            oldstate = false;      
13
        }
14
    }
15
    bool rising(void) {
16
        bool newstate = false;
17
        // bit is set now
18
        if( (*port & (1<<bit)) ) {
19
            newstate = true;
20
        }
21
        // bit was not set, but is now 
22
        if( oldstate == false && newstate == true ) {
23
            oldstate = newstate;
24
            return true; // rising
25
        }
26
        return false; // not rising: falling or kept state
27
    }
28
    bool falling(void) {
29
        bool newstate = true;
30
        // bit is not set now
31
        if( (!(*port & (1<<bit))) ) {
32
            newstate = false;
33
        }
34
        // bit was set, but now is not
35
        if( oldstate == true && newstate == false ) {
36
            oldstate = newstate;
37
            return true; // falling
38
        }
39
        return false; // not falling: rising or kept state
40
    }
41
    bool unchanged(void) {
42
        bool newstate = false;
43
        // bit is set now
44
        if( (*port & (1<<bit)) ) {
45
            newstate = true;
46
        }
47
        if( newstate == oldstate ) {
48
            return true;
49
        }
50
        return false;
51
    }
52
};
53
54
int main(void) {
55
    Edge e(&PORTB, PB1);
56
    // ...
57
58
    if( e.rising() ) {
59
        do_something(); 
60
    }
61
    // ...
62
63
    return 0;
64
}
65
}

Edit: korrekt formatiert sieht einfach besser aus... ;-)

: Bearbeitet durch User
von struppi (Gast)


Lesenswert?

Wilhelm M. schrieb:
> mach eine inline Funktion draus und vertrau dem Compiler
> (ggf. zusammen mit LTO).

Klingt interessant. Wenn innerhalb der Funktion eine Variable static 
deklariert ist, wird dann bei öfteren Verwendung diese Variable öfter 
angelegt?

Sheeva P. schrieb:
> Edit: korrekt formatiert sieht einfach besser aus... ;-)

Danke für deine Mühe.
Was meisnst du mit korrekt formatiert?
Lg

von Sheeva P. (sheevaplug)


Lesenswert?

struppi schrieb:
> Sheeva P. schrieb:
>> Edit: korrekt formatiert sieht einfach besser aus... ;-)
>
> Danke für deine Mühe.
> Was meisnst du mit korrekt formatiert?

Daß das Forum mir beim ersten Posten meines Beitrages die Formatierung 
zerschossen hat und ich sie deswegen mit einem Edit korrigieren mußte.

von struppi (Gast)


Lesenswert?

Sheeva P. schrieb:
> struppi schrieb:
>> Sheeva P. schrieb:
>>> Edit: korrekt formatiert sieht einfach besser aus... ;-)
>>
>> Danke für deine Mühe.
>> Was meisnst du mit korrekt formatiert?
>
> Daß das Forum mir beim ersten Posten meines Beitrages die Formatierung
> zerschossen hat und ich sie deswegen mit einem Edit korrigieren mußte.

A Ok ich verstehe:)
kennst du dich mit inline aus?

von Sheeva P. (sheevaplug)


Lesenswert?

Wilhelm M. schrieb:
> P.P.S: ... mach eine inline Funktion draus und vertrau dem Compiler
> (ggf. zusammen mit LTO).

Inline-Funktionen dürfen IIRC keine nicht-konstanten statischen 
Variablen enthalten, die Variablem "mem" müßten dann also außerhalb der 
Funktionen liegen. Ich glaube, die Makros fallen da auch auf die Nase. 
;-)

von Wilhelm M. (wimalopaan)


Lesenswert?

Nein, das Problem an dem Code des TO ist, dass keine Flankenerkennung 
durchgeführt wird. Es wird der statische Zustand ermittelt. Denn es 
handelt sich um anonyme Blöcke, die statics werden also bei jedem Aufruf 
mit 0 initialisiert.

Ein
1
volatile uint8_t p = 1;
2
3
int main() {
4
    if (RISING_AGE(p, 0)) {
5
        printf("A\n");        
6
    }
7
    if (RISING_AGE(p, 0)) {
8
        printf("B\n");        
9
    }
10
}

sollte "A" und "B" ausgeben.

: Bearbeitet durch User
von Boäh (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Denn es
> handelt sich um anonyme Blöcke, die statics werden also bei jedem Aufruf
> mit 0 initialisiert.

Kann man das bitte näher erläutern.

Beitrag #5260746 wurde vom Autor gelöscht.
von Neugieriger (Gast)


Lesenswert?

Ist hier der Wettbewerb: "Wie mache ich es möglichst umständlich?"?

Sheeva P. schrieb:
1
> bool rising(void) {
2
>         bool newstate = false;
3
>         // bit is set now
4
>         if( (*port & (1<<bit)) ) {
5
>             newstate = true;
6
>         }
7
>         // bit was not set, but is now
8
>         if( oldstate == false && newstate == true ) {
9
>             oldstate = newstate;
10
>             return true; // rising
11
>         }
12
>         return false; // not rising: falling or kept state
13
>     }

Man, bei so was bekommt man ja Plaque!

Ordentlich formuliert könnte das so aussehen.
1
bool rising(void) 
2
{
3
  bool newstate = *port & (1<<bit);
4
  bool risen = newstate && !oldstate;
5
  oldstate = newstate;
6
  return risen;
7
}


Die Klasse sollte sich aber nur um Flanken kümmern, und nichts anderes:

   Single Responsibility Principle!

Ein Vorschlag:
1
class EdgeDetector 
2
{
3
public:
4
5
  EdgeDetector& update(bool actual)
6
  {
7
    previous = present;
8
    present = actual;
9
    return *this;
10
  }
11
12
  bool risen() const
13
  {
14
    return present && !previous;
15
  }
16
17
  bool fallen() const
18
  {
19
    return !present && previous;
20
  }
21
22
  bool changed() const
23
  {
24
    return present != previous;
25
  }
26
27
private:
28
  bool present, previous;
29
};

von Sheeva P. (sheevaplug)


Lesenswert?

Neugieriger schrieb:
> Die Klasse sollte sich aber nur um Flanken kümmern, und nichts anderes:
>
>   *Single Responsibility Principle!*

Schick! Trotzdem halte ich es für eine gute Idee, den Status fest an die 
Port- und Registerkombination zu binden, um damit Fehler wie diesen hier
1
ed.update( (PORTA & (1<<PA0)) );
2
ed.update( (PORTB & (1<<PB1)) );

zu vermeiden. Das ändert aber nichts daran, daß Dein Code viel hübscher 
ist als meiner. Ich sollte wieder mehr C++ coden. ;-)

von struppi (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Nein, das Problem an dem Code des TO ist, dass keine Flankenerkennung
> durchgeführt wird. Es wird der statische Zustand ermittelt. Denn es
> handelt sich um anonyme Blöcke, die statics werden also bei jedem Aufruf
> mit 0 initialisiert.

Warum funktioniert es dann?
Warum Funktioniert den dann dieser CODE?? 
https://www.mikrocontroller.net/attachment/67964/debounce.c

Ich bin ein Anfänger bitte um nähere Erklärungen was das Static und 
staic in Inline Funktionen betrifft.

Danke für Eure Bemühungen!

Lg

von Wilhelm M. (wimalopaan)


Lesenswert?

struppi schrieb:
> Wilhelm M. schrieb:
>> Nein, das Problem an dem Code des TO ist, dass keine Flankenerkennung
>> durchgeführt wird. Es wird der statische Zustand ermittelt. Denn es
>> handelt sich um anonyme Blöcke, die statics werden also bei jedem Aufruf
>> mit 0 initialisiert.
>
> Warum funktioniert es dann?

Ich habe das Macro bewusst falsch verwendet: ein Macro ist eben keine 
Funktion und sollte auch so nicht verwendet werden.

> Warum Funktioniert den dann dieser CODE??
> https://www.mikrocontroller.net/attachment/67964/debounce.c

Steht ja eigentlich in dem Kommentar des Codes drin ...

Jede neue(!) Verwendung des Macros führt zu einem neuen(!) unbenannten 
Block/Scope (hier als GCC-Erweiterung als 
Compound-Statement-Expression). Und der hat seine eigenen lokalen 
Objekte.
(Das kann Vorteile haben ... s.a. Dein zitiertes Beispiel, das für jeden 
Pin
sinnvollerweise eine eigene static-Variable angelegt wird. In meinem 
extra konstruierten Fall wird es aber falsch).

Wird das Ganze also in einer Schleife verwendet und pro Pin nur 
einmal(!) das Macro benutzt, ist es richtig.

> Ich bin ein Anfänger bitte um nähere Erklärungen was das Static und
> staic in Inline Funktionen betrifft.

Da empfehle ich ein C-Buch oder

http://en.cppreference.com/w/c/language

von struppi (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Wird das Ganze also in einer Schleife verwendet und pro Pin nur
> einmal(!) das Macro benutzt, ist es richtig.

Also bei richtiger verwendung ginge es?
Wie würde sich das verhalten wenn ich anstelle von diesem Macro den Code 
in eine Inline Funktion packen würde?
 Würde dann bei jeden Aufruf die staic deklarierte Variable "mem" neu 
verwendet?

Danke Lg

von Wilhelm M. (wimalopaan)


Lesenswert?

struppi schrieb:
> Wilhelm M. schrieb:
>> Wird das Ganze also in einer Schleife verwendet und pro Pin nur
>> einmal(!) das Macro benutzt, ist es richtig.
>
> Also bei richtiger verwendung ginge es?

Ja ;-)

> Wie würde sich das verhalten wenn ich anstelle von diesem Macro den Code
> in eine Inline Funktion packen würde?

Wie wäre es mit ausprobieren?
Wobei es in der o.g. Form nur als Funktion geschrieben wieder eine 
andere Fehlerquelle beinhaltet (s.a. Post von sheevaplug).

>  Würde dann bei jeden Aufruf die staic deklarierte Variable "mem" neu
> verwendet?

Schlage mal die Bedeutung von static nach.

von struppi (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Schlage mal die Bedeutung von static nach.

Ich weiß das static deklarierte Variablen nach der ausfürung der 
Funktion erhalten bleiben.
Was ich aber in meinem C Buch nicht finde wie das verhalten in einer 
Inline Funktion ist!
Lg
Danke Für die Geduld.

Wilhelm M. schrieb:
> Wie wäre es mit ausprobieren?

Funktioniert aber ich weiß nicht wie es sich verhaltet wenn sonderfälle 
auftreten ;)

Wilhelm M. schrieb:
> Einvolatile uint8_t p = 1;
>
> int main() {
>     if (RISING_AGE(p, 0)) {
>         printf("A\n");
>     }
>     if (RISING_AGE(p, 0)) {
>         printf("B\n");
>     }
> }

So wie du mir oben gezeigt hats
Danke
Lg

von Marc (Gast)


Lesenswert?

Sheeva P. schrieb:
> Schick! Trotzdem halte ich es für eine gute Idee, den Status fest an die
> Port- und Registerkombination zu binden, um damit Fehler wie diesen hier
> ed.update( (PORTA & (1<<PA0)) );
> ed.update( (PORTB & (1<<PB1)) );
>
> zu vermeiden.

Im Prinzip hast du recht. Aber eine andere Sache ist gefährlicher:
Häufig soll der Zustand eines anderen Signals bei einer Flanke 
verarbeitet werden. Wird das Auslesen eines Portpins in die Klasse 
gezogen wird das ganze asynchron. Jede Interruptunterbrechung macht das 
ganze inkonsistent.

Daher sollte die Klasse auch mit Daten aus einem gespiegelten Speicher 
klarkommen und nicht nur mit direkten IO Ports.

von Wilhelm M. (wimalopaan)


Lesenswert?

struppi schrieb:
> Wilhelm M. schrieb:
>> Schlage mal die Bedeutung von static nach.
>
> Ich weiß das static deklarierte Variablen nach der ausfürung der
> Funktion erhalten bleiben.
> Was ich aber in meinem C Buch nicht finde wie das verhalten in einer
> Inline Funktion ist!

Wie gesagt: hier entsteht das nä. Problem.

Du hast ja eingangs gefragt, obs mit OOP (C++) ggf. besser ginge. Also: 
wir bewegen uns durch Selbsterkenntnis dahin ;-))

Zeige mal Deinen Code soweit ... dann schauen wir.

von struppi (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Zeige mal Deinen Code soweit ... dann schauen wir.

Welchen? den meiner Inline Funktion?

Lg

von Wilhelm M. (wimalopaan)


Lesenswert?

struppi schrieb:
> Wilhelm M. schrieb:
>> Zeige mal Deinen Code soweit ... dann schauen wir.
>
> Welchen? den meiner Inline Funktion?

Ja, alles was dazu relevant ist.

von struppi (Gast)


Lesenswert?

1
C-Code
2
/* PROTOTYPS */
3
4
 static inline uint8_t steigende( uint8_t DATA,uint8_t BIT );
5
6
7
8
9
10
int main(void)
11
{
12
while (1)
13
    {
14
        if (steigende(pdo_PowerCtrlA[2],0))
15
    {
16
      emcyData[0]=0x13;
17
      emcyData[1]=0x30;
18
      emcyData[2]=0x33;
19
      emcyData[3]=0x40;
20
21
      EmcyWriteReq(0x2301,&emcyData[0]);
22
    }
23
    }
24
}
25
26
27
 static inline uint8_t steigende( uint8_t DATA,uint8_t BIT )            
28
{                  
29
  static uint8_t  mem;
30
  uint8_t ret = 0;
31
  if ( (DATA & (1<<BIT))&&(mem==0))
32
  {
33
    mem=1;
34
    ret=1;
35
  }
36
  if ( (! (DATA & (1<<BIT))&&(mem==1)) )
37
  {
38
    mem=0;
39
    ret=0;
40
  }
41
  return  ret;
42
}


so in etwa;)

von Carl D. (jcw2)


Lesenswert?

struppi schrieb:
> Wilhelm M. schrieb:
>> Schlage mal die Bedeutung von static nach.
>
> Ich weiß das static deklarierte Variablen nach der ausfürung der
> Funktion erhalten bleiben.
> Was ich aber in meinem C Buch nicht finde wie das verhalten in einer
> Inline Funktion ist!

Inline bei Funktion bedeutet nur, daß der Compiler sich den Call sparen 
soll und den Code bei jedem Aufruf direkt einfügt. Er kann das auch ohne 
inline machen, wenn er meint der Call wäre zu aufwendig. Dadurch ändern 
sich aber die anderen Eigenschaften der Funktion nicht. Die Variablen im 
"Funktions-Scope" bleiben weiterhin außerhalb unsichtbar und static legt 
genau eine Instanz einer Variablen an, die genau einmal, beim 
Programmstart initialisiert wird.
Nicht durch die (inzwischen vielen) anderen Verwendungen von inline 
verwirren lassen.

von Wilhelm M. (wimalopaan)


Lesenswert?

struppi schrieb:

>
1
>  static inline uint8_t steigende( uint8_t DATA,uint8_t BIT )
2
> {
3
>   static uint8_t  mem;
4
>   uint8_t ret = 0;
5
>   if ( (DATA & (1<<BIT))&&(mem==0))
6
>   {
7
>     mem=1;
8
>     ret=1;
9
>   }
10
>   if ( (! (DATA & (1<<BIT))&&(mem==1)) )
11
>   {
12
>     mem=0;
13
>     ret=0;
14
>   }
15
>   return  ret;
16
> }
17
>
>
>
> so in etwa;)

Hieran sieht man, dass es (meistens) ungut ist, Funktionen mit internen 
Zuständen (also keine Funktionen mehr im engeren Sinn) zu schreiben. 
Denn wendest Du Deine Funktionen erneut an auf einen anderen Pin / 
Objekt
1
if (steigende(pdo_PowerCtrlA[2],7)) {
2
...
3
}

wirds wieder falsch. Denn Du / der Programmierer muss sich nun merken, 
dass der interne Zustand der Funktion für Bit 0 in pcd_PowerCtrl[2] gilt 
und sonst für nichts anderes.

Folglich wäre es besser:
1
struct Flag {
2
    uint8_t value;
3
};
4
5
typedef struct Flag flag_t;
6
7
inline bool rising_edge(volatile uint8_t* data, uint8_t bit, flag_t* mem) {
8
    uint8_t ret = 0;
9
    if ( (*data & (1<<bit))&&(mem->value == 0)) {
10
        mem->value = 1;
11
        ret=1;
12
    }
13
    else {
14
        mem->value = 0;
15
        ret=0;
16
    }
17
    return ret;
18
}

was nichts anderes bedeutet, als dass wir den internen Zustand 
externalisiert haben. Damit haben wir eine Funktion, die auf ein Objekt 
vom Type flag_t angewendet wird. Das kann man objektbasierte 
Programmierung nennen.

Nun ist muss der Programmierer explizit sagen, welche Flags benutzt 
werden sollen, und macht hoffentlich keinen Fehler (aber optimal ist das 
ja noch nicht!).

Jetzt kannst Du es leicht nach OOP (C++) umbauen.

Die nächste Stufe wäre nun, dass man die Flags ausschließlich einem 
bestimmten Pin zur Flankenüberwachung zuordnen kann (das kann man jetzt 
noch falsch machen). Das könnte man mit dem Ansatz ganz oben von mir 
sicherstellen ...

von Wilhelm M. (wimalopaan)


Lesenswert?

Carl D. schrieb:
> struppi schrieb:
>> Wilhelm M. schrieb:
>>> Schlage mal die Bedeutung von static nach.
>>
>> Ich weiß das static deklarierte Variablen nach der ausfürung der
>> Funktion erhalten bleiben.
>> Was ich aber in meinem C Buch nicht finde wie das verhalten in einer
>> Inline Funktion ist!
>
> Inline bei Funktion bedeutet nur, daß der Compiler sich den Call sparen
> soll und den Code bei jedem Aufruf direkt einfügt.

Jaein, "soll" ist hier aber kein "muss". Das darf er aber auch sonst 
nach der as-if-rule.

inline ist hauptsächlich dazu da, die ODR nicht zu verletzten.

von struppi (Gast)


Lesenswert?

Wilhelm M. schrieb:
> was nichts anderes bedeutet, als dass wir den internen Zustand
> externalisiert haben. Damit haben wir eine Funktion, die auf ein Objekt
> vom Type flag_t angewendet wird. Das kann man objektbasierte
> Programmierung nennen.
>
> Nun ist muss der Programmierer explizit sagen, welche Flags benutzt
> werden sollen, und macht hoffentlich keinen Fehler (aber optimal ist das
> ja noch nicht!).
>
> Jetzt kannst Du es leicht nach OOP (C++) umbauen.
>
> Die nächste Stufe wäre nun, dass man die Flags ausschließlich einem
> bestimmten Pin zur Flankenüberwachung zuordnen kann (das kann man jetzt
> noch falsch machen). Das könnte man mit dem Ansatz ganz oben von mir
> sicherstellen ...

Ok ich verstehe! Also gibt es keine Möglichkeit in c eine Instanz einer 
Funktion zu deklarieren?!

Macht C++ also OOP auf einem 8 Bit Maschine Sinn? Ich habe hier einen 
At90Can128 im Einsatz.

Im Prinzip ist die Macro Methode wenn nur einmal mit selber Variable 
verwendet am leichtesten anzuwenden, Oder?

Lg
Ps Danke für die Fachliche Kompetenz!

von Wilhelm M. (wimalopaan)


Lesenswert?

struppi schrieb:
> Wilhelm M. schrieb:
>> was nichts anderes bedeutet, als dass wir den internen Zustand
>> externalisiert haben. Damit haben wir eine Funktion, die auf ein Objekt
>> vom Type flag_t angewendet wird. Das kann man objektbasierte
>> Programmierung nennen.
>>
>> Nun ist muss der Programmierer explizit sagen, welche Flags benutzt
>> werden sollen, und macht hoffentlich keinen Fehler (aber optimal ist das
>> ja noch nicht!).
>>
>> Jetzt kannst Du es leicht nach OOP (C++) umbauen.
>>
>> Die nächste Stufe wäre nun, dass man die Flags ausschließlich einem
>> bestimmten Pin zur Flankenüberwachung zuordnen kann (das kann man jetzt
>> noch falsch machen). Das könnte man mit dem Ansatz ganz oben von mir
>> sicherstellen ...
>
> Ok ich verstehe! Also gibt es keine Möglichkeit in c eine Instanz einer
> Funktion zu deklarieren?!

Was meinst Du damit? Ein Funktionsobjekt? Ein Closure?

> Macht C++ also OOP auf einem 8 Bit Maschine Sinn? Ich habe hier einen
> At90Can128 im Einsatz.

Nach meiner Meinung: absolut! Allerdings muss sollte man sich im 
wesentlichen auf statische Polymorphie beschränken, TMP benutzen und 
auch Exceptions aussen vor lassen (gehen eh nicht beim avr-g++).

Generell kann man das gleiche machen wie in C und noch sehr, sehr viel 
mehr. Was m.E. zu besserem (in vielerlei Hinsicht) Code führt (teilweise 
sogar schneller).

Aber es werden jetzt gleich ganz viele Stimmen kommen, dass C++ Quatsch 
ist ;-))

> Im Prinzip ist die Macro Methode wenn nur einmal mit selber Variable
> verwendet am leichtesten anzuwenden, Oder?

Wie gesagt: wenn man keinen Fehler macht, ist so ziemlich alles ok.

M.E: geht es aber immer um ein ganz wesentliches Grundprinzip: Eine 
Schnittstelle sollte einfach richtig und schwer falsch zu benutzen sein. 
Und das erfüllt die Macro Methode (auch noch aus anderen Gründen) nicht.

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Denn wendest Du Deine Funktionen erneut an auf einen anderen Pin /
> Objekt
> ...
> wirds wieder falsch.

Man kann es auch anders herum sehen:

Wenn man mit dem Makro für jeden abzufragenden Pin jeweils genau eine 
Stelle im Source wählt, dann funktioniert alles wie gewünscht, denn dann 
gibt es für jeden abzufragenden Pin gibt es genau einen "Speicher" mem.

Die Externalisierung ist dann nicht notwendig. Und so wird das Makro 
zumindest einfacher anzuwenden als jede (inline-)Funktion.

struppi schrieb:
> Im Prinzip ist die Macro Methode wenn nur einmal mit selber Variable
> verwendet am leichtesten anzuwenden,

Ja.

Ja, ich weiß: Makro-Hassern dreht sich da der Magen um. Aber jeder 
Versuch, das Makro durch Alternativlösungen zu schlagen, wird auf jeden 
Fall komplizierter - auch wenn der Compiler es schaffen sollte, trotz 
"Externalisierung" denselben Code wie beim Makro zu erzeugen. Der 
Binärcode wird auf jeden Fall nicht besser oder performanter.

von struppi (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Was meinst Du damit? Ein Funktionsobjekt? Ein Closure?

Ich meine einfach eine Art um die Funktion zwei drei ....X mal zu 
verwenden und das die variablen die stisch sein müssen einfach neu 
angelegt werden im Hintergrund ohne das ich einen Fehler machen kann.

Wilhelm M. schrieb:
> TMP benutzen

Was ist das?

Gibt es ein Beispiel wie das Atmel Studio7 eingestellt werden muß damit 
ich c++ verwenden kann?


Danke für deine guten Erklärungen!!!!

von Wilhelm M. (wimalopaan)


Lesenswert?

Frank M. schrieb:

> Wenn man mit dem Makro für jeden abzufragenden Pin jeweils genau eine
> Stelle im Source wählt, dann funktioniert alles wie gewünscht, denn dann
> gibt es für jeden abzufragenden Pin gibt es genau einen "Speicher" mem.

Wie gesagt: wenn man es richtig macht, ist ja alles ok. (Oben hatte ich 
zur Erläuterung ein Beispiel gebracht, wie es leicht falsch wird).

> Die Externalisierung ist dann nicht notwendig. Und so wird das Makro
> zumindest einfacher anzuwenden als jede (inline-)Funktion.

Mehr ist bei reinem C m.E. nicht möglich.

> struppi schrieb:
>> Im Prinzip ist die Macro Methode wenn nur einmal mit selber Variable
>> verwendet am leichtesten anzuwenden,
>
> Ja.
>
> Ja, ich weiß: Makro-Hassern dreht sich da der Magen um. Aber jeder
> Versuch, das Makro durch Alternativlösungen zu schlagen, wird auf jeden
> Fall komplizierter - auch wenn der Compiler es schaffen sollte, trotz
> "Externalisierung" denselben Code wie beim Makro zu erzeugen.

Das sollte er schaffen ;-)

> Der
> Binärcode wird auf jeden Fall nicht besser oder performanter.

Ich denke, er ist identisch.

Allerdings: wenn es die Anwendung zulässt, dass man die Flags als lokale 
Variable (etwa in main) repräsentiert, wird der Ansatz mit 
Externalisierung effizienter.

von Peter D. (peda)


Lesenswert?

Ich mache die Flankenerkennung immer zusammen mit der Entprellung und 
parallel für einen kompletten IO-Port. Das bischen Code wird in einen 
Timerinterrupt mit eingefügt. Das Main muß dann nur noch das Ereignis 
abholen.

von Wilhelm M. (wimalopaan)


Lesenswert?

struppi schrieb:
> Wilhelm M. schrieb:
>> Was meinst Du damit? Ein Funktionsobjekt? Ein Closure?
>
> Ich meine einfach eine Art um die Funktion zwei drei ....X mal zu
> verwenden und das die variablen die stisch sein müssen einfach neu
> angelegt werden im Hintergrund ohne das ich einen Fehler machen kann.

Dann wären wir in C++ bei einem Funktor angelangt.

> Wilhelm M. schrieb:
>> TMP benutzen
>
> Was ist das?

Template-Meta-Programmierung.

> Gibt es ein Beispiel wie das Atmel Studio7 eingestellt werden muß damit
> ich c++ verwenden kann?

Da kann ich leider nicht weiterhelfen, das benutze ich nicht.

von struppi (Gast)


Lesenswert?

Peter D. schrieb:
> Ich mache die Flankenerkennung immer zusammen mit der Entprellung und
> parallel für einen kompletten IO-Port. Das bischen Code wird in einen
> Timerinterrupt mit eingefügt. Das Main muß dann nur noch das Ereignis
> abholen.

Ich kenne deine ausgezeichnete Entprellung.
Ich benötige aber oft eine Flankenerkennung bei Variablen die über zB 
Can rein kommen.
Deine Variante erkennt die fallende Flanke ich benötige aber beide.
Außerdem müsste ich deine Entprellung ausbohren und und mittels 
Hilfsvariable zB 16 bit zusammen bauen.

Deshalb finde ich so eine Funktion oder Makro besser und wartbarer.

Wilhelm M. schrieb:
> Da kann ich leider nicht weiterhelfen, das benutze ich nicht.

Kein Problem hast mir ja sonst viel bei gebracht;)

Lg

von Carl D. (jcw2)


Lesenswert?

struppi schrieb:
> Peter D. schrieb:
>> Ich mache die Flankenerkennung immer zusammen mit der Entprellung und
>> parallel für einen kompletten IO-Port. Das bischen Code wird in einen
>> Timerinterrupt mit eingefügt. Das Main muß dann nur noch das Ereignis
>> abholen.
>
> Ich kenne deine ausgezeichnete Entprellung.
> Ich benötige aber oft eine Flankenerkennung bei Variablen die über zB
> Can rein kommen.
> Deine Variante erkennt die fallende Flanke ich benötige aber beide.
> Außerdem müsste ich deine Entprellung ausbohren und und mittels
> Hilfsvariable zB 16 bit zusammen bauen.

Peters Flankenerkennung merkt sich diese auch bis sie jemand abfragt, 
wärend oben eine C++-Version zu sehen war, die die erkannten Flanken 
schon beim nächsten Takt wieder vergessen hat. Nur "alt XOR neu" ist 
eher ein Hochpassfilter.

von Sheeva P. (sheevaplug)


Lesenswert?

Marc schrieb:
> Sheeva P. schrieb:
>> Schick! Trotzdem halte ich es für eine gute Idee, den Status fest an die
>> Port- und Registerkombination zu binden, um damit Fehler wie diesen hier
>> ed.update( (PORTA & (1<<PA0)) );
>> ed.update( (PORTB & (1<<PB1)) );
>> zu vermeiden.
>
> Im Prinzip hast du recht. Aber eine andere Sache ist gefährlicher:
> Häufig soll der Zustand eines anderen Signals bei einer Flanke
> verarbeitet werden. Wird das Auslesen eines Portpins in die Klasse
> gezogen wird das ganze asynchron. Jede Interruptunterbrechung macht das
> ganze inkonsistent.
>
> Daher sollte die Klasse auch mit Daten aus einem gespiegelten Speicher
> klarkommen und nicht nur mit direkten IO Ports.
1
#include <functional>
2
3
typedef std::function<bool(void)> PinReader;
4
class EdgeDetector {
5
public:
6
    EdgeDetector(PinReader updater_): updater(updater_) {
7
        oldstate = updater();
8
    }
9
    
10
    bool hasRisen(void) { return rise; }
11
    
12
    bool hasFallen(void) { return fall; }
13
14
    bool hasChanged(void) { return rise or fall; }
15
    
16
    EdgeDetector& update(void) {
17
        bool newstate = updater();
18
        rise = newstate and !oldstate;
19
        fall = !newstate and oldstate;
20
        oldstate = newstate;
21
        return *this;
22
    }
23
    // nifty
24
    EdgeDetector& operator()(void) { return update(); }
25
protected:
26
    PinReader updater;
27
private:
28
    bool oldstate;
29
    bool rise;
30
    bool fall;
31
};
32
33
34
35
// usage:
36
37
EdgeDetector kasper{
38
    [](void)->bool{
39
        return (PORTA & (1<<PA0));
40
    }
41
};
42
43
// ...
44
45
    ATOMIC_BLOCK( ATOMIC_FORCEON ) {
46
        kasper.update();
47
        if( kasper.hasRisen() ) {
48
            do_something();
49
        }
50
    }
51
52
53
// ... oder ... 
54
55
    ATOMIC_BLOCK( ATOMIC_FORCEON ) {
56
        if( kasper().hasRisen() ) {
57
            do_something();
58
        }
59
    }

Wer kein std::functional hat, kann natürlich auch Funktionszeiger 
benutzen.

von tictactoe (Gast)


Lesenswert?

Sheeva P. schrieb:
> Wer kein std::functional hat, kann natürlich auch Funktionszeiger
> benutzen.

Lieber nicht. Auf einem Microcontroller hat man ja nicht unvorhersehbar 
viele Edge-Inputs, sondern nur genau so viele wie die angeschlossene 
Hardware hergibt. Deshalb ist es viel besser, die Klasse gleich als 
Template zu gestalten, wobei der updater ein Template-Parameter ist. Ich 
wette, dass in typischen Anwendungsfällen der Code am Ende kleiner ist, 
selbst wenn man, sagen wir, 5 EdgeDetectoren instanziieren muss.

BTW, ich würde außerdem versuchen, die 3 bool Variablen in ein Byte zu 
packen:
1
    bool oldstate : 1;
2
    bool rise : 1;
3
    bool fall : 1;
Wobei ich zugebe, dass ich in meinem Leben noch nie bools in ein 
Bitfield gequetscht habe.

von Wilhelm M. (wimalopaan)


Lesenswert?

Sheeva P. schrieb:

> Wer kein std::functional hat, kann natürlich auch Funktionszeiger
> benutzen.

Evtl. ist die Einschränkung auf Callables (void) -> bool etwas zu 
einschränkend. In solchen fällen kann man sehr gut class template 
argument deduction (ggf. mit einem deduction guide) verwenden:
1
template<typename Updater>
2
class EdgeDetector {
3
public:
4
    EdgeDetector(Updater f) : mF{f}{}
5
    template<typename A = void>
6
    void update(const A& arg) {
7
        mF(arg);
8
    }
9
    void update() {
10
        mF();
11
    }
12
private:    
13
    Updater mF;
14
};
Man sollte in meinem Code oben noch die Überladung durch SFINAE 
ersetzen...

von Wilhelm M. (wimalopaan)


Lesenswert?

tictactoe schrieb:

> BTW, ich würde außerdem versuchen, die 3 bool Variablen in ein Byte zu
> packen:
>
1
>     bool oldstate : 1;
2
>     bool rise : 1;
3
>     bool fall : 1;
4
>
> Wobei ich zugebe, dass ich in meinem Leben noch nie bools in ein
> Bitfield gequetscht habe.

Kann man aber gut machen. Gibt halt mehr Flash (Assembler Code), weniger 
RAM.

Mittlerweile habe ich mir angewöhnt, so etwas auch generisch zu lösen. 
Dann kann ich wählen per TemplateParameter, ob einzelne bools, ein 
BitField, oder ggf. auch ein GPIOR (bspw. bei AVR sehr effizient wenn 
sbi/cbi fähig) benutzt wird.

von Sheeva P. (sheevaplug)


Lesenswert?

tictactoe schrieb:
> Auf einem Microcontroller hat man ja nicht unvorhersehbar
> viele Edge-Inputs, sondern nur genau so viele wie die angeschlossene
> Hardware hergibt. Deshalb ist es viel besser, die Klasse gleich als
> Template zu gestalten, wobei der updater ein Template-Parameter ist. Ich
> wette, dass in typischen Anwendungsfällen der Code am Ende kleiner ist,
> selbst wenn man, sagen wir, 5 EdgeDetectoren instanziieren muss.

Das käme auf einen Versuch an.

> BTW, ich würde außerdem versuchen, die 3 bool Variablen in ein Byte zu
> packen:

Da hatte ich auch schon 'dran gedacht und wollte es in meinem Posting 
eigentlich auch erwähnt haben, aber...

von Sheeva P. (sheevaplug)


Lesenswert?

Wilhelm M. schrieb:
> Evtl. ist die Einschränkung auf Callables (void) -> bool etwas zu
> einschränkend.

Ich hatte darüber nachgedacht, es dann aber ganz absichtlich so gemacht. 
Der Status kann in diesem Fall ja eh nur ein Bool sein, und deswegen ist 
es IMHO korrekt, den Rückgabewert fest darauf zu beschränken. Und weil 
ich das Ganze festnageln und gerade nicht von externen Parametern 
abhängig machen, sondern im Gegenteil unbeabsichtigte Fehler verhindern 
will, soll diese Funktion gar keine Parameter annehmen können. Wer das 
trotzdem unbedingt haben will, kann das ja wahlweise als Template oder 
über Vererbung realisieren.

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.