einfache und übersichtliche Flanken erkennung

Gast #5908507
Lesenswert?

Guten Abend,
ich bin auf der Suche nach einer einfachen und übersichtlichen Erkennung 
von Flanken in C.
Ich möchte Flanken erkennen die nicht schneller als meine Main Loop.
Mir geht es dabei einfach um eine übersichtliche Art auf ein Ereignis 
nur beim Flankenwechsel zu reagieren.

Ein Beispiel wäre das ein Fehlerbit erkannt wird und einmal eine 
Debugausgabe gesendet wird.

Habt ihr hier zu eine Funktion die man sich für solche Zwecke 
referenzieren kann?

Danke für die Hilfe
Gast #5908515
Lesenswert?

Eine Funktion, um eine simple Zustands-Variable zu beschreiben?

Also wenn das einfach sein soll, dann würde ich mal sehr gerne sehen, 
wie du das bisher programmiert hast.
1
uint8_t errorAlreadyProcessed=0;
2

3
int main()
4
{
5
    ...
6
   for (;;)  // main loop
7
   {
8
       ...
9
       if (error==1)
10
       {
11
          if (errorAlreadyProcessed==0)
12
          {
13
             errorAlreadyProcessed=1;
14
             processError();
15
          }
16
       }
17
   }

Wie soll man das mit einer zusätzlichen Funktion verschönern? Kann ich 
mir nicht vorstellen.
Beitrag #5908613 wurde von einem Moderator gelöscht.
Gast #5909725
Lesenswert?

Peter D. schrieb:
> Anbei mal ein einfaches Macro zur Flankenerkennung.

Danke für das Makro:)
 Ich kann es im Moment nnicht testen da ich keine Hardware da habe.
Verstehe ich es richtig das man so Zb das Makro verwenden könnte?

if (get_edge(PINB & (1<<0))==RISE)
  {
    uputs_("PINB0 RISE\n\r");
  }

Danke für die Hilfe
Gast #5909820
Lesenswert?

gedenk schrieb:
> Das Problem ist das man sich Händisch immer eine Variable merken
> anlegen
> muss uns sich den Zustand merken muss.

Das ist nicht das Problem sondern die Lösung!

Ist der neue Wert anders als der alte bedingt nunmal das der Rechner 
beide vergleichen kann.
Gast #5910496
Lesenswert?

NichtWichtig schrieb:
> Das ist nicht das Problem sondern die Lösung!
>
> Ist der neue Wert anders als der alte bedingt nunmal das der Rechner
> beide vergleichen kann.

Ist mir schon bewusst;)
Ich war nur auf der Suche nach einem System wo ich nicht Händisch diese 
Variable anlegen muss.

Peter D. schrieb:
> Anbei mal ein einfaches Macro zur Flankenerkennung.

Dies gefällt mir vom Prinzip her gut muss es jetzt testen.
Gast #5910975
Lesenswert?

Ihr nehmt blos die flasche Sprache.
1
if rising_edge(clk) then  
2
   if (count = 24) then  
3
      count <= 0;  
4
      clk_temp<= not clk_temp;  
5
   else  
6
      count <= count +1;  
7
   end if;  
8
end if;

So einfach kann das Leben sein!
Gast #5911126
Lesenswert?

Danke Euch.
Aber viele verstehen meine Frage falsch.
Ich weiß das man zur Flanken erkennung den vorherigen Zustand wissen 
muss.
Mir hat legendlich die Idee gefehlt wie man eine Flankenerkennung als 
eine Art "Funktion" bzw Makro macht um nicht für jede Flanke die man 
auswerten will jedes mal händisch eine Variable anlegen muss.

Die Lösung von Peter D.
Erfüllt genau meine Anforderung.
Die Merker Variable wird für jede Verwendung deklariert.

Und nein ich will keine Entprellung. Ich möchte nur eine 
Flankenerkennung.

Danke schönen Tag
#5911144
Lesenswert?

Arduino Fanboy D. schrieb:
> Ansonsten kann ich dir nur eine bequeme C++ Lösung anbieten.

Ich habs zwar nicht probiert, aber der AVR-GCC wird mir bestimmt 1000 
Fehlermeldungen vor den Latz knallen.

Wie heißen denn die dafür nötigen Libs und wo kann man sie downloaden?

Es fällt mir doch sehr oft auf, das C++ Codebeispiele out of the box 
erstmal nicht lauffähig sind und daher gerade für Anfänger keine Hilfe.
#5911263
Lesenswert?

