Hallo zusammen. Ich bräuchte Hilfe bei einem kleinen Projekt. Mit einem Durchflussmesser möchte ich eine kleine Wasserpumpe überwachen ob sie noch genug Pumpleistung bringt. Laut meiner Rechnung bringt der Durchflussmesser ca. 22Impulse/s. Ich verlange jetzt keine komplette vorgekaute Lösung aber ein paar Denkanstöße wären sehr hilfreich. Die geförderte Wassermenge möchte ich dann mit einem LCD anzeigen. Wie kann ich jetzt diese Impulse zählen um sie dann auszuwerten? Muss ich das Signal an einem Pin mit ext. Interrupt anschließen oder an einem "normalen" Eingang und diesen dann kontinuierlich abfragen und bei Änderung von 0 auf 1 einen Zähler laufen lassen?
Das hängt alles vom Sensor ab... Vielleicht mal ein Typ oder ein Datenblatt für uns?
Ich würde den Sensor wie in Schaltbild 2 (TTL Output) anschließen und mit einem Pin Change Interrupt am µC zählen. Also ja, an einem Pin mit Interrupt. Bei den AVRs haben mittlerweile allerdings fast alle µC die Möglichkeit ein PCINT an jedem Pin durchzuführen.
Ich habe Schaltbild 1 gewählt (simple circuit) und das Signal am PB2 (INT2) angeschlossen. Wird es so nicht funktionieren? Jeder Flankenwechsel löst also einen Interrupt aus. Und wie messe ich dann die Zeit zwischen zwei Interrupts?
Tobias schrieb: > Und wie messe ich dann die Zeit zwischen zwei Interrupts? Stichwort Input-Capture, schau mal im Datenblatt, was man mit dem ICP1 Pin machen kann.
Tobias schrieb: > Ich habe Schaltbild 1 gewählt (simple circuit) und das Signal am PB2 > (INT2) angeschlossen. Wird es so nicht funktionieren? Jeder > Flankenwechsel löst also einen Interrupt aus. Und wie messe ich dann die > Zeit zwischen zwei Interrupts? Je nachdem wie du den Sensor versorgst, könnte hier die Spannung zu hoch sein... Du kannst auch mit einem Timer 1 Sekunde einstellen und dann zählen wie viele Pulse in dieser Zeit kommen. Da du ja 22 Pulse pro sek maximal erhältst ist die Zeit zwischen den Captures zu messen evtl mit mehr Rechenaufwand verbunden. Geht aber natürlich auch.
Tobias schrieb: > Muss ich das Signal an > einem Pin mit ext. Interrupt anschließen oder an einem "normalen" > Eingang und diesen dann kontinuierlich abfragen und bei Änderung von 0 > auf 1 einen Zähler laufen lassen? Ein externer Interrupt wäre eine Lösung, alternativ könntest du aber auch einen Pin-Change-Interrupt benutzten. Beim Atmega32 sind die IOs auf, ich glaube, drei Pin-Change-Interrupt-Gruppen aufgeteilt. Schau dir dazu am besten mal das Datenblatt an. Tobias schrieb: > Jeder > Flankenwechsel löst also einen Interrupt aus. Und wie messe ich dann die > Zeit zwischen zwei Interrupts? Indem du umstellst und den Interrupt nur auf steigende oder fallende Flanken sensitivierst. Die Zeit zwischen zwei Interrupts kannst du z.B. mit einem Timer messen ;)
Dennis K. schrieb: > Je nachdem wie du den Sensor versorgst, könnte hier die Spannung zu hoch > sein... Der Sensor hängt an den gleichen 5V wie der Controller Mit Interrupts habe ich mich jetzt beschäftigt aber auf eine Lösung für mein Problem bin ich leider immer noch nicht gekommen
M. K. schrieb: > Indem du umstellst und den Interrupt nur auf steigende oder fallende > Flanken sensitivierst. Die Zeit zwischen zwei Interrupts kannst du z.B. > mit einem Timer messen ;) das heißt ich schreibe in den ISR einen Timer mit entsprechender Frequenz, so das er nicht überläuft. Und wenn ich den Timer auslese erhalte ich in abhängikeit der eingestellt Frequenz die Zeit für einen Takt und kann dann auf Takte pro Minute umrechnen.
Fängt der Timer dann jedes mal beim Interrupt bei 0 an zu zählen oder muss ich den erst reseten?
Also mein Lösungsansatz währe jetzt so: Steigende Flanke am Eingang startet einen Interrupt --> Inhalt der Variable "wert" auf Variable "Anzahl" übertragen und anschließend "wert" auf 0 setzen, timer reset und timer starten --> Mit einem Overflow Interrupt zähle ich mit "wert"++ die Anzahl Überläufe Wenn ich jetzt die Variable "Anzahl" mit 256 multipliziere und durch die CPU Frequenz teile müsste ich doch die Zeit für einen Impuls haben????
Man kann auch einen Timer/Counter vom Sensor takten lassen (Stichwort 'extern Clock') und z.B. jede Sekunde den Counterwert lesen und zurücksetzen. Kostet zwei Timer/Counter aber läuft so gut wie von alleine.
:
Bearbeitet durch User
Rudolph schrieb: > Stichwort Input-Capture, schau mal im Datenblatt, was man mit dem ICP1 > Pin machen kann. ... Und mal so als allgemeiner Tipp, die AVRs sind zwar an sich ohnehin wohl leider kurz vor Ihrem Ende, der Mega32 ist aber auch noch ein besonders altes Exemplar der Familie. Ein Mega324PA wäre ein Pin-kompatibler Ersatz mit etwas mehr Möglichkeiten.
Ja ich merke gerade dass mir beim Atmega32 einige Funktionen fehlen. Kann ich den Mega324PA einfach einsetzen ohne umlöten zu müssen?
Tobias schrieb: > Kann ich den Mega324PA einfach einsetzen ohne umlöten zu müssen? Rudolph schrieb: > Ein Mega324PA wäre ein Pin-kompatibler Ersatz mit etwas mehr > Möglichkeiten. Bei der geringen Frequenz des Signals kann man auch einen Timer aus z.B. 1 ms einstellen und einen bel. Pin pollen und beim wechsel von Low nach HIGH einen Zähler hochsetzen. Das geht ohne Pin-Interupt.
Schau dir mal diese Projekt an sollte, Softwaretechnisch angepasst, eine mögliche Lösung aufzeigen. Beitrag "Genaue Ölmengenmessung Heizung" Impuls lost Interupt aus dieser wird gezählt und mit dem Durchfluss pro Impuls multipliziert. Das Ergebniss wird dann angezeigt. Gruß Janoschik
Man macht das mit dem Input Capture mode eines Timers. Wenn man die ausloesende Flanke im Interrupt des Capture Ereignis umstellt bekommt man sogar eine hoehere Messrate. Das einzige was man noch beachten muss ist einen Timeout einzubauen der Erkennt oder der Sensor steht also keine Pulse mehr kommen. Der Rest ist dann ein bisschen Rechnen, aber das macht der uC gerne fuer einen.
Tobias schrieb: > das heißt ich schreibe in den ISR einen Timer mit entsprechender > Frequenz, so das er nicht überläuft. Und wenn ich den Timer auslese > erhalte ich in abhängikeit der eingestellt Frequenz die Zeit für einen > Takt und kann dann auf Takte pro Minute umrechnen. So ähnlich. Mein Ansatz wäre wohl so: Pin-Change-Interrupt und Timer-Overflow-Interrupt einstellen, Variable generieren, mit der die Timer-Overflows gezählt werden. Warten auf den ersten Pin-Change-Interrupt, der startet den Timer. Dann wartet man auf den zweiten Pin-Change-Interrupt. Wenn der zweite Pin-Change-Interrupt kommt erfasst man den aktuellen Timer-Wert inclusive aller Overflows und setzt die Zählvariable für Overflows und den Timer-Counter auf 0 zurück. Im Hauptprogramm kann man nun die Zeit ausrechnen lassen und zur Anzeige bringen oder was man sonst damit machen will. Man muss sich hier noch eine Abbruchbedingung überlegen, wie lange mal also auf den zweiten Puls warten will. Es gibt ja sicher eine Zeit wo das keinen Sinn mehr macht auf den zweiten Puls zu warten.
Rudolph schrieb: > Und mal so als allgemeiner Tipp, die AVRs sind zwar an sich ohnehin wohl > leider kurz vor Ihrem Ende,... Was soll das denn heißen?
Christian S. schrieb: > Was soll das denn heißen? Das heist er sitzt beim CEO von Microchip auf dem Schoss und weiss alles.
Christian S. schrieb: >> Und mal so als allgemeiner Tipp, die AVRs sind zwar an sich ohnehin wohl >> leider kurz vor Ihrem Ende,... > > Was soll das denn heißen? Sorry, der Hinweis ist einfach Off-Topic. Ich mache nur wenig Hoffnung auf weiter-Entwicklung der AVRs, nachdem Atmel von Microchip übernommen wurde. Es wird langsam Zeit, sich was anderes zu suchen, ich suche auch schon länger, nur gefunden habe ich noch nichts mit dem ich die AVRs ersetzen wollen würde. Nur, Mega8/Mega16/Mega32 hatte ich vor >10 Jahren schon im Einsatz und vor ich weiss nicht wie vielen Jahren durch Mega88PA/Mega164PA/Mega324PA ersetzt.
codebeispiel für atmega328:
1 | dle_init(){ |
2 | DDR(dle_port)&=~(1<< dle_pin); |
3 | PORT(dle_port)|=((1<< dle_pin)); |
4 | PCICR |= (1 << PCIE(dle_int_port));//PCIntControlRegister |
5 | PCMSK(dle_int_port) |= (1<< dle_pin); |
6 | sei(); |
7 | }
|
8 | //--------------------------------------------------
|
9 | |
10 | ISR(dle_vect){ |
11 | //760imps=>1l, 12,6imps/sec
|
12 | dle_isr_imp++; |
13 | }
|
14 | |
15 | //--------------------------------------------------
|
16 | |
17 | isr_dle50_start(){ |
18 | PCMSK(dle_int_port) |= (1 << dle_pin); |
19 | }
|
20 | |
21 | //--------------------------------------------------
|
22 | |
23 | isr_dle50_stop(){ |
24 | PCMSK(dle_int_port) &=~(1 << dle_pin); |
25 | dle_l_hou=(dle_isr_imp*10+33)/66; |
26 | lg(4,7);li(dle_l_hou);lw("l"); |
27 | dle_isr_imp=0; |
28 | }
|
29 | |
30 | //==================================================
|
31 | //in der main:
|
32 | |
33 | |
34 | if(sec==8){ |
35 | isr_dle50_start(); |
36 | }
|
37 | |
38 | |
39 | if(sec==58){ |
40 | isr_dle50_stop(); |
41 | }
|
Ich hab jetzt mal den Weg versucht über externen Interrupt einen Timer zu starten und die Überläufe zu zählen bis die nächste Flanke kommt aber es funktioniert nicht. Ohne Oszi ist es hald auch schwer zu sagen ober überhaupt ein ordentliches Eingangssignal anliegt. Eigentlich müsste doch die Variable hochzählen und dann von der if in die else schleife gehen. Kann da mal jemand einen blick darauf werfen? Vielleicht fällt ja nem Könner gleich was auf was nicht passt-.
gibt es irgendeine Möglichkeit zu testen ob der Interrupt überhaupt ausgelöst hat?
Tobias schrieb: > Vielleicht fällt ja > nem Könner gleich was auf was nicht passt ja. > uint8_t anzahl = 5; anzahl muss volatile sind. Warum lässt du das Programm nicht einfach mal im Simulator laufen, dann wüsstest du wenigsten ob die Software richtig ist.
Peter II schrieb: > Warum lässt du das Programm nicht einfach mal im Simulator laufen, dann > wüsstest du wenigsten ob die Software richtig ist. im Simulator häng ich dann irgendwann in der while Schleife die den Text auf LCD ausgibt. Ohne Signal dass den Interrupt auslöst komm ich da nicht raus.
Hier mal ein Beispiel von mir. Ausgewertet wurde hier ein Tachosignal und je nach Geschwindigkeit wird hier eine Wicklung eines EMotors umgeschaltet (für größeren Speed ;)).
1 | /* Name: main.c
|
2 | * Author: Michael Koehler
|
3 | */
|
4 | |
5 | #include "main.h" |
6 | #include <avr/wdt.h> |
7 | |
8 | int main(void) |
9 | {
|
10 | setUpAvr(); |
11 | |
12 | for(;;){ |
13 | /* insert your main loop code here */
|
14 | wdt_reset(); |
15 | }
|
16 | return 0; /* never reached */ |
17 | }
|
18 | |
19 | void setUpAvr(void){ |
20 | /* insert your hardware initialization here */
|
21 | // output set
|
22 | DDRB |= (1 << PB1); |
23 | // int0 set
|
24 | // int0 interrupt at rising edge
|
25 | MCUCR |= (1 << ISC01) | (1 << ISC00); |
26 | // int0 interrupt on
|
27 | GIMSK |= (1 << INT0); |
28 | // timer1 overflow interrupt
|
29 | TIMSK |= (1 << TOIE1); |
30 | // watchdog
|
31 | wdt_enable(WDTO_60MS); |
32 | wdt_reset(); |
33 | // enable interrupts
|
34 | sei(); |
35 | }
|
36 | |
37 | ISR(INT0_vect){ |
38 | if (TCNT1 == 0) { |
39 | //start timer
|
40 | TCCR1 |= (1 << CS13) | (1 << CS10); |
41 | } else { |
42 | if (TCNT1 < 144) { |
43 | PORTB |= (1 << PB1); |
44 | } else if (TCNT1 > 164){ |
45 | PORTB &= ~(1 << PB1); |
46 | }
|
47 | TCNT1 = 0; |
48 | |
49 | }
|
50 | }
|
51 | ISR(TIMER1_OVF_vect){ |
52 | PORTB &= ~(1 << PB1); |
53 | TCCR1 &= ~((1 << CS13) | (1 << CS10)); |
54 | TCNT1 = 0; |
55 | }
|
Ich hab zwar mit INT0 hier gearbeitet, das geht aber genauso mit beliebigen PC-Interrupt. Bei mir kam/kommt ein ATTiny85 mit 1 MHz Takt zum Einsatz, nicht weil er perfekt dafür geeignet ist sondern weil das mein Standard-8-Pin-AVR ist von dem ich noch um die zehn Stück in der Schublade hab.
:
Bearbeitet durch User
Tobias schrieb: > Ich hab jetzt mal den Weg versucht über externen Interrupt einen Timer > zu starten und die Überläufe zu zählen bis die nächste Flanke kommt aber M. K. schrieb: > Hier mal ein Beispiel von mir. Wie kommt Ihr alle auf steigende Flanke ? Soviel ich sehe, ist es die fallende Flanke, weil Vcc im Ruhezustand.
Marc V. schrieb: > Soviel ich sehe, ist es die fallende Flanke, weil Vcc im Ruhezustand. hö, 1. hab ich kein Pull-Up an und 2. macht
1 | ...
|
2 | MCUCR |= (1 << ISC01) | (1 << ISC00); |
3 | ...
|
das Ganze auf eine steigende Flanke sensitiv (Datasheet ATTiny85, Page 51). Wie du da jetzt darauf kommst, es sei auf fallende Flanke sensitiv versteh ich grade nicht.
M. K. schrieb: > Wie du da jetzt darauf kommst, es sei auf fallende Flanke sensitiv > versteh ich grade nicht. Weil im Datenblatt vom Hersteller ein OC Ausgang mit Pullup nach VCC eingezeichnet ist. Da ist es für mich logisch auf fallende und nicht auf steigende Flanke zu reagieren. Ihr könnt das natürlich machen wie es euch beliebt.
Marc V. schrieb: > Weil im Datenblatt vom Hersteller ein OC Ausgang mit Pullup nach > VCC eingezeichnet ist. Den Pull-Up musst du aber einschalten was ich, wie man auch oben sehen kann, nicht mache. Ohne das ist der Eingang unbestimmt (kann High oder Low sein). Da der Drehzahlgeber den Eingang bei mir eh auf GND zieht und nur 12V Pulse bei drehenden Rad ausgibt ist es gar nicht so verkehrt auf eine steigende Flanke zu reagieren. Und für eine Pulsfolge von einem Drehzahlgeber ist es idR auch völlig egal ob man auf steigende oder fallende Flanken reagiert da die Pulse und Pausen idR immer gleich lang sind.
:
Bearbeitet durch User
M. K. schrieb: > Hier mal ein Beispiel von mir. Ausgewertet wurde hier ein Tachosignal > und je nach Geschwindigkeit wird hier eine Wicklung eines EMotors > umgeschaltet (für größeren Speed ;)). Vielen Dank für das Beispiel. Ich glaube mein Problem ist momentan einfach das, dass ich den INT2 nicht zum laufen bringen. Laut Datenblatt gibt es ihn zwar und man kann über GICR auch die Bits dafür setzen aber bei MCUCR tauchen dann nur noch bits für INT0 und INT1 auf. Ich lese das Datenblatt jetzt zum x-ten mal aber ich werd nicht schlauer und INT1 und 0 sind bei mir leider schon durch das LCD belegt.
Tobias schrieb: > einfach das, dass ich den INT2 nicht zum laufen bringen. Laut Datenblatt > gibt es ihn zwar und man kann über GICR auch die Bits dafür setzen aber > bei MCUCR tauchen dann nur noch bits für INT0 und INT1 auf. Ich lese das > Datenblatt jetzt zum x-ten mal aber ich werd nicht schlauer und INT1 und > 0 sind bei mir leider schon durch das LCD belegt. MCUCSR ist für INT2 zuständig MCUCSR |= (1<<ISC2) = INT2 auf steigende Flanke GICR |= (1<<INT2) = INT2 aktivieren.
Fang mal damit an, den pin abzufragen, ob überhaupt ein Signal ankommt, danach den interrupt aktivieren: if(!(PINx<<piny)){ led_on;_delay_ms(50); } else { led_off; } Müsste im Takt der Sensorimpulse blinken.
grundschüler schrieb: > Fang mal damit an, den pin abzufragen, ob überhaupt ein Signal ankommt, > danach den interrupt aktivieren: Wozu? Wenn kein Signal kommt, kommt kein Interrupt. Das Aktiveren des Interrupts kosten einen nichts und eine LED kann man auch im Interrupt tooglen, ohne mit einem Delay() den Prozessor lahm zu legen.
um denb Wolfgang schrieb: > Wozu? Fehlersuche. zuerst im Hauptprogramm, dann in der isr. Die Verzögerung, damit kurze Impulse per LED sichtbar werden.
Also vielen Dank für die vielen antworten. Wird ich gleich mal ausprobieren. Jetzt noch ne doofe Frage: Den Eingang an dem das Signal ankommt, muss ich den auf high oder low setzen?
Tobias schrieb: > Jetzt noch ne doofe Frage: Den Eingang an dem das Signal > ankommt, muss ich den auf high oder low setzen? Klare Antwort: weder noch. Ggf. könnte es allerdings sinnvoll sein, dem Eingang einen PullUp- oder PullDown-Widerstand zu verpassen, das hängt von der Signalquelle ab. Und wenn es ein PullUp sein muss und dafür der eingebaute PullUp eines AVR verwendet werden kann, ja dann könnte es sinnvoll sein, das entsprechende Bit des Portregisters auf High zu setzen, weil das nämlich eben diesen PullUp aktiviert. Jedenfalls so lange das entsprechende Bit im zuständigen DDR-Register Low bleibt, der Pin also als Eingang konfiguriert bleibt.
Tobias schrieb: > grundschüler schrieb: >> if(!(PINx<<piny)){ > > was bedeutet diese Zeile? du fragst das PIN-Register ab ob der enstsrechende Pin low ist. Muss allerdings dann >> if(!(PIN(x)&(1<<y))){ heißen.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.