Forum: Mikrocontroller und Digitale Elektronik Drehzahl-/Geschwindigkeitsmessung mit AVR


von JK (Gast)


Lesenswert?

Hallo allerseits,

zur Zeit stelle ich allerhand Überlegungen zur Realisierung eines 
Geschwindigkeitsmessers an.
Als Hardware steht ein ATMega168 zur Verfügung, der über ein PCINT mit 
einem Reed-Schalter verbunden ist.
Ziel ist es, die Zeitabstände zwischen dem Schalten des Reed-Schalters 
zu erfassen. Leider ist kein normaler Interrupt-Pin frei, sodass auf 
einen Pin Change Interrupt ausgewichen werden muss. Das erschwert die 
Erkennung der Flanken bzw. das Entprellen des Signals. Ohne jegliche 
Unterdrückung zählt der Controller ca. 10 Schaltvorgänge, wo eigentlich 
nur einer sein dürfte.

Mein größtes Problem ist aber die Erkennung der Zeitabstände. Die 
aktuellsten Überlegungen zielen dahin, bei jedem zweiten Signal einen 
16bit-Counter zu starten bzw. dann ein Signal später zu stoppen bzw. 
zurückzusetzen. Ist das eine sinnvolle Methode?
Der ATMega168 wird mit einem (timerfreundlichen) 3,6864 MHz Oszillator 
betrieben, was bei einem Vorteiler von 256 dann 14400 Counts pro Sekunde 
bringt. Aus den Anforderungen resultiert, dass im 'schlimmsten' Fall am 
PCINT alle ~ 50ms ein Signal anliegt. Mit den 14400 Counts erfasse ich 
alle 69,4µs ein Signal, habe also einen Messfehler <1%.

Während die Erfassung des Signals unter Echtzeitbedingungen 
funktionieren muss, soll die Ausgabe auf einem LCD stattfinden, was 
entsprechend langsam ist. Daher macht es eigentlich wenig Sinn die 
Geschwindigkeit für jeden Interrupt zu berechnen, sondern alle z.B. 
1/4s. Daraus würde ich eine zweite Variante erhalten, nämlich den Timer 
bis 1/4s (Beispiel) laufen lassen und in der Zeit einfach die Interrupts 
zählen. Über die Genauigkeit dieser Methode bin ich mir aber nicht 
sicher. Erschwerend kommt hinzu, dass durchaus langsame 
Geschwindigkeiten existieren, bei denen die Zeit zwischen den Interrupts 
über 1/4s beträgt.
Die abzudeckende Zeitspanne ist 50ms bis 2800ms.
Alles länger als 2800ms entspricht 0km/h und alles kürzer 50ms braucht 
nicht erfasst zu werden.

Trotz, oder gerade wegen, dieser ganzen Bedingungen und Überlegungen 
konnte ich noch auf keine vernünftige Idee kommen.

Hat jemand Ideen oder Anregungen?

von oldmax (Gast)


Lesenswert?

Hi
Drehzahlmesser... , hab ich auch grad einen in der Mangel. Sprache ASM, 
Controller Atmega8
Da ich die Genauigkeit nicht brauche und Drehzahl max. bis 5000 U/min 
geht, reicht mir das Einlesen des Impulses der Umdrehung im Polling.
Also, ein Timer zählt in einer Variablen msek. Zum Zeitpunkt Impuls wird 
der Zählerstand in eine andere Variable kopiert und der Zähler 
zurückgesetzt. Anschließend wird mit ein bischen Mathe die Umdrehung aus 
dem abgelegten Wert errechnet und auf eine 7 Segment-Anzeige umgesetzt. 
Bis hier arbeite ich in der normalen Programmschleife.
Die Anzeige wird alle 10 mSek. für eine Ziffer aktualisiert. Da keine 
Berechnungen durchgeführt werden und nur auf die abgelegten und bereits 
Codierten Werte zugegriffen wird, ist die Belastung der ISR gering. Ein 
LCD würd ich sogar auch aus der normalen Programmschleife versehen. Die 
Mathematik ür Assembler findest du in der Codesammlung.
Gruß oldmax

von JK (Gast)


Lesenswert?

Vielen Dank für deine Antwort.
Zunächst einmal möchte ich erwähnen, dass ich das Programm in C 
schreibe, da eine Einarbeitung in Assembler mir zu viel Zeit kostet.

Deine Idee scheint soweit nicht schlecht zu sein. Ich verfolge zur Zeit 
eine ähnliche Methode, allerdings will sie noch nicht so ganz...
Mal ein wenig Pseudo-C-Code, um es zu verdeutlichen:
1
uint_16t s_cnt=0, speed=0;
2
3
void main(int) {
4
  //der ganze init()-kram
5
  while(1) {
6
    //ausgabe der geschwindigkeit auf lcd
7
    lcd_write(utoa(speed)); //Pseudo-Code
8
  } 
9
  return 0;
10
}
11
12
//PCINT-ISR (pin change interrupt)
13
ISR(PCINT1_vect) {
14
  if(s_cnt > 720) { //Berechnung erst, wenn Signalabstand >50ms 
15
    speed = (int) (r_umfang * 5184) / (100*s_cnt); 
16
    s_cnt=0; //Rücksetzten des Signal-Timers
17
  }
18
}
19
20
//timer-ISR
21
ISR(TIMER0_COMPA_vect) {
22
  s_cnt++; //Signal-Timer inkrementieren
23
  if(s_cnt>=60000) { //overflow vermeiden
24
     s_cnt=0;
25
  }
26
}

