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?
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
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_16ts_cnt=0,speed=0;
2
3
voidmain(int){
4
//der ganze init()-kram
5
while(1){
6
//ausgabe der geschwindigkeit auf lcd
7
lcd_write(utoa(speed));//Pseudo-Code
8
}
9
return0;
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!
>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.
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.
>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? :-)
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. :)