www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik [Assembler] 50Hz mit Timer1 und Interrupts


Autor: Denny (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
[ATmega8]

Hallo
Ich muss mit Hilfe von Timer1 und den Interrupts einen 
50Hz-Rechteckgenerator erzeugen.
Dafür verwende ich den Timer1 mit einem Prescaler von 1024.
Der Quarz hat 6MHz.
Der Interrupt wird bei einem Überlauf ausgelöst.

Berechnungen sind oben im Code.

Code:
.include "m8def.inc"
.org 0x0000

;Quarz = 6Mhz
;6Mhz/1024 = 170µs pro Takt
;Um 50 Herz zu bekommen muss der Interrupt immer bei 10ms ausgelöst werden
;10ms = 10000µs -> 100000/170 = 6Takte
;Timer1-Wert muss dann 1024-6 sein

RJMP Main
RETI
RETI
RETI
RETI
RETI
RETI
RETI
RJMP INT_T1_OVF

Main: 
LDI R16, 0x04                ;Stack Pointer definieren
OUT SPH, R16                ;Stack Pointer definieren
LDI R16, 0x00                ;Stack Pointer definieren
OUT SPL, R16                ;Stack Pointer definieren

LDI R16, 0b11111010              ;Low-Byte des Timer1 Wertes beschreiben 1024-6 = 00000011_11111010
OUT TCNT1L, R16
LDI R16, 0b00000011
OUT TCNT1H, R16

SBI DDRB, 0                  ;Rechteckgenerator: Ausgang

LDI R16, 0b00000101              ;Prescaler = 1024
OUT TCCR1B, R16

LDI R16, 0b00000100              ;T1 Interrupt aktivieren
OUT TIMSK, R16  

SEI                      ;Globale Interrupts aktivieren

Immer: RJMP immer              ;Endlosschleife

INT_T1_OVF:                  ;Interrupt: Overflow von T1
COM R20                    ;Register 20 invertieren
OUT PORTB, R20                ;Und auf PORTB ausgeben (nur PORTB.0 = Output)

LDI R16, 0b11111010              ;Low-Byte des Timer1 Wertes beschreiben 1024-6 = 00000011_11111010
OUT TCNT1L, R16
LDI R16, 0b00000011
OUT TCNT1H, R16
RETI

Leider funktioniert das nicht, wie ich mir das vorstelle.
Der Port wechselt erst nach 11 SEKUNDEN.

Ich weiß, dass man 50 Hz nicht wirklich mit dem Auge sehen kann...

Was mache ich falsch??

Mit freundlichen Grüßen Denny.

Autor: Denny (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich denke da stimmt irgendwas beim Laden des Timers nicht...

Autor: spess53 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

>Leider funktioniert das nicht, wie ich mir das vorstelle.
>Der Port wechselt erst nach 11 SEKUNDEN.

Das ist genau die Zeit, die der Timer zwischen 2 Overflow-Interrupts 
braucht.

>LDI R16, 0b11111010              ;Low-Byte des Timer1 Wertes beschreiben >1024-6 
= 00000011_11111010
>OUT TCNT1L, R16
>LDI R16, 0b00000011
>OUT TCNT1H, R16

Falsch. OUT TCNT1H muss als erstes beschrieben werden.

Das ganze geht wesentlich einfacher, wenn du den CTC-Mode benutzt.

MfG Spess

Autor: Gastofatz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
T/C1 mit Prescaler 8 im CTC-Mode. Output-Compare-Wert 7500 --> alle 10 
ms ein Tick.

Autor: spess53 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

>T/C1 mit Prescaler 8 im CTC-Mode. Output-Compare-Wert 7500 --> alle 10
>ms ein Tick.

Falsch. Der OCR-Wert muss 7499 sein.

MfG Spess

Autor: Denny (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mir ist gerade eingefallen, dass T1 ein 16Bit und nicht ein 10Bit Timer 
ist.

Autor: gtf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi Denny,
habe leider nichtdie Zeit dir eine vollständige lösung zu bieten

Du mußt nur noch den Richtigen CTC Wert eintragen


; Register definitionen_____________
; Hier werden den "General Purpose Working Register" Namen vergeben 

    .def  temp    =r16      //  Bit 1-8      |  32- Bit Akku
    .def    temp2    =r17      //  Bit 9-16    |  32- Bit Akku
    .def  temp3    =r18      //  Bit 17-24    |  32- Bit Akku
    .def  temp4    =r19      //  Bit 25-32    |  32- Bit Akku    
    .def  temp5    =r20
    .def  temp6    =r21




.include "m8def.inc"


.device Atmega8


.cseg

; IRQ____Address_____Routine______________
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 .org  $0000 
  rjmp   RESET       ; Reset Handler
  reti;jmp EXT_INT0    ; IRQ0 Handler
  reti;jmp EXT_INT1    ; IRQ1 Handler
  reti;jmp TIM2_COMP    ; Timer2 Compare Handler
  reti;jmp TIM2_OVF     ; Timer2 Overflow Handler
  reti;jmp TIM1_CAPT     ; Timer1 Capture Handler
;reti;
  rjmp   TIM1_COMPA     ; Timer1 CompareA Handler
  reti;jmp TIM1_COMPB   ; Timer1 CompareB Handler
  reti;jmp TIM1_OVF     ; Timer1 Overflow Handler
  reti;jmp TIM0_OVF     ; Timer0 Overflow Handler
  reti;jmp SPI_STC     ; SPI Transfer Complete Handler
  reti;jmp USART_RXC     ; USART RX Complete Handler 
  reti;jmp USART_UDRE   ; UDR Empty Handler
  reti;jmp USART_TXC     ; USART TX Complete Handler
  reti;jmp ADC_Complete   ; ADC Conversion Complete Handler
  reti;jmp EE_RDY     ; EEPROM Ready Handler
  reti;jmp ANA_COMP     ; Analog Comparator Handler
  reti;jmp TWSI       ; Two-wire Serial Interface Handler
  reti;jmp EXT_INT2     ; IRQ2 Handler
  reti;jmp TIM0_COMP     ; Timer0 Compare Handler
  reti;jmp SPM_RDY     ; Store Program Memory Ready Handler

;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX


RESET:


; Stack___pointer______________
    ldi    temp, high(RAMEND)
    out     SPH, temp
    ldi    temp, Low(RAMEND)
    out    SPL, temp


; I/O_inint______________
    sbi    DDRB, PB0    





; Timer Counter 1 Init , MODE 4 CTC , TOP = OC1A
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX   

    ldi    temp, (0<<COM1A1)|(1<<COM1A0)|(0<<COM1B1)|(0<<COM1B0)|(0<<FOC1A)|(0<<FOC1B)|(0<<WGM11)|(0<<WGM10)
    out    TCCR1A, temp

    ldi    temp, (0<<ICNC1)|(0<<ICES1)|(0<<5)|(0<<WGM13)|(1<<WGM12)|(1<<CS12)|(0<<CS11)|(1<<CS10)  
    out    TCCR1B, temp

    in    temp, TIMSK
    sbr    temp, 1<<OCIE1A
    out    TIMSK, temp




; CTC- Wert 
    ldi    temp, Low (58)
    ldi    temp2, High (58)
    out    OCR1AH, temp2
    out    OCR1AL, temp

    sei



;***********************************    
;***********************************
;*       Hauptprogramm      **
;***********************************
;***********************************
Programm:
    nop
    rjmp  Programm








;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
TIM1_COMPA:      ; Timer1 CompareA Handler (IRQ)

COM R20                    ;Register 20 invertieren
OUT PORTB, R20                ;Und auf PORTB ausgeben (nur PORTB.0 = Output)

  reti

Autor: Denny (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe es nun geschafft!
.include "m8def.inc"
.org 0x0000

;Reihenfolge sehr wichtig

RJMP Main                  ;Reset Interrupt
RETI
RETI
RETI
RETI
RETI
RJMP Gleich                  ;Timer1: Komparator A Interrupt

Main:
LDI R16, 0x04                ;Stack Pointer definieren
OUT SPH, R16
LDI R16, 0x00
OUT SPL, R16 

SER R16                    ;PORTB als Ausgang
OUT DDRB, R16

LDI R16, 0b00011101              ;Komparator A von Timer1 High-Byte
OUT OCR1AH, R16

LDI R16, 0b01001100              ;Komparator A von Timer1 Low-Byte
OUT OCR1AL, R16

LDI R16, 0b00000010              ;Prescaler = 8
OUT TCCR1B, R16

LDI R16, 0b00010000              ;Komparator A Interrupt aktivieren
OUT TIMSK, R16  

SEI                      ;Interrupts global aktivieren

Immer: RJMP Immer

Gleich:
COM R20                    ;Register invertieren
OUT PORTB, R20                ;Auf den Port ausgeben

CLR R16                    ;Timer1 auf 0 setzen
OUT TCNT1H, R16                ;Muss aber nicht sein
OUT TCNT1L, R16                ;Damit er von vorne anfängt
RETI

Danke an euere Hilfe!

Autor: spess53 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

Dein Comparewert ist nicht korrekt. Muss $8D8B nicht $8D8C sein.

Warum schreibst du eigentlich so unverständlich?

>LDI R16, 0b00011101 ->  ldi r16,High($8D8B)

>LDI R16, 0b01001100 ->  ldi r16,Low($8D8B)

>LDI R16, 0b00010000 ->  ldi r16,1<<OCIE1A

>OUT TCNT1H, R16                ;Muss aber nicht sein
>OUT TCNT1L, R16                ;Damit er von vorne anfängt

Unnötig. Wird automatisch gemacht.

MfG Spess

Autor: Drachenbändiger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Gastofatz hat schon einen guten Vorschlag gemacht .... warum nimmst Du 
den nicht an? Lass doch die Hardware alles machen! Dann sähe es nämlich 
so aus (den Grund für die Wahl von OCR1A lass' ich Dir als Übung):
.include "m8def.inc"

.def wr=r16

.org 0
  rjmp ini_        ; reset vector
ini_:
    
    sbi DDRB, 1<<PB1   ; OC1A is output

    ldi wr, high (RAMEND)
    out SPH, wr
    ldi wr, low(RAMEND)
    out SPH, wr

    // initialize TC1
    ldi wr, high(60000) ; 
    out OCR1AH, wr
    ldi wr, low(60000)
    out OCR1AL, wr

    ldi wr, 1<<COM1A0 ; toggle OC1A on compare match
    out TCCR1A, wr

    ldi wr, (1<<CS10) | (1<<WGM12)  ; FCLOCK/1, CTC mode
    out TCCR1B, wr ;

be_lazy:
    rjmp be_lazy;

Autor: Gastofatz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>LDI R16, 0b00011101              ;Komparator A von Timer1 High-Byte
>OUT OCR1AH, R16
>
>LDI R16, 0b01001100              ;Komparator A von Timer1 Low-Byte
>OUT OCR1AL, R16

Dasselbe, aber leichter zu lesen:

LDI R16, High(7500)
OUT OCR1AH, R16

LDI R16, Low(7500)
OUT OCR1AL, R16

Kollege spess53 hat übrigens recht: Der in das Output-Compare-Register 
zu schreibende Wert muss um 1 kleiner sein (!) als der errechnete Wert. 
So wäre es also ganz korrekt:

LDI R16, High(7500-1)
OUT OCR1AH, R16

LDI R16, Low(7500-1)
OUT OCR1AL, R16

Und das hier...

>CLR R16                    ;Timer1 auf 0 setzen
>OUT TCNT1H, R16                ;Muss aber nicht sein
>OUT TCNT1L, R16                ;Damit er von vorne anfängt

...ist purer Nonsens. Lass TCNT1 in Frieden. Der Timer zählt im CTC-Mode 
von selbst auf den Takt genau richtig (das ist ja der Sinn der Sache), 
und Du bekommst damit die 50 Hz mit der höchstmöglichen Präzision 
(16000000 MHz-Quarz --> 50.00000 Hz). Herumfummeln an TCNT1 kanns nur 
schlechter machen.

Autor: Drachenbändiger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oh -- Du sollst ja Interrupts benutzen --- dann vergiss meine schöne 
Methode mit CTC und WGM12=1 mal schnell! Ist sicher auch 'ne gute Übung 
mit den Interrupts, selbst wenn damit Ressourcen verbraten werden.

Autor: Drachenbändiger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nachtrag: 59999 statt 60000 für OCR1A bei 6MHz SysClk und TC1-Teiler=1, 
um alle 10ms eine Änderung an OC1A zu erhalten.

Autor: Mike Dupo (hero2992)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo... ich werde nicht schlau aus den berechnungen für OCR1A..

Wie muss ich vorgehen um den wert bei 3,868400Mhz zu errechnen?

MFG Hero

Autor: Klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn du  f=3,868400Mhz hast und eine Prescaler von 1024 wird der Timer 
mit f/1024= 3777,734375 Hz getaktet. Bei f_out=25Hz sind für 1/50s genau 
75,5546875 Takte pro Halbwelle.

Wenn du rückwärts zählst, lädst du den Timer1 mit 75 oder 76. Wenn du 
aufwärts zählst dann lädst du den Timer1 mit 2^16 - 75 (76), weil beim 
Erreichen der 0 der IRQ ausgelöst wird. Wenn das Ausgabebit nur 
wechselt, erhältst du 25Hz. Für 50Hz selbst rechnen.

Mit Prescaler=1024 ist das relativ ungenau.

Für Prescaler=1 ist f/1= 3868400 Für 1/100s erhältst du genau 38684 
Takte.
Also diese Zahl in den Timer laden, wenn du rückwärts zählst, dan das 
Ausgabebit ändern. Beim Aufwärtszählen 2^16-38684 in den Timer laden.

Diese 50Hz sind so genau wie dein Quarz.

kl

Autor: Mike Dupo (hero2992)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank Klaus...hast mir sehr geholfen...

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.