Eine kurze Erläuterung des Ganzen:
Ein Timer läuft im Hintergrund mit 14400 Inkrementen pro Sekunde. Diese 
werden in einer Variable festgehalten und diese gegen Overflow 
geschützt. Ein längere Zeitspanne als 3 oder 4 Sekunden (enspricht rund 
60000) ist ohnehin nicht von Interesse, daher wird die Variable einfach 
wieder zurückgesetzt (=0).
Bei jedem Signal an PCINT1/PC0 wird überprüft, ob die Variable 
mindestens 720 beträgt, was einer Zeit von 50ms entspricht. Das dient 
als eine Art Entprellung. Ist die Variable größer, wird die Berechnung 
für die Geschwindigkeit vorgenommen, welche wiederum auf dem LCD 
ausgegeben wird.

Da die ganze Sache nicht funktioniert, lasse ich mir testweise s_cnt auf 
dem LCD anzeigen. Die Beschränkung auf < 60000 scheint nicht zu 
funktionieren, da auch größere Zahlen ausgegeben werden.
Stehe aber so sehr auf dem Schlauch, dass ich keinen Programmierfehler 
oder Denkfehler finde.

Weiß jemand weiter?

Wünsche einen entspannten 4. Advent!

von Entwickler (Gast)


Lesenswert?

>Ziel ist es, die Zeitabstände zwischen dem Schalten des Reed-Schalters
>zu erfassen
...
>Ein Timer läuft im Hintergrund mit 14400 Inkrementen pro Sekunde.

Das passt nicht gut zueinander, da die gewünschte Auflösung nicht besser 
sein kann als die Prellzeit des Sensors.
Hast Du die Möglichkeit einen Magnetfeldsensor zu nehmen? Beispielsweise 
TLE4905. Der prellt nicht.

von JK (Gast)


Lesenswert?

Die Prellzeiten bzw. das gesamte Schaltverhalten wollte ich ohnehin 
kommende Woche mit einem Oszilloskop untersuchen, habe leider keins zu 
Hause.

Theoretisch wäre es auch möglich mit einem Hallsensor zu arbeiten, dafür 
müsste ich mir aber einen bestellen, was bei den Versandkosten ein 
schlechtes Kosten/Nutzen-Verhältnis hat. Es muss doch auch mit einem 
Reedschalter gehen?!

Von dem Reedschalter (Hamlin MARR-5) habe ich ein Datenblatt, wo die 
Operate Time und Release Time angegeben sind. Laut Angabe sind die 
Zeiten inklusive Bouncing < 1ms, was locker auf meine Anforderungen 
passt. So ganz traue ich dem Braten allerdings noch nicht, da es doch 
auch darauf ankommt, wie der Magnet an dem Schalter vorbei geführt wird 
(gleichmäßige Bewegung, Zucken, Geschwindigkeit, Gerade/Schräg).

Problematisch ist bei meinem Aufbau, was ich bereits eingangs erwähnt 
habe: Es steht mir kein richtiger Interrupt mit Flankenerkennung zur 
Verfügung, da die Pins ungünstigerweise belegt sind. Somit muss ich auf 
den Pin Change Interrupt ausweichen, der natürlich für jede Flanke 
auslöst. Mit der Software habe ich noch keine geschickte Lösung finden 
können, um nur die negative Flanke zu detektieren.

von Entwickler (Gast)


Lesenswert?

>Mit der Software habe ich noch keine geschickte Lösung finden
>können, um nur die negative Flanke zu detektieren.

Immer den letzten Zustand des Schalters im Interrupt speichern:

akt_zustand = EINGANG;
zustand = (akt_zustand ^ alter_zustand) & akt_zustand;
alter_zustand = akt_zustand;

zustand == 1: Eingangspin = 1, sonst 0.


Wie wäre es mit einer Lichtschranke? Auch zu teuer? :-)

von JK (Gast)


Lesenswert?

Die XOR-AND-Methode ist prima, funktioniert sehr gut!

Eine Lichtschranke geht prinzipiell natürlich auch, ist nur empfindlich 
gegen Verschmutzung. Der Kostenfaktor wäre grundsätzlich kein Problem. 
Da es sich hierbei aber um ein Lern-Projekt handelt und es in der Praxis 
wohl so nie eingesetzt wird, lohnt sich die Anschaffung von 
aufwändigerer Hardware nicht wirklich.
Aufbauend könnte man anschließend natürlich relativ einfach eine 
Implementierung umsetzen, aber das ist Zukunftsmusik. :)

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.