Hallo Leute! Ich will mit diesem Programm Drehzahl messen. Der externe Interrupt macht mir aber Probleme. Ich starte das Programm und die LED (PORTD6) schaltet sich ein und aus. Ich sende 2 mal ein "n" von meinem PC an den µC und die Drehzahl wird auch zum PC gesendet. Nach diesem Ablauf hört die LED aber auf zu blinken. D.h. der Atmega reagiert auf den externen Interrupt nicht mehr. Woran kann das liegen. Falls ich ein paar wichtige Informationen vergessen habe, einfach posten. Mit freundlichen Grüßen Markus
Versuch mal, das UDR direkt in der Interruptroutine auszulesen, ich glaube sonst kommt der Interrupt immer wieder, auch wenn keine neuen Daten auftauchen.
Danke für die rasche Antwort. Ich habe deinen Tip gleich mal angewandt. Mein Programm funktioniert zwar noch nicht so wie ich es mir vorstelle, aber das Problem, dass der Interrupt nicht mehr auslöst, ist geschichte!!! Ich danke dir sehr. mfg Markus
So nach deinem Tip hat der externe Interrupt weiterhin auch nach Auftreten eines seriellen Interrupts ausgelöst. Jetzt wollte ich das Programm weiter optimieren und den Timer zu starten und den ext. Interrupt zu aktivieren, wenn ein "n" vom PC gesendet wird. Sollablauf: n wird empfangen und externer Interrupt wird aktiviert. Externer Interrupt löst aus und schaltet den Timer ein. Dort wird die Variable counter(volatile) bei jedem Interrupt um eins erhöht. Bei dem zweiten Auslösen des externen Interrupts wird die Drehzahl berechnet (Zeit des Timers * counter = Periodendauer => Drehzahl). Dann soll die Drehzahl(n_ist) an den PC gesendet werden. Drehzahl ist immer -1. Ich habe auch die Variable counter mal gesendet und diese ist 0. Das würde -1 erklären als Ergebnis von (240000/0). Das würde bedeuten der Timer löst nicht aus. Ich finden keine Erklärung. Ich bitte wieder um Hilfe. mfg Markus
Markus Kammerhofer schrieb: > So nach deinem Tip hat der externe Interrupt weiterhin auch nach > Auftreten eines seriellen Interrupts ausgelöst. Nur um das richtig zu stellen. Er hat auch vorher schon ausgelöst. Nur hat dein ständig aufgerufener RXC Interrupt keine Rechenzeit mehr übrig gelassen um irgendetwas anderes zu bearbeiten.
Hier
1 | while(n_berechnet == 0) |
2 | {
|
3 | }
|
kommt dein Programm nie wieder raus, weil n_berechnet nicht im Interrupt geändert wird. Wie schnell kommen denn eigentlich deine Lichtschrankensignale? Und wie schnell läuft der Timer?
Wenn ich mir deinen Code so ansehe, habe ich das starke Gefühl du trickst dich im Moment mit deinen vielen Flags (und ob die jetzt 0 oder 1 sein müssen/sollen) gewaltig selber aus. Du errichtest dir hier eine Komplexität die nicht notwendig ist. Das Geflecht, welches Flag wie stehen muss, damit an anderer Stelle was passiert, wird schön langsam immer undurchschaubarer. Variablennamen ala hs_a, hs_n, a_hilf tragen dann auch nicht gerade dazu bei, das etwas durchschaubarer zu machen. Auch wenn es immer heißt: Interrupt Routinen so kurz wie möglich, so muss man das auch nicht als Dogma sehen. Ein bischen Arbeit darf man schon auch in einer Interrupt Routine machen lassen. So darf eine Interrupt Routine ruhig auch die ermittelte Zeit in eine Drahzahl umrechnen. Die Hauptschleife liefert dann bei entsprechender Anfrage über UART einfach den zuletzt ermittelten Wert. Das andere ist, dass du unnötigerweise deine ganzen Flags als int machst und dadurch dem Prozessor unnötige 16 Bit Operationen aufzwingst, wo es 8 Bit Operationen auch tun. Dann müsstest du dir um atomare Absicherung der Zugriffe auch keine Gedanken machen. Aber das dürfte momentan noch gar nicht das Problem sein. Alles in allem ist der Code so verworren, dass ich ihn neu schreiben würde, wenn ich den übernehmen müsste.
Also ich danke dir für diesen Ratschlag. Der Punkt ist dass ich solch ein großes Programm wie in dieser HTL - Diplomarbeit noch nie schreiben musste. Ich habe also keinerlei Erfahrung. Jetzt bin ich mal eine Woche weg und werde dann mit Hilfe eines Flussdiagrammes versuchen eine funktionierende Lösung zu finden. Mfg Markus
So ich bin zurück aus dem Urlaub. Ich habe jetzt begonnen den Code neu zu schreiben. Werde es diesmal so machen, dass ich Teil für Teil versuche. Der 1. Teil ist jetzt die Drehzahlmessung. ISR(INT0_vect) { if(messen == 0) { TCNT2 = 0; counter = 0; //TCCR2 |= (1<<CS21); TIMSK |= (1<<OCIE2); messen = 1; } else if(messen == 1) { //TCCR2 &= ~(1<<CS21); TIMSK &= ~(1<<OCIE2); n_ist = 240000/counter; GICR &= ~(1<<INT0); sprintf(string, "%d\r", counter); uart_puts(string); messen = 0; } } ISR(TIMER2_COMP_vect) { counter++; } So der Rest des Programmes funktioniert. Über die serielle Schnittstelle wird der externe Interrupt aktiviert. Das Programm reagiert somit 2 mal auf den externen Interrupt, dann wird er wieder deaktiviert. Zwischen den beiden Interrupts sollte der Timer die Variable Counter hochzählen. Alle Variablen die in den Interrupts verwendet werden sind Volatile. Der Wert von Counter bewegt sich zwischen 50 und 300. Die Drehzahl wird aber nicht verändert. Timerzyklus dauert 0,000125 Sekunden. Systemclock = 16MHz Die Zeit die zwischen den beiden Interrupts vergeht beträgt 0,0502 Sekunden bei 600rpm. counter sollte somit ca. 400 ergeben. Ich hoffe ihr könnt mir wieder helfen. mfg
Markus Kammerhofer schrieb: > Alle Variablen die in den Interrupts verwendet werden sind Volatile. Der > Wert von Counter bewegt sich zwischen 50 und 300. Ich nehme an, das weißt du wegen dem sprintf/uart_puts der jetzt noch temporär in der ISR sitzt und dann wieder rausfällt. Auch wenn ich vorher gesagt habe, dass man in einer ISR schon auch etwas machen darf, Ein/Ausgaben auf LCD/UART sind meistens zuviel des Guten. Die dauern einfach zu lange. Dadurch werden Interrupts zu lange blockiert und man verpasst den einen oder anderen Interrupt. Du bist jetzt von einem Extrem ins Gegenteil verfallen. > Die Drehzahl wird aber > nicht verändert. woher weißt du das? > Ich hoffe ihr könnt mir wieder helfen. Zeig immer möglichst vollständigen Code. Zeig auch immer die Datentypen der beteiligten Variablen.
Ich habe einen Gleichstrommotor an einem Netzteil mit konstanter Spannung angehängt. Der Motor dreht im Leerlauf. Da ist die Drehzahl weitesgehend konstant. Die Werte von Counter sind ja vollkommen falsch. So jetzt habe ich versucht die ISR so kurz wie möglich zu halten. Das Ergebnis bleibt aber das selbe. Es dürfte jedoch stimmen, dass der Timer2Interrupt teilweise blockiert wird weil die counter-Variable ist immer kleiner als der Normalwert und nie größer. In der ISR wird nun nur noch der Timer ein und ausgeschaltet. Leider resultiert daraus eine if-Verzweigung. Deshalb habe ich jetzt den gesamten Code gesendet. Ist die if-Verzweigung auch zu lang oder ist etwas anderes falsch? Ich habe das Programm auch mal versucht so zu verändern, dass bei jedem INT0 die Drehzahl berechnet wird und der Timer somit nie gestoppt wird. Da funktioniert die Messung tadellos. Aber einen Timer mit 0,000125s ständig laufen zu lassen und den INT0 immer aktiviert zu haben, wird denke ich auf Dauer zu Problemen führen, weil das Programm noch um einiges größer werden wird. mfg
Markus Kammerhofer schrieb: > Ich habe einen Gleichstrommotor an einem Netzteil mit konstanter > Spannung angehängt. Der Motor dreht im Leerlauf. Da ist die Drehzahl > weitesgehend konstant. Die Werte von Counter sind ja vollkommen falsch. OK. Das hattest du vorher so noch nicht gesagt :-) > Ist die if-Verzweigung auch zu lang oder ist etwas anderes falsch? Das wird dir nicht gfallen. Die ganze Systematik ist sch...e. Sowas macht man nicht mit externen Interrupts u.dgl sondern mit einem Input Capture vom Timer und zugehörigem Interrupt. Sieh dir mal an, wie Frequenzzähler arbeiten. Du baust im Grunde auch nichts anderes. Code für sowas findet sich zuhauf im Forum. > Ich > habe das Programm auch mal versucht so zu verändern, dass bei jedem INT0 > die Drehzahl berechnet wird und der Timer somit nie gestoppt wird. Da > funktioniert die Messung tadellos. Aber einen Timer mit 0,000125s > ständig laufen zu lassen und den INT0 immer aktiviert zu haben, wird > denke ich auf Dauer zu Problemen führen, weil das Programm noch um > einiges größer werden wird. Was hat das damit zu tun. die ISR verbraucht ein paar Prozentpunkte Rechenzeit, wenn überhaupt. Nichts gravierendes, solange du keine großartigen Ausgaben machst.
OK. Ich habe mich jetzt ein wenig eingelesen. Nur fürs Verständnis. Ich hänge meine Lichtschranke an den ICP1-Pin. TCNT1 zählt aufwärts bis eine Flanke am ICP1-Pin ankommt. ICP1 * N/f_clkI/O ergibt somit die Periodendauer einer Umdrehung. Falls 2^16 nicht ausreicht (n ist zu klein) verwendet man den Timer1 Overflow Interrupt. In dieser ISR lasse ich eine Zählvariable laufen, sagen wir mal x. In dem ICP1 Interrupt berechne ich dann die Periodendauer mit dieser Formel: (x * 2^16 + ISP1)*N/f_clkI/O. Ich hoffe das ist richtig wie ich mir das denke. Aber wie kann ich x*2^16 rechnen? Das ergibt ja gigangtische Werte. mfg
Markus Kammerhofer schrieb: > OK. Ich habe mich jetzt ein wenig eingelesen. Nur fürs Verständnis. Ich > hänge meine Lichtschranke an den ICP1-Pin. TCNT1 zählt aufwärts bis eine > Flanke am ICP1-Pin ankommt. Genau. Der springende Punkt ist, dass dir die Capture Hardware den zum Ereignis gehörenden Timer Wert taktgenau wegsichert. Du hast daher im Interrupt alle Zeit der Welt (bis zum nächsten Capture) um diesen Wert aufzuarbeiten. > ICP1 * N/f_clkI/O ergibt somit die > Periodendauer einer Umdrehung. Ich interpretiere das mal als: Genau! > Falls 2^16 nicht ausreicht (n ist zu > klein) verwendet man den Timer1 Overflow Interrupt. In dieser ISR lasse > ich eine Zählvariable laufen, sagen wir mal x. In dem ICP1 Interrupt > berechne ich dann die Periodendauer mit dieser Formel: (x * 2^16 + > ISP1)*N/f_clkI/O. Kann man machen. Iregndwo gibt es immer eine Grenze an der man anstelle der Periodendauer die Anzahl Pulse pro Zeiteinheit misst. Ist eine Frage der Genauigkeit bzw. Messdauer. Man kann ja auch einfach den Vorteiler des Timers höher drehen. > Ich hoffe das ist richtig wie ich mir das denke. > Aber wie kann ich x*2^16 rechnen? Das ergibt ja gigangtische Werte. long ist in C schon erfunden. Und eine Multiplikation mit 2^16 ist auf Assemblerebene eher trivial und sieht nur im C Code etwas wild aus. In der ISR die Periodendauer aus der Differenz zum vorhergehenden Wert ausrechnen. Die tatsächliche Drehzahl kann man dann in aller Ruhe in der Hauptschleife berechnen und irgendwo anzeigen.
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.