Forum: Mikrocontroller und Digitale Elektronik einfache und übersichtliche Flanken erkennung


von gedenk (Gast)


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

von Stefan F. (Gast)


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.

von Alex D. (allu)


Lesenswert?

gedenk schrieb:
> Ich möchte Flanken erkennen die nicht schneller als meine Main Loop.

Einmal in der Main Loop den "Flankeneingang" abfragen und das Ergebnis 
in ein Register links reinschieben. Mit "and" 3 Länge die Länge 
begrenzen. Mit compare 01 oder 10 auf die gewünschte Flanke prüfen.

von gedenk (Gast)


Lesenswert?

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

von Stefan F. (Gast)


Lesenswert?

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

Das klingt für mich nicht danach, dass du jede Flanke erkennen willst, 
sondern nur die erste steigende. Darauf bezog sich mein obiges Beispiel.

von Alex D. (allu)


Lesenswert?

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

Natürlich muss man den vorherigen Zustand kennen, wie sollte man sonst 
eine Änderung erkennen können ?

von ihr tiefer und voller Klang (Gast)


Lesenswert?

gedenk schrieb:
> Ich möchte Flanken erkennen die nicht schneller als meine Main Loop.

Jede Flanke ist schneller als die main loop ->
1
if (false) passiert_eh_nie;

Beitrag #5908613 wurde von einem Moderator gelöscht.
von gedenk (Gast)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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

Ohne Variablen läßt sich nunmal schlecht programmieren.
Du kannst Dir ein Macro schreiben, was für Dich die Variable anlegt.

von kiss (Gast)


Lesenswert?

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

Per ISR, dessen Interrupt nur bei einer Flankenart getriggert wird.

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

Anbei mal ein einfaches Macro zur Flankenerkennung.

von gedenk (Gast)


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

von NichtWichtig (Gast)


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.

von gedenk (Gast)


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.

von Stephan (Gast)


Lesenswert?

gedenk schrieb:
> ch war nur auf der Suche nach einem System wo ich nicht Händisch diese
> Variable anlegen muss.

Dich zwingt Niemand dazu. Du kannst auch eine Speicherstelle/ Bit direkt 
ansprechen.

von gedenk (Gast)


Lesenswert?

Stephan schrieb:
> Dich zwingt Niemand dazu. Du kannst auch eine Speicherstelle/ Bit direkt
> ansprechen.

was meinst du damit?

von Einer K. (Gast)


Angehängte Dateien:

Lesenswert?

gedenk schrieb:
> Ich war nur auf der Suche nach einem System wo ich nicht Händisch diese
> Variable anlegen muss.
Irgendwas musst du aber tun!
Telepathie, können unsere µC noch nicht.

Ansonsten kann ich dir nur eine bequeme C++ Lösung anbieten.
Im Anhang, ein Beispiel.

von Guggel (Gast)


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!

von gedenk (Gast)


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

von Peter D. (peda)


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.

: Bearbeitet durch User
von Martin V. (oldmax)


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

: Bearbeitet durch User
von Einer K. (Gast)


Angehängte Dateien:

Lesenswert?

Peter D. schrieb:
> Wie heißen denn die dafür nötigen Libs und wo kann man sie downloaden?

Die kannst du bei mir bekommen!

Und dann wirft dein Kompiler auch keine Meldungen mehr.
C++11 sollte er dafür schon können, und einen der üblichen AVR Arduinos 
solltest du verwenden.

von Stefan F. (Gast)


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
}

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

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

static bool last_flag = false; /* new variable on every macro usage*/

das macro sollte hier mit "__LINE__" verbessert werden, damit 
automatisch immer eine neue variable angelegt wird!

mt

: Bearbeitet durch User
von kiss (Gast)


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.

von Tobi P. (Gast)


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".

von Peter D. (peda)


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

von Peter D. (peda)


Lesenswert?

Arduino Fanboy D. schrieb:
> Die kannst du bei mir bekommen!

Private Libs würde ich immer so kenntlich machen:
1
#include "CombiePin.h"
2
#include "CombieTimer.h"
3
#include "CombieTools.h"

Dann muß man nicht erst vergeblich danach suchen.

von Frank M. (ukw) (Moderator) Benutzerseite


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.

: Bearbeitet durch Moderator
von Einer K. (Gast)


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.

von Apollo M. (Firma: @home) (majortom)


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

von Peter D. (peda)


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.

von Stefan F. (Gast)


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.

von Frank M. (ukw) (Moderator) Benutzerseite


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.

: Bearbeitet durch Moderator
von Einer K. (Gast)


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.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Arduino Fanboy D. schrieb:
> Geschickt gebaut, kann man so auch z.B. 8 Kanäle gleichzeitig in einem
> Byte verwalten.

Jepp. Wenn man mit dem Speicher nicht geizen muss, gehts auch mit einem 
Array namens last_states[].

von max123 (Gast)


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
}

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.