So, zum zweiten Versuch. Ich möchte mir einen manuellen BPM-Counter und Lauflicht in einem basteln. Per Taster soll der Takt vorgegeben werden, danach soll das Lauflicht im Takt weiter laufen. Das ganze habe ich mit einem Atmel ATmega8 der mit einem 8 Mhz Quarz läuft aufgebaut. In meinem Fall besteht die Lauflichtaktion nur aus einem einfachen Teil das eine LED an Port B zum blinken bringt, reicht ja auch für den Anfang bis ich den rest programmiert habe. Der Timer1 läuft im CTC-Modus und löst alle 0,1ms einen Interrupt aus. Der Taster1 an PD2 löst ebenfalls einen Interrupt aus, genau wie der Taster2 an PD3 der nur dazu dient die Lauflichtfunktion auf Pause zu schalten. Drückt man auf den Taster1 wird im Programmeigenen Statusregister das cnt-flag gesetzt und ein Counter in gang gesetzt der nach dem Timer alle 0,1ms eins hoch zählt. Beim nächsten drücken des Taster1s wird nun die verstrichene Zeit des Counters zum Zeispeicher dazuaddiert und der Counter gelöscht. Ausserdem wird der Divisor inkrementiert und damit eine Division durch den Zeitspeicher durchgeführt. Somit erhalte ich die durchschnittliche Zeit eines Takts, und das eigentlich ziemlich genau weil ich ca 6,5 Sekunden Zeit hab (Bis das 16-bit Zeitspeicherregister beim Wert ~65000 überläuft) um den Takt vorzugeben. Läuft der Zeitspeicher über wird das ovf-flag gesetzt und die rote status-led eingeschaltet. Hier dient der Taster1 dann nur noch zum Syncronisieren des Takts. Hört man zwischendurch auf den Takt weiter einzugeben wird cnt nach 1 Sekunde wieder auf 0 gesetzt. Drückt man den Taster1 nur ein mal dient er nur dazu den Takt zu syncronisieren. Was noch kommen soll sind grafische Ausgabe der BPM mit 7-Segment-anzeigen und eben die Lauflichtfunktion. Zum debuggen habe ich an PC0-4 mal 5 LEDs angeschlossen an denen ich derzeit den Divisor ausgebe. Sollte eigentlich prima funktionieren, leider blinkt die LED etwas zu schnell so dass ich nach ein paar Sekunden schon wieder ausser Takt läuft. Ich frage mich nun warum, ich schließe den Fall aus dass ich zu blöd bin den Takt einzugeben da sowieso nur die verstrichene Zeit zwischen dem ersten und dem letzten Tastendruck zählt und die Anzahl der Tastendrücke dazwischen. Im übrigen läuft sie immer zu schnell, niemals zu langsam, vorrausgesetzt ich gebe den Takt richtig ein. Gefühlsmäßig läuft sie auch immer so um den gleichen faktor zu schnell. Ich frage mich nun woran das liegt :-/ Tut mir leid dass der Code so umfangreich ist aber ich glaube es hätte nichts gebracht wenn ich den code auseinandergepfriemelt hätte, da wäre nicht viel weg gefallen. Danke im vorraus an alle die sich damit auseinandersetzen! Im Anhang befinden sich das AVR-Studio Project samt Assemblercode, ein Programmablaufplan und ein Bild vom Testaufbau. Diese LED Links ist meien Status-LED. Eine DUO-LED die je nach ihrer Polung entweder Grün oder Rot leuchtet. mfg Paul H.
hm, ich werde da jetzt nicht durchsteigen. Auf jeden Fall solltest du das sreg in den Interruptroutinen sichern, ist zwar im Moment noch kein Problem, da deine main nur aus jmp main besteht. Kommt da was hinzu, gibts Murks. Schon mal über Einsatz einer Hochsprache nachgedacht? Ansonsten kan ich dir nur raten, das AVR-Studio richtig zu benutzen. Dort gibts einen Cycle-Counter, der dir ganz genau sagt, wieviel Takte das ganze dauert. Z.B. erst mal einen Breakpoint auf den Timer-Int, dann siehst du, ob der tatsächlich in der von dir gewollten Zeit auslöst. Das kann man weiter betreiben, indem du deine Software-Zähler überprüfst. Und dann bliebe noch die Variante, dass du mit dem internen RC-Osz arbeitest, Fuses überprüfen.
Hi, danke erstmal für die Antwort. Ja, habe schon daran gedacht das SREG zu sichern, habe das aber bewusst weggelassen um unnötigen Code erstmal weitestgehend zu vermeiden. Ja, hatte schonmal daran gedacht das ganze in C zu programmieren aber ich wollte meine ersten Schritte zunächst mal mit Assembler machen, finds auch interessanter so :-) Also hab das ganze mal teilweise im AVRstudio getestet. Der Timer löst exakt bei jeder 1/10 ms einen Interrupt aus, das stimmt also. Die Fuses hab ich nach Datenblatt richtig gesetzt. Die Frequenz stimmt. Selbst wenn nicht wäre das egal, da er theoretisch nur die anzahl der interrupts zählt die in einem bestimmten Zeitraum ausgelöst wurden und hiervon den durchschnitt zwischen zwei tastendrücken ausrechnet. Mit der gleichen Frequenz blinkt ja dann auch die LED. Somit gleicht sich das wieder aus. Gibt es beim ATmega nicht auch die möglichkeit zur laufzeit auf registerwerte zuzugreifen?
Paul Hamacher wrote: > > Gibt es beim ATmega nicht auch die möglichkeit zur laufzeit auf > registerwerte zuzugreifen? Irgendwie verstehe ich Dich nicht richtig... Selbstverständlich greift der AVR zur Laufzeit auf Register zu. Oder meinst Du, ob Du bei der Simulation die Registerinhalte sehen kannst? Falls ja, da gibt es mehrere Möglichkeiten, schau mal in den Menüpunkt view. Da gibt es das Fenster für Register. Ich bevorzuge aber das Fenster für Memory und wähle dort Register aus, finde ich übersichtlicher und platzsparender auf dem Bildschirm. Da ich nicht bereit bin, zum Lesen einer browsertauglichen Textdatei (ASM-Quelltext) ein Zip-Archiv herunterzuladen und zu entpacken (das Projekt läuft ja doch nicht, da meine Ordnerstruktur nicht mit Deiner übereinstimmt), habe ich mir Deinen Quelltext nicht angesehen. Das Anhängen im Textformat erhöht die Chance, dass sich jemand den Text etwas genauer ansieht. Was soll das eigentlich werden? Eine Art Metronom (ja, so heißt dieses im Takt knackende "Musikinstrument")? ...
Tut mir leid, habe mich vielleicht etwas unklar ausgedrückt. Ich meinte damit ob man während der ATmega8 in der Schaltung seinen dienst tut die Werte der Register über den PC auslesen kann, zum debuggen. Ich spreche nicht vom Simulator. Die Zip-Datei habe ich angehängt damit ich den PAP und das Bild zum Aufbau noch mit reinkrig. Habe die asm-datei nochmal angehängt. Den Programmablaufplan hast du ja schon. Habe inzwischen mal die Divisionsroutine zur Unterbrechung durch einen Interrupt freigegeben. Dachte dass die Division eventuell zu lange dauert und den Timer-interrupt so lange blockiert dass ein paar zähl-takte verloren gehen. Bei 8 Mhz schneidet die Divisinsroutine den Timerinterrupt zwar gelegentlich aber der timerinterrupt wird dann trotzdem noch ausgeführt bevor ein zweiter Timerinterrupt ausgelöst wird.. mit der Division sollte es also eigentlich keine Probleme geben, wie es bei höheren Zahlen aussieht weiß ich nicht deswegen hab ich das nun einfach mal so stehen gelassen. Der Praxistest zeigt dass die LED immernoch ausser Takt blinkt Nochmal was das werden soll: Ein Lauflicht das im Takt zur Musik läuft. Der Takt wird schlag für schlag über einen Taster eingegeben. Der Einfachheit halber bringe ich im Moment aber kein Lauflicht zum laufen sondern nur eine LED am Port B zum blinken. Btw: Taster sind per Kondensator zuverlässig hardwareentprellt. Dass diese Entprellung funktioniert sieht man auch an den gelben LEDs die an Port C angeschlossen sind. mfg Paul H.
Tut mir leid den Thread zu pushen aber ich suche immernoch verzweifelt nach einer Lösung. Ich verstehe das nicht, normalerweise müsste alles funktionieren. Eventuell baue ich zum besseren Verständnis noch eine vereinfachte Version. Danke. mfg Paul
Paul Hamacher wrote: > Tut mir leid den Thread zu pushen aber ich suche immernoch verzweifelt > nach einer Lösung. Ich verstehe das nicht, normalerweise müsste alles > funktionieren. Eventuell baue ich zum besseren Verständnis noch eine > vereinfachte Version. Danke. > > mfg Paul Ein Lösungsansatz wäre: Timer1 mit entsprechendem Vorteiler laufen lassen, so dass längstes und kürzestes benötigtes Intervall in seinen Zählumfang passen. Einen hardware-entprelltem Taster am ICP-Eingang anschließen und per Input-Capture-Interrupt den Zeitabstand zwischen zwei Tastendrücken scannen. Diesen Wert (16 Bit) dann als Intervall für den Output-Compare-Interrupt des Timer1 benutzen. In der ICP-ISR: - Neuen Zeitstempel einlesen (ICRL/ICRH) - Alten (gemerkten) Zeitsempel aus SRAM in Hilfsregister kopieren - Neuen Zeitstempel (für nächste Runde) im SRAM speichern - Zeitstempel in OCR1B(H/L) schreiben, um Timeout zu setzen - Hilfsregister vom neuen Zeitstempel subtrahieren und Ergebnis als "Intervall" sichern, falls anhand des Gültigkeitsflags das Intervall verwertbar ist. Dann auch "neu"-Flag setzen. - danach Gültigkeitsflag setzen In der OC1B-ISR: - Gültigkeitsflag löschen (der Zeitabstand zum vorhergehenden Tastendruck ist zu groß um das gemessene Intervall zu verwerten, es gab einen Überlauf, der Timer hat also den Zählerstand des letzten Tastendrucks wieder erreicht) In der OC1A-ISR: - OCR1A(L/H) einlesen, - Intervall dazuaddieren - als Termin für den nächsten Interrupt in OCR1(L/H) schreiben - Lauflicht weiterschalten In der Mainloop: - Wenn neu-Flag gesetzt ist, dann - neu-Flag löschen (Job wird ja erledigt) - Kopie vom Intervall holen - in Frequenz (BpM) umrechnen - ausgabegerecht in einen SRAM-Bereich schreiben - Sleep bis zum nächsten Interrupt... In der OVF0-ISR: - Ausgabe der in der Mainloop vorbereiteten Ziffern (im SRAM) per Multiplex an die 7-Segment-Ziffernanzeigen In der Init-Routine: - Stackpointer, Timer, Ports usw. initialisieren Gültigkeits-Flag und neu-Flag sind Boolsche Variablen (einzelne Bits in einem Register). ...
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.