PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : ATMega16 und Timer Verständnisproblem



-tim-
27.12.2006, 12:10
Hallo,
ich versuche seite 2, 3 Tagen einen Timer mit einem ATMega16 auf die Beine zu stellen, der jede Sekunde eine Zahl verändert. Wie ich es mit Zyklen machen kann hab ich schon so einigermaßen hin bekommen, aber für meine Zwecke wäre eine Interruptlösung besser, da ich die aktuelle Zahl besser ausgeben kann. Einige Ansätze dazu hab ich auch schon gefunden, aber nichts was mir so weitergeholfen hat, das ich es auch verstanden hab was der Code bewirkt, und wo ich die Zeitabstände einstellen kann.
Im Basiswissen unter Timer/Counter (Avr) (https://www.roboternetz.de/wissen/index.php/Timer/Counter_%28Avr%29) hab ich schon was gelesen wie es Theoretisch abläuft, aber wie gesagt, die Umsätzung fehlt mir. Das Einzigste was ich jetzt so am groben Ansatz hab ist, das ich den Interrupt definieren muß. Irgenwie mit TCCR0 TCNT0 und so weiter. Kann mir das jemand mal so Schritt für Schritt erklären?

Danke schon mal für euer Bemühen....

mfG Tim

teslanikola
27.12.2006, 12:28
Also das mit dem Interrupt verhätl sich so, dass die ersten paar Wörter im Flash Interruptvektoren sind, das heist, das wenn ein Interrupt aktiviert ist und dieser aufgerufen wird dann springt dein Programmzeiger auf genau diese addresse ( zb $0010 ), da du da jab aber nur ein befehl ausführen könntest schreibt man dort ein rjmp rein, welches zu deiner Interruptroutine führt. Deine interruptroutine beendest du nicht mit ret sondern mit RETI !!!!

So das waren die Basics nun aber zum Code:

1.
Als erstes werden die Interruptvektoren eingerichtet:
Die addresse findest du entweder im Datenbaltt oder im INC File


;----[ Interrupt-Vektoren ]---------------------------------------------------------

.org 0000 ; Zum Programmbeginn springen
rjmp reset

.org OVF1addr ; Timer1 überlauf
rjmp Overflow


3.
Den Timer einrichten, aber das kannste ja schon

4.
Den Timerinterrupt aktivieren:
Dazu musst du im TIMSK Register den TOIE1 aktiverien ( TimerOverflowInterruptEnable1 für timer1 )


ldi tmp, (1<<TOIE1) ; Timer1 Interrupt
out timsk, tmp ; aktivieren


5.
GANZ WICHTIG!!! GLOBALE INTERRUPTFREIGABE!!!
sonst geht überhaupt kein Interrupt

Dazu einfach den befehl SEI benuzen.


sei ; Interuptts freigeben


6.
Zum schluß deine Iinterruptroutinen ans ENDE deines Quellcodes
beginen mit deinem LABELD ( hier Overflow ) und Ende mit RETI


Overflow:
; HIER DEIN CODE
reti

So das wars auch schon, das kannste so für alle Interrupts übernehmen.

-tim-
27.12.2006, 12:50
Hallo danke, das hat mich schon mal ein ganzes Stück weiter gebrach denn die Testled blinkt schon mal :-)

