Liebe Mikrocontroller.net-Gemeinde,
nachdem ich schon unzählige Stunden hier im Forum als Leser verbracht
habe, habe ich heute ein erstes persönliches Anliegen, das ich nicht
selbst gelöst bekomme:
Meine Hardware:
ATmega32 + STK500
externer Baudratenquarz 7,372800 Mhz
3-Draht-Hallsensor
TTL zu USB - Konverter
Software:
AVR-Studio 4.19
Mein Vorhaben:
Ziel ist es die Rotationsgeschwindigkeit und die Rotationsrichtung
zweier Räder zu erfassen. Hierfür habe ich je Rad zwei Hallsensoren
angedacht, welche unsymmetrisch zueinander angeordnet sind. Der eine
wird also zur Drehzahlerfassung benutzt, über den anderen kann ich in
Kombination mit dem ersten die Drehrichtung bestimmen. So der Plan.
Das Ganze soll dann per UART an an einen PC gesendet werden, soweit bin
ich aber noch garnicht.
Mein Problem:
Ich dachte, ich nehme die beiden 8-Bit-Timer, einer für jedes Rad, und
starte diese mit der negativen Flanke an INT0 bzw. INT1 (externe
Interrupts). Also wenn Hall_1 schaltet, Start Timer0 über INT0, wenn
Hall_2 schaltet, Start Timer2 über INT1. Gestoppt/zurückgesetzt werden
sollen die Timer ebenfalls durch diese externen Interrups (negative
Flanke), davor noch den Wert auslesen und schon habe ich die
Rotations-Frequenz der Räder. So war der Plan.
Da es sich ja nur um 8-Bit-Timer handelt, wollte ich sie softwaremäßig
über Hochzählen einer Zählvariable bei Timer-Overflow erweitern.
Jetzt habe ich festgestellt, dass sich Timer-Overflow-Interrupt und der
externe Interrupt irgendwie in die Quere kommen. Jedenfalls, sobald ich
"TIMSK |= (1<<TOIE0);" einbinde, funktioniert das Umschalten einer
Test-LED über den externen Interrupt nicht mehr wie gewünscht. Sie
toggelt nicht mehr nur bei der negativen Flanke sondern blinkt nur ganz
kurz auf sobald der Magnet in die Nähe des Halls kommt (auch wenn ich im
Schaltbereich bleibe, geht sie sofort wieder aus)
Im Datenblatt habe ich folgendes gefunden, das sich auch nicht so ideal
für mich anhört, oder?!:
"If external pin modes are used for the Timer/Counter0, transitions on
the T0 pin will clock the
counter even if the pin is configured as an output. This feature allows
software control of the
counting."
Hier mein bisheriger Code:
while(!(UCSRA&(1<<UDRE)));// warten, bis Sendepuffer leer ist
106
UDR=data;// Byte senden
107
return0;
108
}
Hat jemand von euch eine Idee was ich hier besser machen könnte bzw wie
ich diese Aufgabe allgemein besser angehen könnte? Hab das Gefühl, dass
das besser, komfortabler, geht. Wäre toll wenn ihr da vielleicht ne Idee
oder zwei hättet. Eigentlich ist das Problem ja ein recht Simples.
Vielleicht gehe ich es falsch an.
Gute Nacht und vielen Dank schonmal!
Viele Grüße,
Daniel
Das hier
> PORTD = ~(PORTD & (1<<PD7));
toggelt nicht nur den einen Pin, sondern setzt im gleichen Aufwasch auch
alle anderen Pins an diesem Port auf 1
Ehe ich das jetzt bitweise auseinander pfriemle - so toggelt man einen
Pin. Und zwar NUR diesen einen Pin!
PORTD ^= ( 1 << PD7 );
Pin setzen: PORTD |= ( 1 << PD7 );
Pin löschen: PORTD &= ~( 1 << PD7 );
wenn du die Dinge anders machst, darfst du dich nicht wundern, wenn an
deinen Pins seltsame ungewollte Änderungen passieren.
> If external pin modes are used for the Timer/Counter0
Taktest du denn deinen Timer mit dem T0-Pin?
Nein?
Dann betrifft es dich auch nicht.
Ich bin zwar aus deiner Beschreibung nicht wirklich schlau geworden, wie
du dir die Drehzahlmessung vorstellst, für mich klingt das bischen das
ich verstanden habe, allerdings nicht richtig. Aber das wirst du dann
schon sehen.
Sieh dir mal dieses Datenblatt an.
Dieser Sensor kann genau das was du da machen willst. Im Datenblatt ist
auch so in etwa beschrieben wie der Sensor arbeitet.
TLE4942; das icon links oben in der Ecke, nicht das große werbebanner
http://www.datasheetarchive.com/dataframe.php?file=DSA-265387.pdf&dir=Datasheets-14&part=TLE%204942#
Ich würde das ganze anders angehen.
1. die externen Inputs zu den CapCom timer nutzen um mit jeder Flanke
eines Sensors pro Rad den counter hochzählen.
2. "normalen"Timer nutzen um in definierten Abständen einen Interrupt zu
triggern. zb 100 msec.
In diesem Interrupt die counter aus 1) auslesen.
3. x counts pro zb 100 msec ==> Geschwindigkeit
4. pro Rad einen Sensor auf einen externen IRQ pin legen
5. bei Interrupt beide Sensoren eine Rades einlesen ==> ergibt ein
Bitmuster 00,01,10,11
6. aus der Abfolge der Bitmuster , letzter IRQ zu aktueller IRQ bekommst
du die Drehrichtung.
sensor 1 : ____----____----____----
sensor 2 : _----____----____----_
Bitfolge1: 0 0 1 1 0 0 1 1 0 0 1 1
Bitfolge2: 0 1 1 0 0 1 1 0 0 1 1 0
Wechsel zb also von 11 zu 10 Drehrichtung rechts
11 zu 01 Drehrichtung links
Hello!
> Das hier>>> PORTD = ~(PORTD & (1<<PD7));>> toggelt nicht nur den einen Pin, sondern setzt im gleichen Aufwasch auch> alle anderen Pins an diesem Port auf 1
Ich hab das schon selbst getestet in einer While-Schleife mit delay,
also ohne Interrupts und irgendwie schien es auch so zu funktionieren
als würde nur die eine LED toggeln, allerdings wird glaub tatsächlich
immer nur eine LED eingeschaltet aber der ganze PORT wieder
ausgeschaltet.
> Ehe ich das jetzt bitweise auseinander pfriemle - so toggelt man einen> Pin. Und zwar NUR diesen einen Pin!>>> PORTD ^= ( 1 << PD7 );
Das sieht auf doch schöner und auf den ersten auch Blick verständlicher
aus. Tollerweise hat es auch zur Lösung meines Problems geführt :)
Vielen Dank hierfür. Ich dachte dass ich mir mal selbst was überlege
statt es aus dem Tutorial zu übernehmen. Ging wohl schief. Merci auf
jeden Fall.
Kurze Frage: Muss immer eine Interruptroutine zu dem jeweiligen
Interrupt definiert sein? Was macht der Prozessor wenn ein Interrupt
ausgelöst wird, er aber keine Routine dazu findet?
> Ich bin zwar aus deiner Beschreibung nicht wirklich schlau geworden, wie> du dir die Drehzahlmessung vorstellst, für mich klingt das bischen das> ich verstanden habe, allerdings nicht richtig. Aber das wirst du dann> schon sehen.
Zur besseren Erläuterung habe ich an diesen Post mal eine Zeichnung
angehängt. Dieses Rad gibt es eben zwei Mal. Ich dachte eben, dass ich
mit dem zweiten Hallsensor, welcher nicht für die Drehzahlbestimmung
zuständig ist, abgleiche wo der von dem Drehzahl-Hallsensor gestartete
(bzw. Rückgesetzte) Zähler, gerade steht. Im Vergleich zu der einen
Schritt vorher erreichten Zählerstand, kann ich dann erkennen ob
Linkslauf oder Rechtslauf. Also sobald der Zählerstand (zum Zeitpunkt
der Auslösung des Hallsensors 2) über der Hälfte des gesamten letzten
Zählerstandes ist, habe ich z.B. Rechtslauf, wenn <50% dann Linkslauf.
So stelle ich mir das vor. Verständlich? Gibts da irgendwelche logischen
Kurzsschlüsse die ich übersehen habe, außer dass man immer auf eine
Messung vorher angewiesen ist?
Was mir aufgefallen ist, ist die Tatsache, dass ich dann wohl insgesamt
4 externe Interrupts bräuchte, für jeden Hallsensor einen, oder? Ist das
irgendwie möglich? Oder geht das auch ohne Interrupts?
Werde mir gleich nochmal den Lösungsvorschlag von Ralph genauer ansehen.
Eigentlich bin ich durch die schon getroffene Wahl der Sensoren und auch
bzgl. des Aufbaus, der so bleiben sollte, etwas eingeschränkt.
Über weitere Anregungen freue ich mich natürlich!
Viele Grüße,
Daniel
Daniel D. schrieb:> Vielen Dank hierfür. Ich dachte dass ich mir mal selbst was überlege> statt es aus dem Tutorial zu übernehmen.
Dann hast du nicht verstanden, wie und warum das eigentlich
funktioniert. Und sowas ist immer schlecht.
> Kurze Frage: Muss immer eine Interruptroutine zu dem jeweiligen> Interrupt definiert sein? Was macht der Prozessor wenn ein Interrupt> ausgelöst wird, er aber keine Routine dazu findet?
Was der Prozessor macht?
Der springt seinen Interrupt Vektor an und macht dort weiter.
Wenn du selbst keine ISR definiert hast, dann trägt der Compiler
allerdings trotzdem dort eine Funktion ein. Und diese Funktion macht:
einen komplett-Reset des Prozessors.
So, ja. Wenn du einen Interrupt freigibst dann solltest du auch eine ISR
dafür haben. Hast du keine, dann dreht der Compiler es so hin, dass der
µC resettet wird und das Programm von vorne beginnt.
Karl Heinz Buchegger schrieb:> Daniel D. schrieb:>>> Vielen Dank hierfür. Ich dachte dass ich mir mal selbst was überlege>> statt es aus dem Tutorial zu übernehmen.>> Dann hast du nicht verstanden, wie und warum das eigentlich> funktioniert. Und sowas ist immer schlecht.
Danke für deine Antwort. Das mit dem XOR habe ich schon verstanden -
hatte es auch schon auf dem Blatt Papier mit kleiner Wertetabelle
nachvollzogen. Ich dachte eben nur, dass ich das auch ohne XOR mit
eigener Überlegung lösen kann. Das war dann eben nicht richtig.
Daniel D. schrieb:> Karl Heinz Buchegger schrieb:>> Daniel D. schrieb:>>>>> Vielen Dank hierfür. Ich dachte dass ich mir mal selbst was überlege>>> statt es aus dem Tutorial zu übernehmen.>>>> Dann hast du nicht verstanden, wie und warum das eigentlich>> funktioniert. Und sowas ist immer schlecht.>> Danke für deine Antwort. Das mit dem XOR habe ich schon verstanden -> hatte es auch schon auf dem Blatt Papier mit kleiner Wertetabelle> nachvollzogen. Ich dachte eben nur, dass ich das auch ohne XOR mit> eigener Überlegung lösen kann. Das war dann eben nicht richtig.
Dein Fehler war, dass du dich nur auf dieses 1 Bit konzentriert hast.
Was du übersehen hast ist, dass du mit deinen Operationen auch die
anderen Bits beeinflusst.
Daher ist es immer eine gute Idee, das man beim Ausprobieren von
Bitoperationen auf dem Papier (was ich extrem begrüsse!) immer auch die
anderen Bits mit einbezieht und die relativ wahllos mit 0 oder 1
annimmt. Denn dann verlgeicht man das Endergebnis mit der
Ausgangssituation und da darf sich dann von den restlichen Bits kein
einziges verändert haben.
D.h. nicht gleich mit dem ersten Testergebnis zufrieden geben, sondern
ein paar verschiedene Dinge ausprobieren.
Guten Abend nochmal!
Den Vorschlag von Ralph habe ich bis Punkt 3 so umgesetzt. Funktioniert
auch super. Jetzt habe ich das Problem, dass das Rad ohne Übersetzung
mit Pedalen angetrieben wird. Nachdem ich mal gegogelt habe wie hoch die
maximale Trittfrequenz eines Radfahrers sein kann, stellte ich fest,
dass der Sensor keine 10 mal pro Sekunde ausgelöst wird. Meine Auflösung
ist also ziemlich "bescheiden", um es mal nett auszudrücken. Eigentlich
bräuchte ich zwei "Input Capture Units" habe ich festgestellt nach
durchpflügen des Datenblatts.
Oder aber ich hätte tatsächlich 4 external Interrupts. Zwei (einer für
jedes Rad) starten jeweils einen 8-Bit-Timer und stoppen ihn wieder -->
Zählerstand auslesen --> Frequenz. Die anderen beiden checken für T0 und
T2 die Zählerstände und vergleichen sie mit der Gesamtdauer der vorher
abgenommen Frequenz --> Drehrichtung.
Leider fehlt mir ein externer Interrupt. Da kam mir gerade beim
Schreiben eine IDEE!!: Ich könnte doch über Timer1, der noch übrig ist,
dafür sorgen, dass dieser (bei Prescaler=1) nach einem Prozessortakt
überläuft und einen Interrupt erzeugt. Wenn ich ihn dann mit dem
externen Takt an T1 takte, habe ich einen weiteren externen Interrupt,
der nur um einen Prozessortakt verzögert ist, oder?
Wäre das machbar?
Ralph schrieb:> 4. pro Rad einen Sensor auf einen externen IRQ pin legen> 5. bei Interrupt beide Sensoren eine Rades einlesen ==> ergibt ein> Bitmuster 00,01,10,11> 6. aus der Abfolge der Bitmuster , letzter IRQ zu aktueller IRQ bekommst> du die Drehrichtung.>>>> sensor 1 : ____----____----____----> sensor 2 : __----____----____----__>> Bitfolge1: 0 0 1 1 0 0 1 1 0 0 1 1> Bitfolge2: 0 1 1 0 0 1 1 0 0 1 1 0>> Wechsel zb also von 11 zu 10 Drehrichtung rechts> 11 zu 01 Drehrichtung links
Wenn du mal die angehängte Zeichnung von meinem Beitrag vom 05.12.2012
um 13:57 ansiehst, dann geht sowas bei mir eher nicht, hm? Bei mir ist
immer ein Sensor = 0, wenn der andere = 1 ist : /
Hat vielleicht noch jemand zufällig nen total tollen Einfall, der mir
alles "ganz einfach" macht?
Merci und gute Nacht!
Daniel
Laß das Starten und Stoppen sein. Nimm T1 und laß ihn durchlaufen. Die
Zeiten sind dann einfach die Differenz zum letzten Lesen.
Wenn die Interrupts nicht reichen, warum nimmst Du den alten ATmega32?
Die neueren (z.B. ATmega324) habe Pin-Change-Interrupts an fast jedem
Pin.
Peter
> Leider fehlt mir ein externer Interrupt.
Um die Drehzahl von (scheinbar) 2 Fahrräder zu messen?
Hinweis: deine Sensoren feuern ja nicht gleichzeitig. D.h. du kannst
alle Sensoren miteinander verschalten und an lediglich einen Interrupt
Eingang legen. Jeder Sensor geht zusätzlich noch an seinen eigenen
Port-Pin. In der ISR siehst du halt ganz einfach am Port nach, welcher
Sensor den Interrupt ausgelöst hat.
Aber eigentlich kann man die Sensoren auch durch Pollen abfragen. Und
dabei langweilt sich der µC wahrscheinlich sogar noch.
Und wie Peter schon sagte: Lass doch den Timer in Ruhe dahinzählen. Oder
stoppst du jedesmal deine Armbanduhr und startest sie bei 0, wenn du
eine Zeit abmessen willst? Nö - du merkst dir die Sekunden am Anfang und
die am Ende und ziehst dann die Endzeit von der Anfangszeit ab.
Daniel D. schrieb:> Jetzt habe ich das Problem, dass das Rad ohne Übersetzung> mit Pedalen angetrieben wird. Nachdem ich mal gegogelt habe wie hoch die> maximale Trittfrequenz eines Radfahrers sein kann, stellte ich fest,> dass der Sensor keine 10 mal pro Sekunde ausgelöst wird. Meine Auflösung> ist also ziemlich "bescheiden", um es mal nett auszudrücken.
Tja, daran hättest du auch mal eher denken können. Da du nicht nur die
Geschwindigkeit, sondern auch die Drehrichtung erfassen willst, wäre es
wohl besser gewesen von Anfang an auf einen Quadraturencoder zu setzen.
Den kannst du dann auswerten wie im Artikel Drehgeber beschrieben.
Ganz nebenbei könntest du damit auch deine Winkelauflösung
vervielfachen. Ein sehr gängiges Konstruktionsprinzip wären auf dem Rad
angebrachte Reflexmarken und zwei Reflex-Lichtschranken pro Rad.
> Eigentlich> bräuchte ich zwei "Input Capture Units" habe ich festgestellt nach> durchpflügen des Datenblatts.
Wenn das Ganze so langsam ist, daß du weniger als eine Umdrehung in
200ms hast, dann reicht auch Pollen zu festen Zeitpunkten. Z.B. einmal
alle 10 Millisekunden. Der µC langweilt sich dann immer noch.
XL
Karl Heinz Buchegger schrieb:>> Leider fehlt mir ein externer Interrupt.>> Um die Drehzahl von (scheinbar) 2 Fahrräder zu messen?
sozusagen direkt die Trittfrequenz an der Kurbelwelle beider Räder, ja
> Hinweis: deine Sensoren feuern ja nicht gleichzeitig. D.h. du kannst> alle Sensoren miteinander verschalten und an lediglich einen Interrupt> Eingang legen. Jeder Sensor geht zusätzlich noch an seinen eigenen> Port-Pin. In der ISR siehst du halt ganz einfach am Port nach, welcher> Sensor den Interrupt ausgelöst hat.
Ziemlich tolle Idee! Juhu :) So werde ich das machen. Dann brauch ich
nur noch zwei external Interrupts. Einer für jedes Rad - es wäre ja
möglich das die Sensoren von beiden Rädern exakt zum gleichen Zeitpunkt
durchschalten. Wenn auch vermutlich nur theoretisch.
> Aber eigentlich kann man die Sensoren auch durch Pollen abfragen. Und> dabei langweilt sich der µC wahrscheinlich sogar noch.
Wenn man sich mal mit Baudraten beschäftigt, vor allem aber mit Timern,
dann merkt man erstmal wie viel Zeit der Prozessor bei 8Mhz noch hat
anderes zu tun - vor allem verglichen zu einer Aufgabe die maximal alle
200ms abzuarbeiten ist. Allerdings finde ich es mit Interrupts schöner
und irgendwie auch korrekter. Nur weil mein µC wenig zu tun hat, möchte
ich nich auf ne Lösung zurückgreifen die ich verwerfen muss, falls das
Projekt erweitert werden sollte oder ähnliches.
> Und wie Peter schon sagte: Lass doch den Timer in Ruhe dahinzählen. Oder> stoppst du jedesmal deine Armbanduhr und startest sie bei 0, wenn du> eine Zeit abmessen willst? Nö - du merkst dir die Sekunden am Anfang und> die am Ende und ziehst dann die Endzeit von der Anfangszeit ab.
Peter sagte:
"nimm Timer1 und lass ihn durchlaufen". Ok. Angenommen Timer1 läuft
durch, dann merke ich mir jedes mal die Zeiten von diesem, die Differenz
bestimmt meine Frequenz (ich bräuchte dann tatsächlich nur diesen Einen
- verrückt). Die Drehrichtung würde ich dann auch herausbekommen. Dann
läuft T1 aber über und vielleicht nicht nur einmal, je nachdem wie
langsam der Radler tritt. Dazu lass ich eine Variable bei Overflow
mitzählen. Dann merke ich mir also immer Zählerstand und Stand der
Overflow-Variablen. Irgendwann muss ich diese ja aber auch mal wieder
"0" setzen bzw. sie hat einen natürlichen Overflow. Wie wähle ich denn
diesen Zeitpunkt geschickt, dass mir das dann keine Probleme macht?
@ Axel Schwenke
Das mit dem Quadraturencoder wär natürlich ne tolle Sache gewesen. Hab
mich auch kurz eingelesen, nachdem der schon ein paar Antworten drüber
erwähnt wurde. Leider bin ich jetzt an die HW gebunden.
Noch zwei kleine Verständnisfragen:
-Immer wieder lese ich von "SREG-Inhalt" speichern und zurück schreiben.
Wann ist das denn nötig?
- Wann liest man denn Flags aus bzw beschreibt diese selbst? Beim
"Pollen"? wenn ich wissen will ob etwas fertig ist? Bei Interrupts
werden ja immer Flags gesetzt bzw bei IRQs. Bisher hab ich das Gefühl,
dass diese hauptsächlich für den µC selbst da sind anstatt für den
Anwender zum Programmieren bzw Auslesen?
Danke schonmal herzlich ganz allgemein für eure Beteiligung an meinen
Problemchen!
Daniel
Daniel D. schrieb:> -Immer wieder lese ich von "SREG-Inhalt" speichern und zurück schreiben.> Wann ist das denn nötig?
Das ist in jeder ISR nötig, weil das unterbrochene Programm bei seiner
Fortsetzung den gleichen Inhalt im SREG haben muß wie vor dem Interrupt.
Aber der C-Compiler macht das implizit für Dich. In Assembler müßte man
das selber machen
Daniel D. schrieb:> - Wann liest man denn Flags aus bzw beschreibt diese selbst?
Naja, das kommt darauf an, welche Flags Du meinst und was Du eigentlich
machen willst... Wenn Du IRQ (Interrupt Request) Flags meinst: Dann beim
Pollen.
Peter Dannegger schrieb:> Wenn die Interrupts nicht reichen, warum nimmst Du den alten ATmega32?> Die neueren (z.B. ATmega324) habe Pin-Change-Interrupts an fast jedem> Pin.
Bis du das geschrieben hast, wusste ich garnicht, dass es diesen gibt.
Hab zwar schonmal was davon gelesen, dass es µC mit mehreren
PIN-Change-Interrupts gibt, aber irgendwie hatte ich das so gelesen bzw.
noch so im Kopf, dass dies eher ältere Modelle waren statt neue. Hab ich
dann wohl verdreht.
Gerade eben habe ich übrigens festgestellt, dass T1 bei Prescaler 1024
immer noch ziemlich lang braucht überzulaufen (knapp 10 Sekunden). Davor
hätte ich schon lang vom µC v=0 ausgegeben. Allerdings, wie behandelt
man denn solche Zeitstempel direkt um die Überläufe herum, also ganz
allgemein? Einmal ist die Differenz direkt innerhalb eines
Zählerdurchlaufs, einmal geht sie über den Overflow.
Ich muss mir immer etwas über eine zusätzliche Variable merken, oder?
Man könnte ja auch sagen:
Zeitdifferenz = (65535-Zeitstempel_1)+Zeitstempel_2 und mit
If Zeitdifferenz > 65535 then { Zeitdifferenz = Zeitdifferenz - 65535 }
das Ergebnis korrigieren, wenn nötig. Allerdings ist 65535 ja schon ne
ziemlich große Zahl für den µC und dann auch noch kontrollieren ob es
was größeres als 65535 gibt...hm
Daniel D. schrieb:> Dazu lass ich eine Variable bei Overflow> mitzählen.
So einfach geht das nicht. Es gibt da einiges zu beachten:
Beitrag "AVR Timer mit 32 Bit"Daniel D. schrieb:> Wie wähle ich denn> diesen Zeitpunkt geschickt, dass mir das dann keine Probleme macht?
Garnicht. Die Differenz zwischen 2 Werten stimmt auch nach einem
Overflow. Die Erfinder des Binärsystems haben sich das clever überlegt.
Peter
Daniel D. schrieb:> Karl Heinz Buchegger schrieb:>>> Leider fehlt mir ein externer Interrupt.>>>> Um die Drehzahl von (scheinbar) 2 Fahrräder zu messen?>> sozusagen direkt die Trittfrequenz an der Kurbelwelle beider Räder, ja
Ein kleiner Hinweis:
Bis du einmal die Kurbelwelle rundumgedreht hat, schafft dein µC eine
Schachpartie, inklusive Siegesfeier und anschliessender Ausnüchterung!
ALLES was du als Mensch tust, ist aus Sicht deines µC Extremzeitlupe!
Bis deine Sensoren nach einer Umdrehung wieder vorbeikommen, kann der
erst mal ein Nickerchen machen. Du unterschätzt deinen µC!
Interrupts brauchst du, wenn der µC im µ-Sekunden Bereich reagieren
muss. So schnell kannst du aber gar nicht treten.
Karl Heinz Buchegger schrieb:> ALLES was du als Mensch tust, ist aus Sicht deines µC Extremzeitlupe!> Bis deine Sensoren nach einer Umdrehung wieder vorbeikommen, kann der> erst mal ein Nickerchen machen. Du unterschätzt deinen µC!
Spätestens nachdem ich mich jetzt einige Zeit mit Timern beschäftigt
habe, weiß ich wie lang die Zeit einer Umdrehung im Gegensatz zu einem
(oder tausenden) Prozessortakt(en) ist. Allerdings ist es doch auch
schön, das mit Interrupts zu programmieren und ich lerne etwas dabei...
Jetzt muss ich erstmal das mit dem Overflow bzw Zeitdifferenz verstehen,
auch bzgl. des Beitrags von Peter
-->
Peter Dannegger schrieb:> So einfach geht das nicht. Es gibt da einiges zu beachten:>> Beitrag "AVR Timer mit 32 Bit"Daniel D. schrieb:> Zeitdifferenz = (65535-Zeitstempel_1)+Zeitstempel_2 und mit>> If Zeitdifferenz > 65535 then { Zeitdifferenz = Zeitdifferenz - 65535 }
so einfach geht das dann wohl nicht, oder?
Daniel D. schrieb:> Daniel D. schrieb:>> Zeitdifferenz = (65535-Zeitstempel_1)+Zeitstempel_2 und mit>>>> If Zeitdifferenz > 65535 then { Zeitdifferenz = Zeitdifferenz - 65535 }>> so einfach geht das dann wohl nicht, oder?
Du hast recht. zu kompliziert
1
unsignedintStempel1;// der frühere
2
unsignedintStempel2;// der spätere
3
4
unsignedintDifferenz=Stempel2-Stempel1;
fertig.
Erst dann, wenn die Differenz(*) tatsächlich größer als 65535 werden
kann, muss man die Overflows berücksichtigen. Bei allem darunter
brauchst du .... nichts tun. Durch die unsigned Rechnerei kommt trotzdem
das richtige Ergebnis raus.
100 - 65435 ergibt 200
und das ist genau die Anzahl der Zahlen zwischen 65435 und 100. Auch
dann, wenn zwischendurch der Timer von 65535 auf 0 zurückgesetzt wurde -
also einen Overflow gemacht hat.
(*) wenn also der zeitliche Abstand so groß ist, dass die tatsächliche
Zählerstand-Differenz größer als 65535 wird. Damit ist nicht gemeint,
dass der Timer überläuft. Wenn du im maximum Differenzen von, sagen wir
mal, 25000 erwartest, dann ist die einfache Subtraktion ok. 25000 ist
weit weg von 65535
Axel Schwenke schrieb:> Wenn das Ganze so langsam ist, daß du weniger als eine Umdrehung in> 200ms hast, dann reicht auch Pollen zu festen Zeitpunkten. Z.B. einmal> alle 10 Millisekunden. Der µC langweilt sich dann immer noch.
so einfach darf man da nicht rechnen!
der Magnet ist vielleicht in einem Abstand von 20cm vom Mittelpunkt und
bringt den Hallsensor in einem Bereich von 0,5cm zum Ansprechen
Winkel umgerechnet also 0,5/(20*2*3,14) * 100% = 0,4% einer Umdrehung
Zeit also bei 200ms: 0,8ms
pollen alle 10ms reicht also bei weitem nicht!
Liebe Leute,
vielen herzlichen Dank für eure Hilfe! Vor allem die Tipps mit dem
einlesen eines weiteren Pins zu dem external-IR-Pin und das mit der
"unsigned-Rechnerei"!! Danke den Erfindern des Binärsystems :)
Top!
Meine 4 Sensoren detektieren jetzt Drehzahl und Richtung der beiden
Räder.
Jetzt noch ne UART mit Interrupts, dann bin ich glücklich. Das ist ja
tollerweise ausführlich im Tutorial beschrieben.
Eine Frage noch aber noch zur Uart: Dieses "clever runden" benötigt man
nur, wenn man keinen Baudratenquarz einsetzt, oder? Bei einem
Baudratenquarz kommt ja mit der Formel aus dem Datenblatt immer ein
exakter Wert raus. Welchen Vorteil bringen denn dann Quarze im
ganzzahligen Mhz-Bereich? Bzw. welchen Nachteil Baudratenquarze? Kosten?
Danke nochmal und viele Grüße,
Daniel
Unten stehend mein Code, für Interessierte (vielleicht gibt es ja auch
noch Verbesserungsvorschläge):
das ist zweimal EXAKT der gleiche Code, nur mit anderen Variablennamen
bzw. Werten! Den Code kann man auch EINMAL in eine Funktion stecken (mit
lokalen Variablen) und dann von den beiden Stellen entsprechend
aufrufen.
und diese Funktion kann dann aufgerufen werden ...
1
...
2
if((s_1==0)&&(s_1_Start==1))// zweite Flanke Drehzahlsensor + Startbit Spieler 1 gesetzt // wenn zweiter Wert von Timer abgegriffen
3
{
4
period_S1=zaehlerS12-zaehlerS11;// Periode (Zählschritte) Spieler1 berechnen (16 bit)
5
period_Richtung_S1=zaehlerS1_Richt-zaehlerS11;// Periode Richtung Spieler1 berechnen
6
7
if(period_Richtung_S1<period_S1/2)// wenn Sensoren rechts, von hinten betrachtet. Ansonsten "> period_S1/2"
8
{
9
s_1_Richtung=0;// Vorwärts Spieler1 (wenn Sensoren rechts, von hinten betrachtet)
10
}
11
else
12
{
13
s_1_Richtung=1;// Rückwärts Spieler1 (wenn Sensoren rechts, von hinten betrachtet)
14
}
15
PORTC=~PORTC;
16
OutMessage('A',0x00,s_1_Richtung,period_S1);
17
s_1_Start=0;
18
}
19
20
if((s_2==0)&&(s_2_Start==1))// zweite Flanke Drehzahlensor + Startbit Spieler 2
21
{
22
period_S2=zaehlerS22-zaehlerS21;// Periode (Zählschritte) Spieler2 berechnen
23
period_Richtung_S2=zaehlerS2_Richt-zaehlerS21;// Periode Richtung Spieler2 berechnen
24
25
if(period_Richtung_S2<period_S2/2)// wenn Sensoren rechts, von hinten betrachtet. Ansonsten "> period_S1/2"
26
{
27
s_2_Richtung=0;// Vorwärts Spieler2 (wenn Sensoren rechts, von hinten betrachtet)
28
}
29
else
30
{
31
s_2_Richtung=1;// Rückwärts Spieler2 (wenn Sensoren rechts, von hinten betrachtet)
32
}
33
OutMessage('S',0x01,s_2_Richtung,period_S2);
34
s_2_Start=0;
35
}
... was den ganzen Code schon mal verkürzt und übersichtlicher macht.
Die beiden Vorschriften zur Behandlung von Spielern sind auch im Grunde
identisch. Der Rechengang ist der gleiche, nur die Variablen sind
andere. Den Rechengang kann man aber in eine Funktion verlagern und von
der Aufrufstelle her mit den relevanten Variablen füttern.
Nix gegen Kommentare, aber schau dir mal zb diesen Kommentar an
1
period_Richtung_S2=zaehlerS2_Richt-zaehlerS21;// Periode Richtung Spieler2 berechnen
und dann vergleich mal mit dem Code und frag dich: "Was steht im
Kommentar, was nicht auch im Code steht?"
Und die Antwort darauf lautet: nix!
An die Variable period_Richtung_S2 wird ein Wert zugewiesen. genau das
steht auch im Kommentar. Da steht "Periode Richtung Spieler 2". Das ist
der Variablenname, nur ohne die _. Was steht noch im Kommentar? Da steht
"berechnen". Schön. Das heisst im Klartext doch eigentlich nur, dass im
Code ein = vorkommt und rechts vom = eine Berechnung steht.
Schon. Aber genau das gleiche sehe ich auch, wenn ich mir den Code
ansehe.
Also: Was bringt dieser Kommentar - NIX!
Ein Kommentar, der nix bringt, der mir nix darüber erzählt, was warum an
dieser Stelle im Code vorgeht, das ist ein Kommentar, den man auch
löschen kann, ohne dass sich im Programm bzw. im Verstehen des Codes
irgendetwas ändert. Und genau das solltest du auch tun. Denn: Im besten
Fall bringt mir dir Kommentar nix. Im schlechtesten Fall ist der
Kommentar aber falsch, weil er bei Codeänderungen nicht nachgezogen
wurde. Und DANN verwirrt er, weil er nicht mehr mit dem Code
übereinstimmt.
Und Kommentare dieser Sorte gibt es viele in deinem Code.
Einen Code kommentieren bedeutet NICHT, dass man an jede Zeile etwas
drannschreiben MUSS - egal ob das irgendeinen Sinn hat oder nicht. Im
Idealfall ist der Code sein eigener Kommentar und nur wenn das nicht
geht, dann brauch ich einen Kommentar. Aber dann muss mir der Kommentar
auch tatsächlich etwas erzählen, was ich nicht sehen kann, indem ich mir
den Code ansehe! Ein Kommentar, der nur den Code als deutschen Satz
wiedergibt, der erzählt mir nix neues, der kann mir gar nichts neues zu
dieser Codestelle erzählen.
Grunregel:
Im Code steht das WIE
Im Kommentar steht das WARUM
Wenn sich das WARUM eindeutig aus dem Code ablesen lässt, dann brauch
ich auch keinen Kommentar dazu.
Das sich die Periode aus der Differenz Ende minus Start ergibt, das sehe
ich auch im Code. Das kennt auch jeder, der ein bischen nachdenkt, denn
das hat er schon oft benutzt. Wenn ich mit einer Uhr mit Sekundenzeiger
den vom Baum fallenden Apfel stoppe und der Apfel beginnt dann zu
fallen, wenn der Sekundenzeiger auf 12 stand und er schlägt am Boden
auf, wenn der Sekundenzeiger auf 17 stand, dann ist er 17-12 gleich 5
Sekunden gefallen. D.h. das muss ich nicht kommentieren! Denn das muss
sowieso jedem klar sein, dass
period = Ende - Start;
gilt. Wenn einem Leser das nicht in 3 Sekunden klar ist, dann soll er
erst mal in einen Grundkurs Physik gehen. Was er vielleicht nicht weiß,
das ist das diese Berechnung wegen unsigned immer funktioniert. DAS kann
ich in einem kleinen Kommentar dazu anmerken. Also: WARUM eine einfache
Subtraktion reicht.
Hallo Karl Heinz,
deine Ratschläge zur Code-Pflege hab ich mal so umgesetzt. Danke.
Jetzt hab ich gerade noch eine Frage zu deiner Idee:
Karl Heinz Buchegger schrieb:> Hinweis: deine Sensoren feuern ja nicht gleichzeitig. D.h. du kannst> alle Sensoren miteinander verschalten und an lediglich einen Interrupt> Eingang legen. Jeder Sensor geht zusätzlich noch an seinen eigenen> Port-Pin. In der ISR siehst du halt ganz einfach am Port nach, welcher> Sensor den Interrupt ausgelöst hat.
Aber:
Wenn ich alle Sensorausgänge verschalte, auf INT0 lege und zusätzlich
die Ausgänge dann auch noch auf je einen Port-Pin lege, werden dann
nicht alle PINs gegen Ground gezogen, wenn nur einer der Hall-Sensoren
durchschaltet? Die Ausgänge sind ja alle miteinander elektrische
verbunden?!
Oder denke ich da falsch?
Danke schonmal!
Die Frage geht natürlich nicht nur an Karl Heinz :)
----- PA0 (Pullups aktiviert)
|
S1 ---
|--- INT1 (Pullups aktiviert, reagiert auffallende Flanke)
S2 ---
|
----- PA1 (Pullups aktiviert)
Nur nochmal zur Visualisierung.
Wenn jetzt S1 = 0 wird, der Hall-Sensor also schaltet, und ich alles so
"hart" elektrisch verdrate, dann haben doch PA0, INT1 und PA1 dieselben
Pegel, ich kann also S1 und S2 nicht mehr unterscheiden...aber das ist
ja genau das was ich machen möchte.
Muss ich da Logik-Bausteine verwenden? Z.B:
------------ PA0
| ___
S1 ---| |
| OR |--- INT1
S2 ---|_____|
|
------------ PA1
Wär das korrekt? Ginge es auch ohne Logik-Baustein?
Danke! Viele Grüße,
Dnaiel
Daniel D. schrieb:> Oder denke ich da falsch?
die Überlegung ist schon richtig, einfach zusammenschließen geht nicht.
Aber Du kannst das mit Dioden entkoppeln (suche "oder dioden
entkoppeln")
Nächstes mal dann den ATmega324. Jetzt bin ich leider auf den ATmega32
angewiesen.
Ich hab das jetzt mit Hilfe der Dioden gelöst. Der Aufwand hält sich ja
in Grenzen.
Bliebe immer noch die Frage:
Wäre ein Oder-Gatter mit negierten (da ich ja auf die fallende Flanke
reagieren möchte) eine Alternative hierzu?
Daniel
Hm...kann mir grad wohl keiner beantworten?!
Angehängt habe ich ein Screenshot aus diesem Datenblatt:
http://www.produktinfo.conrad.com/datenblaetter/500000-524999/503655-da-01-en-HALLSENSOR_55100_3H_02_A.pdf
Kann mir hier jemand sagen was der Satz "2) Add resistor Rpu as
shown for sinking output" bedeutet? Ich nutze diesen Sensor ja als
"sinking output", wenn ich das richtig interpretiere (Pullups am
µC-Eingang aktiviert). Was bedeutet denn "sinking output"? Jedenfalls
funktioniert alles einwandfrei, auch ohne Widerstand Rpu.
Für Widerstand und Kondensator sind garkeine Dimensionierungen angegeben
Jetzt habe ich einen Kondensator mit 100nF zwischen GND und VCC
geschaltet. Welchen Wert als Widerstand würde man denn wählen und wieso?
Dankeschön im Voraus!
Daniel