Hi
C ist nicht meine Sprache und trotzdem mal einen Tip zur 
Flankenerkennung. Angenommen, du hast die Variablen "Alt" und "Neu" 
sowie "Temp", "Fl_to_1" und "Fl_To_0".
Zuerest eine Exclusiv-Oder "Alt" mit "Neu" und das Ergebnis nach "Temp".
Nun mit "Temp" und "Alt" eine Und-Verknüpfng und du hast den Wechsel von 
"1" nach "0". Dann eine Und-Verknüpfung von "Temp" und "Neu" und du hast 
den Wechsel von "0" nach "1". Anschließend kopierst du "neu" nach "Alt". 
Die Bits in den Variablen "Fl_to_1" sowie "Fl_to_0" kannst du jetzt 
abfragen, die Bearbeitung ausführen und diese Bits löschen. Erst bei 
einem erneuten Wechsel in der Variable "Neu" werden sie wieder in der 
Flanke erkannt und neu gesetzt. In Assembler etwa 10 Anweisungen, wobei 
hier immer von Bytes und nicht von Bits die Rede ist.
1
Flanken:  ; In "Alt" und "Neu", Out "Fl_to_1" und "Fl_to_0"
2
  LDS  r16, alt
3
  LDS  r17, neu
4
  EOR  r16,r17
5
  Mov  temp, r16  ;temp = beliebiges Register
6
  AND  r16, r17      ; 
7
  STS  Fl_to_1, r16  ; Ergebnis Flankenbits "0" nach "1"
8
  LDS  r16, alt
9
  AND  r16, temp
10
  STS  Fl_to_1, r16  ; Ergebnis Flankenbits "1" nach "0"
11
        STS     alt, r17
12
RET

Ich denk, in C ist das auch nicht viel schwieriger.
Gruß oldmax
Gast #5911283
Lesenswert?

Statt Makro-Magie könnte man sich auch einfach eine ganz normale 
Funktion schreiben. Das mag altmodisch wirken, aber C ist ja auch eine 
altmodische Programmiersprache - passt also.
1
#include <stdbool.h>
2
#include <stdint.h>
3
#include <stdio.h>
4

5
bool zustand_Schalter1=0;
6

7
/**
8
 * Erkennt Flankenwechsel bei einem Signal.
9
 * @param signal Eingangssignal
10
 * @param zustandsVariable Zeiger auf eine Variable, die den vorherigen Zustand speichert.
11
 * @return 1 bei stegender Flanke, -1 bei fallender Flanke, 0=keiner Änderung
12
 */
13
int8_t flanke(bool signal, bool* zustandsVariable)
14
{
15
    if (signal != *zustandsVariable)
16
    {
17
        *zustandsVariable=signal;
18
        if (signal)
19
           return 1;
20
        else
21
           return -1;
22
    }
23
    return 0;
24
}
25

26
void test_fuer_schalter1(bool signal)
27
{
28
    int8_t f=flanke(signal, &zustand_Schalter1);
29
    switch (f)
30
    {        
31
        case +1: printf("steigende Flanke"); break;
32
        case -1: printf("fallende Flanke"); break;
33
        default: printf("keine Flanke");
34
    }
35
}
36

37
int main()
38
{
39
    test_fuer_schalter1(0);
40
    test_fuer_schalter1(1);
41
    test_fuer_schalter1(1);
42
    test_fuer_schalter1(0);
43
    test_fuer_schalter1(0);
44
}

In Kombination mit einem I/O Pin könnte man das so verwenden:
1
#include <stdbool.h>
2
#include <stdint.h>
3
#include <stdio.h>
4
#include <util/delay.h>
5
#include <avr/io.h>
6

7
// Hier noch die Funktion "flanke" rein kopieren oder inkludieren.
8

9
bool zustand_taster=0;
10
int zaehler=0;
11

12
int main()
13
{
14
    for(;;) // forever loop
15
    {
16
        uint8_t signal=PINB & (1<<PB0);
17
        if (flanke(signal, &zustand_taster) == 1)
18
        {
19
           zaehler++;
20
           printf("Zaehlerstand: %d", zaehler);
21

22
           // entprellen
23
           _delay_ms(10);
24
        }
25
    }
26
}
Gast #5911310
Lesenswert?

gedenk schrieb:
> Bei vielen Spsen gibt es RTrig Ftrig das sind Funktionen die intern den
> Zustand speichern aber von extern kommt nur das zu Triggernde Signal
> rein.

Man könnte auch einfach das Interrupt Flag eines zB AVR in der main 
abfragen (und löschen). Das speichert entsprechend der Konfiguration das 
Triggerereignis.
Gast #5911332
Lesenswert?

gedenk schrieb:
> ich bin auf der Suche nach einer einfachen und übersichtlichen Erkennung
> von Flanken in C.