Das mit dem Punkt "3. Den Timer einrichten, aber das kannste ja schon " .. Theoretisch hab ich das glaube verstanden, aber wie ich das im ASM umsetze bin ich leider überfragt :-( könntest du mir das auch noch mal erklären?

-tim-
27.12.2006, 18:08
Also ich hab jetzt was gefunden und das auf meinen µC umgearbeitet. Die berechnung stimmt nicht genau. Der Timer schaltet nicht bei jeder sekunde aber so in etwa bei 1,5, das ist aber auch etwas nebensache/einstellungssache
Kann mir mal jemand den Code erklären?


;************************************************* ***********************/
;* */
;* Precise 1 Second Timebase */
;* */
;* Author: Peter Dannegger */
;* danni@specs.de */
;* */
;************************************************* ***********************/
.nolist
.include"m16def.inc"

.equ xtal = 8000000
.equ debounce = 100
.equ remainder = xtal - xtal / debounce * debounce

.def isreg = r15
.def wr0 = r16
.def iwr0 = r17
.def prescaler = r18
.def second = r19

.list
rjmp init
.org OC1Aaddr
rjmp OC1Aint
;-------------------------------------------------------------------------
OC1Aint:
in isreg, sreg
;************************************************* ***********************/
;* Insert Key Debouncing Here */
;************************************************* ***********************/

ldi iwr0, high( xtal / debounce - 1 )
out ocr1ah, iwr0
ldi iwr0, low( xtal / debounce - 1 )
out ocr1al, iwr0

dec prescaler
brne _oci1

ldi prescaler, debounce
inc second

ldi iwr0, high( xtal / debounce + remainder - 1 )
out ocr1ah, iwr0
ldi iwr0, low( xtal / debounce + remainder - 1 )
out ocr1al, iwr0
_oci1:
out sreg, isreg
reti
;-------------------------------------------------------------------------
init:
ldi wr0, 0xFF
out ddrb, wr0

ldi wr0, high( ramend )
out sph, wr0
ldi wr0, low( ramend )
out spl, wr0

ldi wr0, 1<<WGM12^1<<CS10
out TCCR1B, wr0

ldi wr0, high( 8000000 / 100 - 1 )
out ocr1ah, wr0
ldi wr0, low( 8000000 / 100 - 1 )
out ocr1al, wr0
out tcnt1l, wr0
ldi prescaler, debounce

ldi wr0, 1<<OCIE1A
out TIMSK, wr0
sei
main:
ldi second, 0
_mai1:
cpi second, 60
breq main
out ddrb, second
rjmp _mai1
;------------------------------------


Die Erklärung würde mir für den init Teil reichen. Danke

teslanikola
27.12.2006, 18:22
Schau dir einfach mal im Datenblat die Timerrigeister TIMSK TCCRxA TCCRxB an, da stehts wunderbar beschrieben, die daten schreibste mit dem befehl OUT

-tim-
27.12.2006, 18:41
Hallo dann werd ich mich mal durch das Englisch durchquälen. (mein Englisch ist nicht das beste :-( )

Also danke erst mal.

mfg Timmee

-tim-
28.12.2006, 14:38
Eine kleine Verständnisfrage hab ich da noch, wenn der Interrupt ausgelöst wurde, wartet dann der Timer mit weiterzählen bis die Ausgelöste Rotiene fertig ist oder zählt er gleich na dem Auslösen weiter?

teslanikola
28.12.2006, 14:54
Der Zählt weiter

-tim-
29.12.2006, 12:48
Ich ich den Code jetzt soweit das ich den Timer in der geschwindigkeit einstellen kann. Dann hab ich mir mit dem Programm rnVAR berechen lassen, welchen Prescaler (Teiler) und welchen Timervorgabewert ich nehmen soll. Das Programm hat mir als Teiler 256 und als Timervorgabewert 34286 Vorgeschalgen. Mit diesen Infos hab ich dann den Code gefüttert, aber mein timer reagiert nicht bei einer Sekunde sondern bei ca 7-8 Sekunden. Kann mir jemand sagen wo mein Fehler liegt?

Verwendete Hardware:
ATMega16
8MHz Quarz


.include"m16def.inc"


.def isreg = r15
.def wr0 = r16
.def iwr0 = r17
.def prescaler = r18
.def second = r19


.list
rjmp init
.org OC1Aaddr
rjmp Timerinterrupt


Timerinterrupt:

inc second
reti

init:
ldi wr0, 0xFF
out ddrb, wr0

ldi wr0, high( ramend )
out sph, wr0
ldi wr0, low( ramend )
out spl, wr0

ldi wr0, 1<<WGM12^1<<CS12 ; Festlegen des Teilers 265
out TCCR1B, wr0

ldi wr0, high( 34286 ) ;Festlegen des Timervorgabewertes
out ocr1ah, wr0
ldi wr0, low( 34286 ) ;Festlegen des Timervorgabewertes
out ocr1al, wr0
out tcnt1l, wr0

ldi wr0, 1<<OCIE1A
out TIMSK, wr0
sei

main:
ldi second, 0

_mai1:
cpi second, 60
breq main
out PORTB, second
rjmp _mai1


Kann mir jemand sagen wo mein Rechen- oder Codefehler liegt?

Michael
29.12.2006, 15:05
Hallo -tim-,

reagiert nicht bei einer Sekunde sondern bei ca 7-8 Sekunden. Kann mir jemand sagen wo mein Fehler liegt?
hast du die Fuses auf externen Quarz gestellt oder läuft der AVR noch auf den internen 1MHz?

Gruß, Michael

teslanikola
29.12.2006, 15:24
Und du hast vergessen das, wenn der Timer Hochgezäht hat wieder von 0 anfängt und nicht mehr bei 34286 , das muss der aber wissen, also schreib die Timervorgabe nochmals in die Interruptroutine, dann müsstes passen

-tim-
29.12.2006, 15:54
Ich hab es jetzt mal so gemacht wie teslanikola vorgeschlagen hat und hab dem Timer die Werte nocheinmal übergeben. Aber es hat sich leider nicht geändert, die Sekunde ist immer noch zu lang.



.include"m16def.inc"


.def isreg = r15
.def wr0 = r16
.def iwr0 = r17
.def prescaler = r18
.def second = r19


.list
rjmp init
.org OC1Aaddr
rjmp Timerinterrupt


Timerinterrupt:

ldi wr0, 1<<WGM12^1<<CS12 ; Festlegen des Teilers 265
out TCCR1B, wr0
ldi wr0, high( 34286 ) ;Festlegen des Timervorgabewertes
out ocr1ah, wr0
ldi wr0, low( 34286 ) ;Festlegen des Timervorgabewertes
out ocr1al, wr0
out tcnt1l, wr0

inc second
reti

init:
ldi wr0, 0xFF
out ddrb, wr0

ldi wr0, high( ramend )
out sph, wr0
ldi wr0, low( ramend )
out spl, wr0

ldi wr0, 1<<WGM12^1<<CS12 ; Festlegen des Teilers 265
out TCCR1B, wr0

ldi wr0, high( 34286 ) ;Festlegen des Timervorgabewertes
out ocr1ah, wr0
ldi wr0, low( 34286 ) ;Festlegen des Timervorgabewertes
out ocr1al, wr0
out tcnt1l, wr0

ldi wr0, 1<<OCIE1A
out TIMSK, wr0
sei

main:
ldi second, 0

_mai1:
cpi second, 60
breq main
out PORTB, second
rjmp _mai1



@Michael
Mit dem was du geschrieben hast, kann ich leider nichts anfangen. Kannst du mir erklären was du meinst?

teslanikola
29.12.2006, 16:04
teste mal nur ob die Interrupt zeitig aufgerufen wird und lass das MAIN mal wech, der interrupt müsste jede sekunde kommen, dh wenn du ne LED blinken läst blinkt die 1 mal in 2 sek ( 1sec an 1 sec aus )

PS haste ICQ? wen ja suche nach XXXXXXXXXXXXXXXXX, das bin ich und komm online

-tim-
30.12.2006, 09:06
Hallo Leute,
nach dem sich teslanikola sich bereit erklärt gestern seinen Nachmittag zu opfern, konnte mein Problem gelöst werden. Die Codes die ich oben gepostet habe, haben nicht viel mit einem normalen Timer zu tun, da sie mehr für PWM gedacht sind.

Ich danke allen die sich bei der Lösung des Problems beteiligt haben, aber vor allem teslanikola ;-)

Ich hoffe den Code den ich anhänge kann jedem Helfen, der auch mit den Gleichen Problemen zu kämpfen hat wie ich ;-)



