Ich habe die Aufgabe mit einem AVR bei mehreren Kanälen jeweils 3
Zustände (High,Low, Toggle) zu unterscheiden. Das Toggeln kann sich in
der Frequenz unterscheiden, wobei nur eine bestimmte Frequenz als
wirkliches toggeln, die jeweils anderen als AN und AUS-Schalten
interpretiert werden. Flankeninterrupts an den Pins kann ich dazu nicht
verwenden.
Ich habe mir folgendes gedacht:
Mit TimerX Kanäle abtasten und jeweils Zustände per shiften in eine
Variable schreiben. Nach 8 Abtastwerten wird dieser ausgewertet. Dies
entspricht 2 Perioden. Ich brauche also ein char. Nach den 8 Werten
müsste in diesem char etwa sowas stehen, wenn es sich um das erwähnte
toggelnhandelt:
11001100 oder
10011001 oder
00110011 ...
Kann man sich ja denken..!? Um nun festzustellen, ob der Kanal nun mit
er Frequenz getoggelt wurd, muss in dieser Variablen der code
"...110011..." oder "...001100..." auftauchen. Dabei nehme ich an, dass
3 Halbperioden (an, aus, an oder aus,an,aus) ausreichen um das Toggeln
zu identifizieren. 2 würden ja zB. nicht ausreichen, da es sich auch um
ein normales AN, AUS-Schalten handeln könnte.
Ich habe mir nun diesen Algorithmus ausgedacht um diesen Code zu
identifizieren.
Kurz zur Erklärung: Es wird solange die char Variable buffer (in der
sich vielleicht der Code 0011 0011versteckt), mit der Maske 0011 1111
geodert bis entweder der Code herauskommt oder das Shiftmaximum erreicht
ist. In diesem Fall darf nur 2 mal links geshiftet werden, da dann die
8Bit erreicht sind. Da auch der invertierte Code 00 00 11 00 auftauchen
kann, wird nochmal geodert.
Wichtig ist hierbei, dass lediglich 3 Halbperioden als Code
interpretiert werden. Deshalb wird auch nicht mit dem ~code sondern nur
mit dessen ersten 6Bit gearbeitet.
Ich hoffe das war soweit verständlich. Ist der Algorithmus irgendwie
nachvollziehbar/richtig? Habe nur bedingte Debugmöglichkeiten. Gibt es
vielleicht bereits fertige Lösungen für das Problem?
Im Endeffekt, will ich eine bestimmte Frequenz messen und von einfachem
AN, AUS mit anderen Frequenz zuverlässig unterscheiden. Die Abtastwerte
liegen im Abstand von 70ms. Die Periode die hier detektiert werden soll
liegt also bei 4x70ms.
Vielleicht könnr ihr mal was zum Code bzw. Problematik sagen
Gruß
Maddin
Lass doch einen Timer frei rennen. Dann mach an den Input-Pin ein
Pinchange Interrupt. Dann kannst du High- und Low-Zeiten messen, deren
maximale Länge nur durch mehrmaliges(einmal ist ok) überlaufen des
Timers begrenzt sind(darum kann man sich aber auch kümmern).
Jedesmal bei Pinchange speicherst du den Timerwert in einer Variable
Timer_old. Das muss am Ende der PCINT-ISR geschehen.
Es ergibt immer Timer_jetzt - Timer_old die Zeit eines High bzw.
Low-Pegels.
Die kannst du in ein Array schreiben(Ringpuffer oder Liste) und
auswerten, ob deine Frequenz dabei ist. Dazu reicht eigentlich schon ein
einziger Wert.
mehrmalige Timerüberläufe kann man detektieren und ein Flag setzen, dass
der nächste Pinchange nicht gewertet wird(Wert wird nicht ins array
übernommen).
Du kannst jetzt sogar obere und untere Schwellen für die halbe
Periodenzeit deiner zu suchenden Frequenz setzen und bestimmen, wieviele
halbe Periodenzeiten dir genügend Genauigkeit zum sicheren Detektieren
bieten.
mfg mf
Kommt mir rechlich kompliziert vor.
Warum zählst du nicht einfach für jeden Kanal mit, wieoft hintereinander
du ihn im selben Zustand vorgefunden hast. Bei 2-mal oder weniger
toggelt er, bei mehr ist er konstant auf an oder aus.
Mini Float schrieb:> Dann mach an den Input-Pin ein> Pinchange Interrupt.
Dazu müsste der entsprechende Kanal sich an einen INT Pin befinden. Tut
er aber nicht. Ich habe 16 Kanäle die alle ausgewertet werden müssen.
Über den Rest den zu schriebst, muss ich jez erstmal nachdenken.
Man kann ein Pinchange durchaus auch ohne Interrupt auswerten.
Benutz einfach ein Statusregister um den aktuellen Zustand zu sichern,
dann kannst du alle Pins der Reihenfolge nach mit dem Statusregister
vergleichen. Bei Änderung wird dann der jeweilige Timerwert abgelegt und
die Bitposition im Statusregister angepasst. Dazu kannst du am besten
ein Xor Verknüpfung nehmen.
Beispiel:
Pin5 von 0 auf 1
Statusregister: --0-----
Pinposition: 00100000
-> Xor ergibt: --1-----
Wichtig wäre noch die Frequenz die du als Toggeln ermitteln willst bzw.
das Verhältnis zum Timer. Bei der Ermittlung der jeweiligen Zeit ist
natürlich auf einen Timerüberlauf zu achten.
Karl heinz Buchegger schrieb:> Warum zählst du nicht einfach für jeden Kanal mit, wieoft hintereinander> du ihn im selben Zustand vorgefunden hast. Bei 2-mal oder weniger> toggelt er, bei mehr ist er konstant auf an oder aus.
Ich glaube das ist nicht ganz eindeutig. Kommt zum Beispiel für 2
Abtastwerte also eine Halbperriode (bzw. ein kurzes AN) eine 1, dann
handelt es sich nicht zwangsläufig um das zu detektierende Toggeln mit
entsprechender Frequenz.
Auch wenn sich dein Vorschlag schonmal interessan und einfach anhört.
Das muss ja eine Art Ringzähler sein oder so. Nehmen wir an ich
inkremtentiere immer dann, wenn der vorherige Zustand dem aktuellen
entspricht. Dann muss ich das ja irgendwannn auswerten. Dann steht
irgendwann in der Variablen eine 1. Ich habe also den Toggeln Zustand
erkannt. Nun bleibt das Signal weiterhin auf dem Zustand und die Zahl
wird größer. Es liegt also doch garkein Toggeln vor.
Oder ich muss die Variable erst nach eine längeren zeit auswerten. Aber
dann sagt zum Beispiel eine 2 in de Variablen nichts darüber aus wie
groß die Lücke zwischen 2 gleichen Zuständen ist.
Hhmmmm
Gruß
Maddin
Hannes schrieb:> Man kann ein Pinchange durchaus auch ohne Interrupt auswerten.
Das Verstehe ich nicht. Wann bekommte ich mit wann eine Pinchange
vollzogen wurde? Wer teilt mir das mit? Soll ich wie blöd 16 Kanäle
pollen?
>Soll ich wie blöd 16 Kanäle pollen?
Nie wie blöde, sondern schlau! 5-10kHz bei 16MHz Takt sind kein Problem.
Auch ein AVR hat ein Recht auf Vollbeschäftigung :-)
Um das nochmal genauer zu erklären ^^
Du liest einfach zu einem bestimmten Zeitpunkt deine Ports aus und gehst
Pin für Pin durch und vergleichst mit dem Vorzustand (Statusregister).
Wenn sich etwas geändert hat wird es wie oben beschrieben im
Statusregister übernommen.
Wichtig wäre für die Auswertung noch ob du nur eine bestimmte Frequenz
detektieren willst, oder alle Impulse die unter einer bestimmten Dauer
liegen.
Prinzipiell gibt es zwei Möglichkeiten. Entweder nutzt du einen frei
laufenden Timer und ermittelst die genaue Länge über die Differenz der
beiden Timerwerte oder du nutzt ein Timerinterupt welches auf die
Frequenz des Toggelns abgestimmt ist. Letzteres erscheint mir die
einfachere Variante, allerdings kann es dann zu Ungenauigkeiten kommen
wenn du einen ganz bestimmten Frequenzbereich isolieren willst.
Hoffe ich konnt erstmal bissl helfen :D
Hi
>Das Verstehe ich nicht. Wann bekommte ich mit wann eine Pinchange>vollzogen wurde? Wer teilt mir das mit? Soll ich wie blöd 16 Kanäle>pollen?
Die Pin-Change-Interrupt-Flags werden auch ohne Interrupt gesetzt. Also
beschränkt sich das Ganze auf die Abfrage einiger Bits in einem
IO-Register.
Du darfst nur das Rücksetzen der Flags nicht vergessen.
MfG Spess
spess53 schrieb:> Die Pin-Change-Interrupt-Flags werden auch ohne Interrupt gesetzt. Also> beschränkt sich das Ganze auf die Abfrage einiger Bits in einem> IO-Register
Ich weiß nich ob ich es schon verstehe. Ich soll jeweils das PINx
Register auslesen? So mache ich es ja im Moment. Mich verwirrt der
Ausdruck Pin-Change-Interrupt ohne den Zusammenhang mit den INT-pins.
Das heißt für mich einfach das Register PIN auslesen.
Bis jetzt läuft es so: Der Timer liest im 70ms Takt Kanäle ein und
shiftet den Status in die Variable bufferx.
Wenn ich das richtig verstehe, wäre es sinnvoller einfach für jeden
Kanal ein Bit bereitzuhalten, dass den jeweils vorherigen Status
beinhaltet. Diesen bekommt man in dem man PINx an der richtigen
Bitstelle ausliest.
Das mit der Zeitmessung wäre auch eine Möglichkeit.
Im Prinzip ist das alles nicht zeitkritisch, es muss nur zuvelässig
klappen. Das betreffende Toggeln geschieht im ca. 140ms Takt. Also 140ms
An und wieder 140ms Aus. In der Regel setzt sich dies dann für einen
längeren Zeitraum fort. Irgendwann kann der Kanal dann eben auf Low oder
High bleiben. Und dies muss unterschieden werden vom Toggeln. Natürlich
kann der Kanal sich auch im 300ms Takt toggeln, was jedoch einfach als
AN und AUS detektiert werden muss. Änderungen unter 140ms brauchen nicht
detektiert werden, solange die folgenden Kanalzustände richtig
interpretiert werden. Da darf also nichts durcheinander kommen, nur weil
mal kurz schnell AN,AUS und wieder AN geschalten wurde.
Ich bleib dran. Schonmal ein Dank an euch
Gruß
Maddin
mein Vorschlag zur Auswertung wäre noch für jeden Pin ein Register
anlegen. Dieses wird immer um 1 erhöht wenn der ausgelesen wurde und
sich nix verändert hat. In gewisser Weise die Dauer des jeweiligen
Pegels. Dazu kannst du die Ausleserate erhöhen und hast mehr
Genauigkeit.
Zwei dinge sind zu beachten:
1. bei Überlauf eines Registers
-> Lösung: wenn Flag gesetzt, zb. 128 in Register schreiben und Flag
löschen
(Dadurch bewegen sich die dauer-an/aus Ports immer zwischen 128 und
255)
2. Auswertung bei Toggeln
-> Prüfen ob die Dauer im gewünschten Bereich liegt
(sprich über 128 ist oder unter zb. 100; obere und untere Grenze)
Ich gebe zu es ist etwas aufwändig, aber dadurch kannst du mit einer
guten Timereinstellung sehr genau bestimmte Toggellängen filtern.
Hi
>Ich weiß nich ob ich es schon verstehe.
Nicht so richtig. Wenn dein AVR Pin-Change besitzt dann hat er auch
folgende Register:
Pin Change Interrupt Control Register - PCICR
Pin Change Interrupt Flag Register - PCIFR
und ein oder mehrere
Pin Change Mask Register x – PCMSKx
in den PCMSKx-Registern legst du fest, welche Pins der zugehörigen
Gruppe einen Interrupt auslösen kann.
Im PCICR wird festgelegt welche Pin-Gruppe einen Interrupt auslösen
darf.
Die PCIFx-Bits im PCIFR zeigen an, in welcher Pin-Gruppe ein sich Pin
verändert hat. Das passiert aber auch, wenn in PCICR kein Interrupt
freigegeben wurde. Um also eine Änderung an deinen Pins festzustellen
brauchst du nur die PCIF-Bits im PCIF-Register überprüfen. Wenn eins
oder mehrere 1 sind, haben Pins gewackelt. Die PCIF-Bits musst du danach
durch Schreiben einer 1 wieder zurücksetzen.
MfG Spess
Bei Verwendung von PCINT würde ich aber vorher klären, woher die Impulse
kommen. Falls es ein Relaisausgang sein sollte, wäre PCINT tödlich. Bei
140ms Pulsdauer würde ich es per Pollen machen. Allerdings nicht mit
70ms sondern feiner aufgelöst.
Maddin schrieb:> Bis jetzt läuft es so: Der Timer liest im 70ms Takt Kanäle ein und> shiftet den Status in die Variable bufferx.
Was so, oder so ähnlich schon vorgeschlagen wurde:
Die eingelesenen, aktuellen Werte mit denen bei der letzten Abfrage
eingelesenen (und gespeicherten) Werte XOR Verknüpfen. Das Ergebnis der
XOR-Verknüpfung schiftest Du dann, für jeden Kanal, in je eine bufferx
Variable. Die Anzahl der 1sen in der jeweiligen Variable gibt dann an,
wie oft der dazugehörige Eingang während der letzten 8 Abfragen,
getoggelt wurde.
Gruß
John
Pin Change Interrupts kennst du nicht, ist schade.
Da mein Vorgehen noch nicht ganz verstanden wurde, muss Ich das wohl mal
in Pseodocode gießen.
Hier mal nur für einen zu prüfenden Pin. Sonst muss eben ein
mehrdimensionales Array hergenommen werden und in der Pinchange
Interrupt Routine auf jeden geänderten Pin die Schiebung und das
Speichern angewendet werden auch braucht man immer ein anderes Time_old
wie auch eigene Flags. Ringpuffer statt schieben ist natürlich
eleganter, aber hier zum Verständnis des Prinzips mal draußen gelassen.
Doch nun der Reihe nach:
spess53 schrieb:> Nicht so richtig. Wenn dein AVR Pin-Change besitzt dann hat er auch> folgende Register:>> Pin Change Interrupt Control Register - PCICR> Pin Change Interrupt Flag Register - PCIFR>> und ein oder mehrere>> Pin Change Mask Register x – PCMSKx
Geht es also doch um die PCINTs!? Habe es also doch verstanden. Bei mir
heißen die EICRA, EICRB, EIMSK... mMan stellt ein, auf welche Flanke der
Interrupt kommen soll und für welchen INTx Pin und dann springt das
Programm in die ISR(INTx). Davon gibts aber nunmal nur 8. Ich könnte
höhstens bei 8 von den 16 die Frequenz damit feststellen.
Nebenbei: Bei 8 von den 16 stelle ich den Kanalzustand eh schon über
diese PCINTs fest. bei den anderen 8 überprüfe ich mit TimerAbfrage.
Hi
>Bei mir heißen die EICRA, EICRB, EIMSK...
Nein. Die sind für INT0...INTn. Aber vielleicht solltest du mal
verraten, welchen AVR du benutzt.
MfG Spess
Hi
>Ich merk schon, wir reden von verschiedenen Sachen. Das gesamte>Datenblatt kennt kein PCI oder ähnliches.
Richtig.
>Nutze ATmega64
Der hat keinen Pin-Change-Interrupt. Das sind normale externe
Interrupts.
MfG Spess
Hannes schrieb:> mein Vorschlag zur Auswertung wäre noch für jeden Pin ein Register> anlegen. Dieses wird immer um 1 erhöht wenn der ausgelesen wurde und> sich nix verändert hat. In gewisser Weise die Dauer des jeweiligen> Pegels. Dazu kannst du die Ausleserate erhöhen und hast mehr> Genauigkeit.
Mit diesem erhöhen wird man meiner Meinung nach nicht die Frequenz genau
bestimmen können. Woher soll man wissen ob der Kanal lange einen Zustand
hatte und dann gleiche Zeit einen anderen oder ob zwischendurch immermal
wieder umgeschaltet wurde. Das wird man später nicht mehr wissen wenn
man nur eine Zahl hat.
du kannst das ganze auch über ein Timerinterupt steuern, dann ist auch
egal was der Controller kann. Is sozusagen eine Softwarelösung :)
also ich würde das so machen:
Timerinterupt -> alle Pins werden eingelesen und mit dem vorherigen
Zustand verglichen
Dann wird für jeden Pin ein Register geschrieben, bei gleichem Pegel um
1 erhöhen und bei Änderung den Wert im Register auswerten und dann
wieder 0 setzen.
Bei der Auswertung stellt sich die Zahl im jeweiligen Register ein
Vielfaches des Timerinterupttaktes dar. Wichtig ist hier nur das alle
Register die über 255 steigen, also überlaufen, wieder auf einen Wert
gesetzt werden der nicht als Toggeln erkannt wird.
(wenn flag gesetzt -> 141 in Register schreiben -> flag löschen)
Damit bewegen sich alle Register der Pins die länger als 255 Durchläufe
an sind zwischen 141 und 255. Das Toggeln kann dann im unteren
Zahlenbereich detektiert werden. Also wenn eine Änderung vorliegt, dann
wird der Wert in dem Register welches die Pegeldauer eines Pins enthält
geprüft.
(alles was zb zwischen 70 und 140 liegt wird dann als Toggeln
betrachtet)
Du kannst den Timerinterupt so einstellen, das du in den Registern die
Millisekunden zählst. Dadurch können dann Mindestdauer und Höchstdauer
genau gesetzt werden.
Einziger Nachteil könnte sein, das es durchaus passieren kann wenn alle
Pins gleichzeitig wechseln, das der nächste Timerinterupt übersprungen
wird.
Ich hoffe mal das war jetzt ausführlich genug und stiftet nich zu viel
Verwirrung :D
Hannes schrieb:> Ich hoffe mal das war jetzt ausführlich genug und stiftet nich zu viel> Verwirrung :D
Das war sehr gut. Denke mal drüber nach und bastel rum. Danke vorerst.
du musst allerdings wie schon erwähnt berücksichtigen, das wenn alle
Pins wechseln und bearbeitet werden müssen die Summe der
Programmschritte nicht über das nächste Timerinterupt führen darf.
Sonst wirds ungenau :P bzw. wird die Obergrenze für die Pegeldauer die
noch als Toggeln erkannt wird erhöht.
Dann viel spaß beim basteln =)
if(!(flag_for_toggle))function_for_high();//Funktion nur aufrufen, wenn Zustand "nicht toggeln" ist
22
elsefunction_for_toggel();
23
if(last_status)
24
{
25
counter++;//wenn letzter Signalzustand auch high dann Zähler++
26
if(counter==0xFF)counter=0x80;//wenn Zähler überläuft, dann auf 128 setzen
27
}
28
elseflag=1;//wenn nicht, also Zustand sich geändert hat, dann Flag für main() setzen
29
last_status=1;//speicher aktuellen Zustand für nächstes Abtasten
30
}
31
elseif(pin_low)//wenn Signal low am Pin
32
{
33
if(!(flag_for_toggle))function_for_low();//Funktion nur aufrufen, wenn Zustand "nicht toggeln" ist
34
elsefunction_for_toggel();
35
if(!(last_status))
36
{
37
counter++;//wenn letzter Signalzustand auch low dann Zähler++
38
if(counter==0xFF)counter=0x80;//wenn Zähler überläuft, dann auf 128 setzen
39
}
40
elseflag=1;//wenn nicht, also Zustand sich geändert hat, dann Flag für main() setzen
41
last_status=0;//speicher aktuellen Zustand für nächstes Abtasten
42
}
43
44
...
45
}
Habe das nun mal ausprobiert für einen Kanal. Ob das nun die
ressourcensparenste lösung ist, mag mal dahin gestellt sein. Was zählt
ist vorher ein Ergebnis.
Fakt ist, dass function_for_toggle() nicht aufgerufen wird. Meiner
Meinung nach bleiben nur die Möglichkeit der falschen werte für die
if(..) in der Main oder eben ein von mir noch unentdeckter einfacher
Fehler im Algorithmus. Vielleicht springt euch was ins Auge...?
Kurz zur Erklärung, es gibt eine Funktion für jeden Zustand
(high,low,toggel). Die Funktionen für high und low werden gesetzt wenn
der Signalzustand ensprechend ist und das Flag flag_for_toggle NICHT
gesetzt ist. Ist es gesetzt wird die Funktion fürs toggeln aufgerufen.
Das Flag wird in der Main gesetzt in dem der Counter untersucht wird auf
einen bestimmten Bereich. (Noch) in der ISR wird counter kurz vor dem
überlauf auf 128 gesetzt. Counter läuft also wiederholt von 128 bis 255
wenn der Zustand des Kanals lange einen Zustand hat.
Ein Manko gibt es noch, sollte jedoch nicht zum aktuellen Fehlverhalten
führen: Startet das Programm und last_status ist 0 und wird ein high
erkannt, dann wird auch schon das Flag für die main gesetzt auch wenn
dies noch keiner wichtigen! Zustandsänderung entspricht.
Gruß
Maddin