Hallo zusammen, ich versuche seit einigen Tagen den atmega8 zu Programmieren. Lcd Ansteuerung usw ist auch kein Problem. Ich möchte eine Frequenz die am ICP Pin angeschlossen ist messen. Also es liegt zb. ein Signal von 10-20 Khz an welches der atmega ausgeben soll auf dem display. Ich Programmiere das ganze in Assembler und irgendwie komme ich mit den ganzen Registern die dazu benötigt werden nicht so ganz zurecht Wäre über eine Hilfe sehr dankbar. MfG
Naja, soviele Register sind es nun auch wieder nicht. Wenn du den Timer1 schon in Betrieb hast, sind es doch nur noch ein paar Bits die dich vom ICP trennen. Etwas genauer erklärt (allerding in C) ist das ganze hier: http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Einfangen_eines_Eingangssignals_.28Input_Capturing.29 Kurz gesagt musst du nur den Timer1 scharf schalten:
1 | TCCR1A = 0x00; |
2 | TCCR1B = (1<<CS10); // für ICP siehe Datenblatt Bit 6 und7 |
3 | |
4 | TIMSK = (1<<TICIE1); // ICP-Interrupt einschalten |
5 | |
6 | sei(); // Interrupts global freischalten |
Das ist es im Grunde schon. Sobald die in TCCR1B selektierte Flanke erkannt wird, wird in die entsprechende Interrupt-Routine verzweigt. Im 16-Bit Register ICR1 findest du dann den Zählerwert 'eingefroren' welcher zum Zeitpunkt der Flanke in TCNT1 stand.
ja das ist mein Prob da ich von C kein Plan hab mein Assembler Code sieht im moment so aus: timer: ldi r16,0b00001000 out TIMSK,r16 ldi r16,0b01000001 out TCCR1B,r16 ldi r16,0b00000000 ldi r17,0b00000000 out TCNT1H,r16 out TCNT1L,r17 rcall LCD_line2 in r16,ICR1H rcall LCD_data rcall wait20ms in r16,ICR1L rcall LCD_data rcall wait20ms rcall timer
frankparker wrote: > mein Assembler Code sieht im moment so aus: > timer: ldi r16,0b00001000 > ... > rcall timer Die Rekursion (Selbstaufruf) hier ist bestimmt ein Fehler.
ja der selbstaufruf ist bisher nur zum testen drin. es soll halt ein interrupt ausgelöst werden wenn eine High Flanke an ICP(pb0) kommt. rjmp onTC1 ;TC1 Capture onTC1: rcall LCD_line2 in r16,ICR1H rcall LCD_data rcall wait20ms in r16,ICR1L rcall LCD_data rcall wait20ms reti timer: ldi r16,0b00000000 out TCCR1A, r16 ldi r16,0b11000001 out TCCR1B,r16 ldi r16,0b00000000 ldi r17,0b00000000 out TCNT1H,r16 out TCNT1L,r17 ldi r16,0b00001000 out TIMSK,r16 sei rcall timer in main wird halt timer aufgerufen und gestartet. Dort soll der das programm den abstand zwischen zwei high flanken messen und erstmal ausgeben.
Also ehrlich gesagt fehlt bei deinen ganzen Code-Fragmenten die Interrupt-Vektor Tabelle. Woher soll ich denn jetzt wissen, wo dein Programm beginnt? Und noch wichtiger, ob deine ISR an der richtigen Stelle steht? Ich rate mal: 1. "onTC1" ist deine Interrupt-Routine. In deiner ISR möchtest du also tatsächlich 40ms warten? Das bedeutet, du gehst davon aus, dass zwischen 2 steigenden oder 2 fallenden Flanken 40ms vergehen. Das entspricht ganzen 25 Hz... Versuch die ISR so kurz wie möglich zu halten und gibt die Ergebnisse in deiner Hauptschleife aus. Alles andere ist murks. Achja, denkst du wirklich, du erkennst so schnell aufflackernde Werte auf deinem LCD? 2. "timer" ist deine Hauptschleife, oder? Die Initialsierung (Prescaler 1, steigende Flanke und Noise Canceler ein) schaut soweit gut aus. Allerdings schaltest du den Interrupt für den Output Compare/Match1 Interrupt ein, nicht den für den Input- Capture (Bit 5 laut Datenblatt!) Ausserdem ist es etwas unüblich die Initialisierung permanent zu wiederholen. Setz lieber nach 'sei' eine Endlosschleife, in der du die gemessenen Daten ausgibts. Am besten über einige Messungen gemittelt.
Ja OnTC1 ist meine Interrupt Routine. Die 40 ms waren nur drin um halt auf dem Display was erkennen zu können. timer ist nicht unbedingt meine Hauptschleife. In der Haupschleife hab ich einen Anzeige-Text fürs display und ein rcall aufruf zum "Timer" Bei TIMSK war das Bit falsch gesetzt - das ist mir eben auch schon aufgefallen. Ich hab das jetzt mal geändert timer: cli ldi r16,0b00000000 out TCCR1A, r16 ldi r16,0b11000101 out TCCR1B,r16 ldi r16,0b00000000 ldi r17,0b00000000 out TCNT1H,r16 out TCNT1L,r17 ldi r16,0b00100000 out TIMSK,r16 sei ret ausgabe:rcall LCD_line2 in r16,ICR1L rcall LCD_data in r16,ICR1H rcall LCD_data reti rcall ausgabe das Programm ruft nun einmal Timer auf zur initialisierung und danach nur noch die Ausgabe. Intterupt hab ich mit rjmp onTC1 ;TC1 Capture aktiviert Nur was ich jetzt bei onTC1 eintragen muss weis ich nicht
> Intterupt hab ich mit rjmp onTC1 ;TC1 Capture aktiviert
Du hast den Sinn einer Interrupt-Routine nicht verstanden. Du darfst
diese Routine NIEMALS aufrufen, da der Befehl "reti" für die Rückkehr
aus einem echten Interrupt gedacht ist. Und der wird von deiner Hardware
automatisch aufgerufen sobald eine Flanke erkannt wurde. Um die Routine
zu triggern musst du also eine Flanke an den ICP1 Pin anlegen. Dann
sucht sich der Atmega8 automatisch die Adresse deiner ISR aus der
Vektortabelle.
Ob du deine ISR in den richtigen Vektor geschrieben hast kann ich
allerdings nicht sagen da du dich so beharrlich weigerst mal die
Vektortabelle oder besser noch deinen ganzen Source mal anzuhängen.
ja aber das Programm soll doch den Interrupt selber auslösen - also beim ersten mal zähler auf 0 und beim zweiten aufruf den Wert ausgeben. Ich hab am ICP Pin eine Frequenz angelegt und das Display gibt mir wechselnde zeichen aus Ich schreib das ganze mit SISY - und hab das da in ein Main Programm und unterprogramme unterteil - ich weis nich wie ich das in eine Datei zum hochladen kriege. Das sind halt meine ersten schritte - vorher hab ich bisher nur den 8085 Programmiert.
; Reset and Interrupt vector Beschreibung begin: rjmp main ;POWER ON RESET reti ;Int0-Interrupt reti ;Int1-Interrupt reti ;TC2 Compare Match reti ;TC2 Overflow rjmp onTC1 ;TC1 Capture reti ;TC1 Compare Match A reti ;TC1 Compare Match B reti ;TC1 Overflow reti ;TC0 Overflow reti ;SPI, STC Serial Transfer Complete reti ;UART Rx Complete reti ;UART Data Register Empty reti ;UART Tx complete reti ;ADC Conversion Complete reti ;EEPROM Ready reti ;Analog Comparator reti ;TWI (I²C) Serial Interface reti ;Store Program Memory Redy
> ja aber das Programm soll doch den Interrupt selber auslösen - also beim > ersten mal zähler auf 0 und beim zweiten aufruf den Wert ausgeben. Interrupts werden nicht vom Programm sondern von der Peripherie, in deinem Fall der Input-Capture-Einheit ausgelöst. Wenn du das nicht möchtest, kannst du auch das Bit #5 im Register TIFR (Timer Interrupt Flag Register) pollen. Wenn es gesetzt ist kannst du aus ICR1 deinen Timerstand sichern und es wieder löschen (siehe Datenblatt). Nochmal: Du kannst per Software keinen Interrupt auslösen. > Ich hab am ICP Pin eine Frequenz angelegt und das Display gibt mir > wechselnde zeichen aus Ich kenn deine Display-Ansteuerung nicht. Wenn du deine wie auch immer ermittelte Zahl ausgeben möchtest, musst du sie sicher erst noch in einen darstellbaren ASCII-String umwandeln. > Ich schreib das ganze mit SISY - und hab das da in ein Main Programm und > unterprogramme unterteil - ich weis nich wie ich das in eine Datei zum > hochladen kriege. Alles in eine Textdatei kopieren? Oder notfalls mehrere Dateien anhängen? Oder zippe das Projekt, wenn's denn unbedingt sein muss. > Das sind halt meine ersten schritte - vorher hab ich bisher nur den 8085 > Programmiert. Meine ersten Schritte hab ich auf einem KC89 gemacht. Da gabs kein Internet, dafür Stern-Rekorder um Programme aufzuzeichnen... ;)
Ja mit dem Interrupt ist ja auch ok über den ICP Pin Der soll ja nur eine Periode Messen, die ich dann später auswerte... Ich hab dir das ganze mal angehängt - sind aber noch viele Programm teile drin die noch nicht benutzt werden. Wenn ich die frequenzauswertung hingekriegt hab wird später ein Farbsensor am ICP Pin angeschlossen.
Langsam verliere ich ehrlich gesagt die Lust mir dein ungetestetes Zeug weiter anzuschauen :( 1. Du hast lauter .txt-Dateien in deinem RAR-Archiv. Du bindest aber .s Dateien ein. Selbst wenn ich für teuer Geld dein SisyAVR Paket gekauft hätte, könnt ich es nicht einmal assemblieren. 2. Deine Main sieht anders aus, als das was du oben gepostest hast. Wieso initialisierst du den Timer plötzlich nicht mehr vor deiner Hauptschleife sondern erst bei der Ausgabe in der Subroutine "Farbe"? 3. Deine ISR ist auf einmal leer?? Ich würde vorschlagen, du malst dir mal einen schnöden, altmodischen PAP (Programmablaufplan) auf, in dem du dir erstmal klar machst, welche Aufgaben in welcher Reihenfolge erledigt werden müssen. Beachte hierbei, dass du einen PAP für deine Anwendung und einen weiteren für deine Interrupt-Routine brauchst. Beide laufen quasi getrennt voneinander ab.
zu1 ) ja das sind txt dateien da ich in SISY einfach alles in txt dateien kopiert hab. Das Assemblieren klappt ja ;) zu2 ) der Timer initialiesiere ich schon die ganze Zeit so - oben war ja nur ein auschnitt des Programms. Ich hab das nur so aufgeteilt damit ich nicht eine riesiege Datei haben möchte sondern übersichtlicher in einzelnen dateien. zu3) ISR ist leer weil du ja sagtest das ich die Ausgabe lieber unter sei bei timer setzten sollte. Das war ja eben meine Frage was ich nun da eintragen soll. Ein PAP hab ich ja - ich weis ja was das Programm machen soll Display usw initalisieren auf eine High Flanke warten timer starten bei nächsten High Flanke den Zähler Wert ausgeben da ich ja eine feste frequenz über einen Generator einspeise müssten ja immer die selben zeichen auf dem Display erscheinen - mehr will ich ja zuerst garnicht. Danke schonmal für deine Hilfe - kann mir schon denken des das nervt weil ich mich erst seit kurzem mit dem Atmega beschäftige.
> zu1 ) ja das sind txt dateien da ich in SISY einfach alles in txt > dateien kopiert hab. > Das Assemblieren klappt ja ;) Schön das du es assemblieren kannst. > zu2 ) der Timer initialiesiere ich schon die ganze Zeit so - oben war ja > nur ein auschnitt des Programms. Dein Problem ist nicht, WAS du in die Register schreibst, sonder WANN. Der Tip mit dem PAP diente dazu dir die Reihenfolge der Aktionen klarer zu machen. Würde das funktionieren: warte_das_auto_losfährt(); starte_motor(); Sicher nicht, das Auto fährt erst los, wenn der Motor an ist. Genauso ist es mit dem Timer, er wird erst initialisiert und kann danach verwendet werden. Zyklisch am Zündschlüssel zu drehen wenn der Motor läuft ist dem vorankommen auch nicht dienlich. > Ich hab das nur so aufgeteilt damit ich nicht eine riesiege Datei haben > möchte sondern übersichtlicher in einzelnen dateien. Sehr löblich. Allerdings kenn ich deinen Assembler nicht. Und ich weiß auch nicht, ob du alles korrekt/vollständig im richtigen Kontext kopiert hast. Zippe (diese Wortschöpfung stammt entweder von 'to zip' oder auch von dem allgemein gebräuchlichen Archiv-Format ZIP) das Projekt so, wie es assemblierbar ist. Sisy werd ich mir trotzdem nicht kaufen. > zu3) ISR ist leer weil du ja sagtest das ich die Ausgabe lieber unter > sei bei timer setzten sollte. > Das war ja eben meine Frage was ich nun da eintragen soll. Wie ich bereits mehrfach sagte, dort wird der Wert aus ICR1 gesichert und ggf. TCNT1 zurück gesetzt. Beachte, dass die Zeit die du zum sichern von ICR1 brauchst, beim nächsten Interrupt fehlt, da TCNT1 je nach Prescaler schon ein paar Takte wieder gezählt haben kann. Den gesicherten Wert verarbeitest du dann in deiner Main. Am besten legst du hier eine Kopie von dem gesicherten Wert an, damit ein neuer Interrupt nicht teile der letzten Messung verfälscht. > Ein PAP hab ich ja - ich weis ja was das Programm machen soll > Display usw initalisieren auf eine High Flanke warten timer starten > bei nächsten High Flanke den Zähler Wert ausgeben Dann überdenke ihn nochmal. Ein Interrupt kann erst ausgelöst werden, wenn die dazugehörige Peripherie initialisiert und Interrupts global frei gegeben wurden. Wenn eine Peripherie initialisiert wurde, lass sie in Ruhe ihren Job tun ;) > da ich ja eine feste frequenz über einen Generator einspeise müssten ja > immer die selben zeichen auf dem Display erscheinen - mehr will ich ja > zuerst garnicht. Nimm die Software schrittweise in Betrieb. Prüfe zunächste, ob die ISR ausgeführt wird (Pin toggeln in der ISR und mit Oszi EIngangssignal und den Debug-Pin vergleichen) Wenn das passt, lass dir feste, hart kodierte Strings auf dem LCD anzeigen. Wenn das passt, mit dem Oszi schauen ob deine ISR noch Ok ist. Dann eine Routine für die Ausgabe von 16-Bit Werten testweise mit hart kodierten Zahlen prüfen. Und zum Schluß N getestete und funktionierende Softwareteile zusammen fügen. > Danke schonmal für deine Hilfe - kann mir schon denken des das nervt > weil ich mich erst seit kurzem mit dem Atmega beschäftige. Mach vielleicht mal eine Pause und schlaf darüber / iss Mittag. Wenn man mit frischer Kraft und konzentriert(!)&systematisch(!) an das Problem heran geht, fällt vieles leichter. Trial&Error finde ich persönlich nicht so produktiv. ;)
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.