;-------------------------------------------------------------
;-- --
;-- Assembler-Code für einen 16Bit-Timer für den ATMega16 --
;-- --
;-- dieser Timer ist für eine Taktfrequenz von 1MHz --
;-- ausgelegt ( interne Taktung des ATMega16 ) --
;-- --
;-------------------------------------------------------------

.include"m16def.inc" ; Einbinden der Definitionsdatei

.def tmp = r16 ; Festlegen des Arbeitsregisters
.equ Timervorgabewert = 49911 ; Festlegung des Timervorgabewertes

.list
rjmp init
.org OVF1addr
rjmp Timerinterrupt



;-- Initalisierung ------------------------
init:
ldi tmp, 0xFF
out ddrb, tmp

ldi tmp, high( ramend )
out sph, tmp
ldi tmp, low( ramend )
out spl, tmp

ldi tmp, 1<<CS11^1<<CS10 ; Festlegen des Teilers 64
out TCCR1B, tmp
ldi tmp, high( Timervorgabewert ) ;Festlegen des Timervorgabewertes
out tcnt1h, tmp
ldi tmp, low( Timervorgabewert ) ;Festlegen des Timervorgabewertes
out tcnt1l, tmp

ldi tmp, 1<<TOIE1
out TIMSK, tmp

ldi tmp, 0x00
out DDRD, tmp

sei

;-- Hauptroutine ----------------
main: rjmp main ; Hauptroutine ausgeführt als Endlosschleife


;-- Timerinterruptroutine -------
Timerinterrupt:

;-- Erneute wertübergabe dan den Timer
ldi tmp, high( Timervorgabewert ) ;Festlegen des Timervorgabewertes
out tcnt1h, tmp
ldi tmp, low( Timervorgabewert ) ;Festlegen des Timervorgabewertes
out tcnt1l, tmp
;---


;---------------------------------------
;-- Hier kommt das rein was durch den --
;-- Timer ausgelöster werden soll --
;---------------------------------------
;-- Beispiel: Veränderung einer LED
sbis PinB,0
rjmp SetLED
rjmp ResLED
;---
;---------------------------------------


;-- Unterroutinen für die LED-Veränderung --
;-- Pin Setzen -----------------
SetLED:
sbi PORTB,0
reti

;-- Pin Rücksetzen -------------
ResLED:
cbi PORTB,0
reti


mfG Tim

teslanikola
30.12.2006, 11:03
Bitte bitte, ich findes wichtig ASM-Anfängern zu helfen obwohl ich selbst no Anfänger bin, ASM ist fürn anfang etwas gewöhnungsbedürftig, aber eine der besten Sprachen ever!