du kannst keine Flanken erkennen, du kannst nur logische Zustände 
vergleichen. Daraus kann man sich eine Pseudo- Flankenerkennung basteln 
indem man eine Zustandsänderung sucht.
1
wenn (PortWert != alterWert) Aenderungsflag = 1;
2
alterWert= PortWert;
Damit findet man aber nur die erste Änderung
1
wenn (PortWert != alterWert) Aenderungszaehler++;
2
alterWert= PortWert;
Bildet die Anzahl der Änderungen


Es kann sinnvoll sein einen Interrupt zu verwenden, der erkennt aber 
Änderungen meist nur in eine Richtung.

Viele Controller haben Onchange Register die eine Änderung der Ports 
seit der letzten Abfrage anzeigen. Das hat aber nichts mit C zu tun und 
ist "nicht portabel".
#5911387
Lesenswert?

Apollo M. schrieb:
> das macro sollte hier mit "__LINE__" verbessert werden, damit
> automatisch immer eine neue variable angelegt wird!

Wozu soll das gut sein?
Jede static Variable hat nur innerhalb des Blocks Gültigkeit und bleibt 
bis zum nächsten Eintritt in den Block erhalten.
Der Linker ist daher verpflichtet, bei jeder Macroexpansion eine neue 
Adresse zu vergeben und er macht das auch.
1
char foo(char flag)
2
{
3
  76:  90 91 61 00   lds  r25, 0x0061
4
// ...
5
char faa(char flag)
6
{
7
  9e:  90 91 60 00   lds  r25, 0x0060
Moderator Persönliche Seite #5911564
Lesenswert?

Apollo M. schrieb:
> das macro sollte hier mit "__LINE__" verbessert werden, damit
> automatisch immer eine neue variable angelegt wird!

Wie Peter dargelegt hat, ist das nicht notwendig. Du kannst ohne 
Probleme beliebig viele gleichnamige (static-)Variablen innerhalb einer 
Funktion anlegen und auch getrennt voneinander verwenden, solange sich 
nicht der Scope überlappt.
Gast #5911634
Lesenswert?

Peter D. schrieb:
> Private Libs würde ich immer so kenntlich machen:

Die Libs sind dafür vorgesehen in einen der Arduino Library Ordner 
geworfen zu werden.
Sie sind dann also für alle Projekte zu sehen.


#include "abc.h"
Nutze ich für Dateien, welche nur in diesem Projekt sichtbar sind.
Im Projekt Ordner liegen.


#include <abc.h>
Um Libs aus den "öffentlichen" libraries zu verwenden. Wo man die Combie 
Libs auch hinein werfen sollte.

Eine dritte Möglichkeit der Unterscheidung bietet sich nicht.
(Firma: @home) #5911935
Lesenswert?

Peter D. schrieb:
> Wozu soll das gut sein?
> Jede static Variable hat nur innerhalb des Blocks Gültigkeit und bleibt
> bis zum nächsten Eintritt in den Block erhalten.
> Der Linker ist daher verpflichtet, bei jeder Macroexpansion eine neue
> Adresse zu vergeben und er macht das auch.

korrekt, die blockklammerung habe ich übersehen. also in die tonne mit 
meiner anmerkung!


mt
#5912238
Lesenswert?

Stefanus F. schrieb:
> Statt Makro-Magie könnte man sich auch einfach eine ganz normale
> Funktion schreiben.

An dem Macro ist überhaupt nichts magisches. Der Code ist 
straightforward und deutlich einfacher zu verstehen, als das C++ 
Beispiel.
Nicht jedem bekannt ist vielleicht, daß man Macros auch lesbar schreiben 
darf, indem man sie mit dem '\' Zeichen auf mehrere Zeilen umbricht.
Der eigentliche Trick des Macros ist aber, daß es ein Block öffnet und 
darin eine Variable anlegt. Somit wird bei jeder Verwendung automatisch 
eine neue Variable erzeugt.
Als Magie würde ich das nicht bezeichnen.

Natürlich grassiert oft das Vorurteil, Macros sind ja ach so pöse. Dabei 
wird aber verkannt, daß selbst die standard Includes schon sehr viele 
Macros beinhalten.
Gast #5912452
Lesenswert?

Ich will das Makro nicht schlecht reden, sondern nur eine Alternative 
aufzeigen.

Ich mag Makros nicht mehr so sehr, nachdem ich mich mit zu vielen 
Verschachtelungen selbst verzettelt habe und andere Nutzer durch meine 
Quelltexte nicht durchgeblickt haben. Mit Bedacht eingesetzt sind sie 
sicher nicht grundsätzlich verkehrt.
Moderator Persönliche Seite #5912483
Lesenswert?

Stefanus F. schrieb:
> Ich will das Makro nicht schlecht reden, sondern nur eine Alternative
> aufzeigen.

Deine Alternative ist aber nur eine Alternative, wenn es nur einen Pin 
auszuwerten gilt. Peters Makro funktioniert aber auch für 2, 3, 4 Pins. 
Hier wird der Code n-fach durch den Preprocessor einfach dupliziert - 
nicht schön, aber wirksam.

Die allgemeinere Lösung über eine Funktion wäre daher wesentlich 
interessanter. Der Spezialfall N=1 ist dabei eher uninteressant. Da kann 
ich (fast) Peters Makro einfach in eine leere Funktion kopieren und 
fertig.
Gast #5912520
Lesenswert?

Frank M. schrieb:
> Die allgemeinere Lösung über eine Funktion wäre daher wesentlich
> interessanter.

Ich betrachte den Flankenerkenner als endlichen Automaten.
Vielleicht die primitivste Form dessen, aber ein Automat.

Das bedingt, dass man irgendwo einen Zustand halten muss.

Hält man den Zustand in der Funktion (static), schränkt das die 
Mehrfachverwendung ein. Wie bei den Makros führt das recht zwingend zu 
Codeduplikaten, für jeden "Kanal" welcher überwacht werden möchte.


Die für "mich" sinnvollste Lösung wäre in C eine Variable außerhalb, für 
den Zustand. z.B. Übergabe eines Zeigers auf den Zustand.

In C++ hält man den Zustand als Instanz Eigenschaft. Dürfe die übliche 
Vorgehensweise sein.


----
Im Grunde sind das die drei Möglichkeiten, welche einem die µC Welt 
bietet.
Mehr sehe ich nicht.

Geschickt gebaut, kann man so auch z.B. 8 Kanäle gleichzeitig in einem 
Byte verwalten.

Natürlich kann man das auch in ASM oder einer anderen Sprache erledigen, 
ändert aber nichts an den Notwendigkeiten.
Gast #6023162
Lesenswert?

Mein Vorschlag:
///////////////////////////////////////////////////////
1
#include<TimerOne.h>
2
//Entprellen und bei positiver Flanke wechset die LED
3
//an Pin 13.
4

5
// Hardware:
6
// Taster an Pin 8 und GND anschließen. (oder ein Stück Draht)
7

8
//Funktion 
9
//1. Falls Taster gedrückt => Zähler (z)wird erhöht
10
//   Falls Taster offen -> Zähler (z)wird vermindert
11
//2.Der Wertebereich von z wird begrenzt. (mit constrain)
12
//3. Umschaltpunkte werden für den Schalter festgelegt.
13
//Hat der Zähler mehr als 80, wird der Schalte swi auf
14
//eins gelegt.Bei einem Zählerstand von weniger als 20,
15
//hat der Schalter den Wert 0. (Hysterese)
16
//4. Ist der alte Zählerstand swi = 0 und der neue 1,
17
//ist eine pos. Flanke vorhanden. 
18
//5. Bei einer pos.Flanke wird die LED an Pin 13 
19
//umgeschaltet (toggel).
20
//6.Damit beim nächsten Schleifendurchlauf der alte
21
//Schalterzustand zur Verfügung steht, wird der aktuell 
22
//Zählerstand auf swialt gespeichert
23

24
int swi, swialt; 
25
int z;                     
26
int gedruekt;
27
int i;
28
void my_isr(void)   //Interrupt Service 
29
{ if ( digitalRead(8))
30
    z++;
31
  else
32
    z--;
33

34
  z = constrain(z, 0, 100);
35

36
  if (z > 80)
37
    swi = 1;
38

39
  if (z < 20)
40
    swi = 0;
41

42
  //Serial.println(swi);
43

44
  if ( swi == 1  && swialt == 0)
45
   {
46
    i++;
47
    digitalWrite(13, i&1);
48
    
49
   }
50
Serial.println(swi);
51

52
  swialt = swi;   //Rueckspeichern
53
 // delay(1);
54
}
55
  
56
 
57

58
void setup()
59
{ pinMode(13, OUTPUT);
60
  pinMode(8, INPUT_PULLUP );
61
  Serial.begin(38400);
62
  (!Serial)
63
  ;
64
  Timer1.initialize(200); // .2 ms Interrupt von Timer 1
65
  Timer1.attachInterrupt(my_isr);
66
}
67

68
void loop ()
69
{
70
  if ( digitalRead(8))
71
    z++;
72
  else
73
    z--;
74

75
  z = constrain(z, 0, 100);
76

77
  if (z > 80)
78
    swi = 1;
79

80
  if (z < 20)
81
    swi = 0;
82

83
  //Serial.println(swi);
84

85
  if ( swi == 1  && swialt == 0)
86
   {
87
    i++;
88
    digitalWrite(13, i&1);
89
    
90
   }
91
Serial.println(swi);
92

93
  swialt = swi;   //Rueckspeichern
94
 // delay(1);
95
}

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren