Ich bin ein absoluter newbie in sachen mikrocontroller und assembler.
Ich möchte nun ein ganz einfaches Prog. schreiben, welches zwei led's
abwechselnd an PD0 und PD1 blinken lässt. und das ganze mit 1Hz.
Das Problem das ich hab ist, dass ich keinen Ahnung hab wie ich den
internen Timer meines ATmega8 (4MHz) verwenden kann.
Was ich bis jetzt herausgefunden habe ist, dass man irgendwelches Bits
setzten muss um einen "Multiplikator" (oder so ähnlich) einzustellen,
der dann sagt wie oft der Timer in der sek. zählt. Was ich auch schon
herausgefunden hab ist, das ich den Timer1 nehmen muss, weil ein 8bit
Timer nicht für so eine "große" Zeitspanne ausreicht.
Bei jedem Überlauf des Timers soll dann ein Interrupt ausgelöst
werden.
Doch wie konfiguriere ich den Timer und starte ihn? welche bits muss
ich setzten??
Könnt ihr mir vielleicht einen Link nennen wo das verständlich
beschrieben wird?
danke
Hallo,
Du setzt das Bit für globale Interruptfreigabe, setzt das Bit, damit
Timer1 einen Überlauf-Interrupt auslöst, stellst den Vorteiler ein, so
das die Quarzfrequenz geteilt durch den Wert des Vorteilers, z.B. 1024,
in die Nähe Deiner gewünschten Frequenz kommt. Takt Vorteiler 65535
= Interruptfrequenz.
Welche Bits in welchen Registern das genau sind, steht im Datenblatt in
den Kapiteln zum Interrupt und zu dem Timer1. Dann kannst Du in der
Interruptroutine irgend ein Bit setzen, und dieses im Hauptprogramm
abfragen und an den Port weiter geben oder Du schiebst in der
Interruptroutine ein Register nach links und gibst dieses im
Hauptprogramm am Port aus.
Auf der Homepage von Scott-Falk Huehn ist ein Beispiel, wie einen LED
blinkt, allerdings ohne Interrupt.
oder hier, allerdings nicht für den Mega:
www.avr-asm-tutorial.net/avr_de/test4.html
Gruß
aus den verschiedensten Seiten hab ich nun folgenden Code gebastelt:
.include "m8def.inc"
.org 0x000
rjmp start
.org 0x08
rjmp overflow ; Timer1 Overflow Handler
;--------------------------------------
start: ;-- stapel initialisieren
ldi r16,HIGH(RAMEND)
out SPH,r16
ldi r16,LOW(RAMEND)
out SPL,r16
;-- LED's initialisieren:
ldi r16,0xFF
out DDRB,r16
ldi r16,0x00
out PORTB,r16
ldi r17,0b01010101
sei
rcall load_count
;--------------------------------
;prescaler auf 256 setzen
ldi r16,0x04
out TCCR1B,r16
;--------------------------------
loop: rjmp loop
;-----------------------------------------´
overflow:
;LED's ändern
inc r17
out PORTB,r17
rcall load_count
reti
;------------------------------------------
load_count:
push r16
;Timer Counter mit 49911 vorladen
ldi r16,HIGH(49911)
out TCNT1H,r16
ldi r16,LOW(49911)
out TCNT1L,r16
pop r16
ret
;------------------------------------------
nur er funzt nicht und ich hab keine ahnung worans liegt.
Wenn ich den proz starte tut sich nichts. alle LEDs tot!
obwohl die LEDs in der Hauptroutine einmal alle angeschaltet werden!
sozusagen als funktionstest!
Aber da geht nichts!!
Woran könnte das liegen??
sbi im oberen I/O-Bereich??
Ich habe jetzt zwar nicht konkret im Datenblatt nachgeschaut, aber ich
denke, dass die Register der Timer im oberen I/O-Bereich liegen.
SBI/CBI geht doch aber nur im unteren Bereich, oder??
...HanneS...
Ups, sorry, habe nur versucht, zu helfen!
Sich über andere Leute beschweren aber selber keinen Lösungsvorschlag
bringen, das ham ma gern ;-)
Habe bei mir gerade nachgesehen und da ist es auch mit out.
Aber das hasse ich wirklich am meisten bei den AVR´s. Dies und das geht
einfach mit diesem oder jenem Befehl ABER...
Ansonsten jibt es nix auszusetzen über die AVR´s.
Das nur mal so am Rande.
@Ricardo:
Dann füge folgendes, am besten lieber doch vor der Marke "loop",
ein:
ldi r16,0b00000100
out TIMSK,r16
oder
ldi r16,1<<TOIE1
out TIMSK,r16
Gruß
Andi
Danke für den Tip.
Leider muss sonst noch ein Fehler drinen sein!
Ich hab den Code nun ein bisschen geändert:
.include "m8def.inc"
.org 0x000
rjmp start
.org 0x08
rjmp overflow ; Timer1 Overflow Handler
;--------------------------------------
start: ;-- stapel initialisieren
ldi r16,HIGH(RAMEND)
out SPH,r16
ldi r16,LOW(RAMEND)
out SPL,r16
;-- LED's initialisieren:
ldi r16,0xFF
out DDRB,r16
sei
rcall load_count
ldi r17,0b01010101
out PORTB,r17
;--------------------------------
;prescaler auf 256 setzen
ldi r16,0x00000100
out TCCR1B,r16
;--------------------------------
;--------------------------------
;timer_1 overflow interrupt enable
ldi r16,0b00000100
out TIMSK,r16
;--------------------------------
loop: rjmp loop
;-----------------------------------------´
overflow:
;LED's ändern
ror r17
out PORTB,r17
rcall load_count
reti
;------------------------------------------
load_count:
push r16
;Timer Counter mit 49911 vorladen
ldi r16,HIGH(49911)
out TCNT1H,r16
ldi r16,LOW(49911)
out TCNT1L,r16
pop r16
ret
;------------------------------------------
ich komm einfach nicht drauf woran es liegt!
der timer dürfte einfach nicht zu zählen beginnen oder kein interrupt
auslösen! Muss ich da noch irgend ein Bit setzen??
Du könntest ja mal zur Sicherheit die Liste für die Interrup-Vektoren
verfollständigen:
.org 0x000
rjmp start ; Reset-Vektor
reti
reti
reti
reti
reti
reti
reti
rjmp overflow ; Timer1 Overflow Handler ($008)
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
Wenn von Haus aus ein IRQ aktiv ist, wird durch ein RETI verhindert,
das irgend was unvorhergesehenes passiert und Dein Prog.-Code ist nicht
innerhalb der Vektor-Liste.
Ansonsten lies Dir im PDF für den ATMega8 ab Seite 44 alles über
Interrups durch.
http://www.atmel.com/dyn/resources/prod_documents/doc2486.pdf
Vielleicht mußt Du noch irgend was wegen dem Bootloader machen, weiß
nur nicht was.
Aber da hilft nur Büffeln im PDF.
Gruß
Andi
ich hatte auch schon prob. alle interrupts zu deklarieren!
hatte aber nichts gebracht! Habs trotzdem nochmal probiert. Aber kein
Erfolg!
Naja dann packen wir mal usere Englischkenntnisse aus!
Hallo,
Du kannst den Timer so konfigurieren, das Du ihn nicht jedesmal neu
laden muss (siehe CTC Clear Timer On Compare).
Laut Kommentar aktivierst Du den OverFlow Interrupt, verwendest aber
einen Compare Match Interrupt. Ich denke Du muesstest Bit 3 (also
0b00001000) nehmen.
Gruss
Andreas
Hallo Ricardo,
du hast einen Fehler in Deiner Prescaler-Routine
;--------------------------------
;prescaler auf 256 setzen
ldi r16,0x00000100
out TCCR1B,r16
;--------------------------------
du musst ldi r16,0b0000100 eingeben. x bedeutet Hexadezimalwert wird
übergeben und der ist mit 00000100 out of range.
Vielleicht solltest Du dir AVR-Studio von der Atmel Seite laden und
Deine Programme darin simulieren. Habe ich auch mit Deinem Programm
gemacht und der Fehler wurde sofort angezeigt.
Gruß Jörg
Nochmals ich,
falls die LED's abwechselnd blinken sollen würde ich statt
;LED's ändern
ror r17
out PORTB,r17
;LED's ändern
com r17
out PORTB,r17
schreiben. Also das 1ner Complement bilden (heißt nur alles was 0 ist
wird 1 und umgekehrt). ROR rotiert nach rechts und fügt ganz links das
Carry-Bit ein und da Du es beim ersten mal nicht gesetzt hast schiebt
er eine 0 ein.
Dazu empfiehlt sich das AVR Instruction Manual (auch bei Atmel).
Gruß Jörg
Nochmals zum 3.,
falls Deine Programme später mal zeitkritischer werden, würde ich die
load_count-Routine in die Timer1-Overflow-Routine setzen.
Der Aufruf der Unterroutine load_count plus dem pusch und pop plus
Rücksprung kosten mal eben 11 Taktzyclen.
Weiterhin kann es sein, dass es zu Laufzeitfehlern kommt, wenn z.B. die
load_count-Routine durch einen anderen Interrupt unterbrochen wird.
Gruß Jörg
Hallo Jörg!
Wenn eine IRQ-Routine gestartet wurde, wird automatisch das I-Bit in
SREG (Global Interrupt Flag) gelöscht, d. h. das während des Ablaufs
einer IRQ-Routine keine andere IRQ-Routine gestartet wird.
Beim Rücksprung aus einer IRQ-Routine mit RETI wird das I-Bit wieder
automatisch gesetzt auch wenn vorher kein Hardware-Call zu einer
IRQ-Routine war.
Man kann das I-Bit mit SEI innerhalb einer IRQ-Routine wieder setzen
wodurch dann ein Interrupt innerhalb einer IRQ-Routine aufgerufen
werden darf aber so was sollte man dann mit eigenen Flag-Variablen
handlen.
Desweiteren kommt es im Normalfall bei einer IRQ-Auslösung während eine
IRQ-Routine läuft quasi zu einer zeitlichen verzögerung.
Z. B. wird für Timer1 das Flag Timer 1 Overflow Interruptflag gesetzt
aber die dazu gehörende IRQ-Routine erst gestartet, wenn die vorher
ausgeführte IRQ-Routine mit RETI beendet wurde.
Will damit nur sagen, das die Unterroutine load_count welche mit rcall
von der IRQ-Routine Timer1_Overflow aufgerufen wurde nicht unterbrochen
werden kann da währenddessen das I-Bit gelöscht ist und erst durch RETI
wieder gesetzt wird.
Gruß
Andi
Hallo Andi,
stimmt hab ich nicht dran gedacht.
Was passiert denn, wenn wärend der Abarbeitung eines Timer 1 Overflow
Interrupts ein weiterer Timer 1 Overflow Interrupt auftritt?
Gruß Jörg
Wenn der Timer1 einen IRQ auslöst während Timer1 läuft wird so gut wie
kein Befehl mehr von dem normal ablaufenden Programm abgearbeitet.
Desweiteren stimmt dann die Frequenz nicht mehr für Timer1 und man
sollte die Frequenz verringern oder die Routine optimieren bzw. auf
weniger Takte bringen.
Gruß
Andi
Danke für alle Tipps!
ich hatte eigentlich schon wenige Stunden nach der 1. Antwort gepostet
das der Fehler mit dem x schuld war!
der thread scheint aber gar nicht auf!?! Wahrscheinlich war irgend ein
kleiner Fehler mit er Verbindung zum Server oder sonst was!
Danke aber trotzdem für die vielen Antworten und Tips