Timer ATMEGA 2560

Gast #4454597
Lesenswert?

Hallo

Ich versuche den Timer5 des ATMEGA 2560 zu nutzen, doch klappt das 
leider nicht. Ich möchte, dass die ISR jede Sekunde einmal aufgerufen 
wird. Bisher habe ich folgendes gemacht:
1
#define wait_ms 1000
2

3
void setup() {
4
  //Init Serial
5
  Serial.begin(9600);
6

7
  //init Timer
8
  TCCR5B |=(1<<CS51);                         //8 prescaler -> 0.5us cycles
9
  TIMSK5 |=(1<<ICIE5)+(1<<OCIE5A);            //Interupt enable and compare A enable
10
  OCR5AH = (wait_ms*1000*1000/500)>>8;
11
  OCR5AL =(wait_ms*1000*1000/500);
12
  TCNT5=0;
13
  sei();                                      //global Interrupt
14
}
15

16
void loop() {
17

18
}
19

20
ISR(TIMER5_COMPA) {
21
    Serial.write('x');
22
    TCNT5=0;
23
}

Leider gibt es mir keinen Interrupt. Habe ich den ISR Interrupt Vektor 
falsch benannt?

Gruss Gary
Gast #4454623
Lesenswert?

Hallo,

da hast Du nicht in das Datenblatt des AVR gesehen, und dir fehlen 
grundlegende C Kenntnisse.

Bit Manipulation. Bit-Masken werden über ODER (OR) verknüpft.

Wenn man diesen Interrupt ICIE5 freigibt, dann sollte man auch einen 
Interrupthandler dazu linken.

Im Datenblatt steht auch, wie man den CTC Modus aktiviert.

Dazu muss man die richtigen Bits in den Registern TCCR5A und TCCR5B 
setzen.
Mode 4: CTC WGM5[3:0] = 0100

Dann muss man auch OCR5A Register für den als TOP-Wert wählen.

Auch passen bei 8MHz = 8*10⁶Hz mit einem Vorteiler von 8, also Timer5 
Clock = 8*10⁶Hz /8 = 1*10⁶Hz diese nicht in einen 16Bit Zähler (OCR5A).

Maximal also: 2¹⁶-1 = 65535

Somit muss man den Zähler 5 noch um einen Softwarezähler um weitere Bits 
erweitern, oder einen anderen Vorteiler (Prescaler) verwenden.
Mit T5 Prescaler = 1024 ergibt sich:
T5 PRELOAD = uint16_t(1.0 * 8*10⁶ /1024 +.5) -1 = 7812 ; mit Aufrunden
Der Fehler ist dann minimal.

Deine Interrupt Service Routine ist fehlerhaft, man nutzt dort kein 
Serial.write() und setzt nicht das Zählerregister 5 auf 0.
Gast #4454656
Lesenswert?

OCR5AH = (wait_ms*1000*1000/500)>>8;
  OCR5AL =(wait_ms*1000*1000/500);

???

bist du dir sicher das, dass richtig ist ?
Nutz man nicht den Wert des Quarzes als Basis der Berechnung ?
Muss der Wert nicht bei jeder ISR wieder hinterlegen werden ?
ich denke mal eher so:

OCR5AH = (wait_ms*1000*1000/500)<<8;
OCR5AL =(wait_ms*1000*1000/500)&0xFF;

oder eher so:
bei 16MHz F_CPU
OCR5AH = (65535-(F_CPU/8/100))<<8;
OCR5AL =(65535-(F_CPU/8/100))&0xFF;
macht aber dann alle 10ms die ISR
Also, größeren Teiler !!

ähm... :
1000*1000*1000/500=2000000 ???
sind doch mehr wie 16bit !?

P.S.
Vorher mal auf nem Blatt Papier mal Rechnen ,
Was macht der ATmeag da eigentlich ?
Wie viele Impulse bekomme ich bei einem Teiler von zum Beispiel 8 in den 
Timer , pro Sekunde ???

MFG
Gast #4454660
Lesenswert?

N.Sekan schrieb:
> Nutz man nicht den Wert des Quarzes als Basis der Berechnung ?

Dieser Wert ist in den 500 eingeflossen: die 500 stellen die 500ns cycle 
dar, aufgrund des prescalers 8.

N.Sekan schrieb:
> 1000*1000*1000/500=2000000 ???
> sind doch mehr wie 16bit !?

Das stimmt, habe das nun geändert, geht aber immer noch nicht.
1
#define wait_us 1000000
2

3
void setup() {
4
  //Init Serial
5
  Serial.begin(9600);
6

7
  //init Timer
8
  TCCR5B |=(1<<CS52);                         //256 prescaler -> 16us cycles
9
  TIMSK5 |=(1<<ICIE5)+(1<<OCIE5A);            //Interupt enable and compare A enable
10
  int val = wait_us/16-1;
11
  OCR5AH = (val)>>8;
12
  OCR5AL = val;
13
  TCNT5=0;
14
  sei();                                      //global Interrupt
15
}
16

17
void loop() {
18

19
}
20

21
ISR(TIMER5_COMPA_vect) {
22
    Serial.write('x');
23
    TCNT5=0;
24
}
Gast #4454664
Lesenswert?

Gary,

ich habe Dir alle Fehler aufgezeigt und auch zwei Beispielrechnungen 
gegeben, wenn Du es nicht für nötig ansiehst im Datenblatt das zu 
verifizieren und auch zu verwenden, na dann..

Dann brauchst Du hier auch nicht mehr schreiben.

Selbst Die Formel für die verschiedenen Zählereinstellungen stehen im 
Datenblatt.

20.4.2 Clear Timer on Compare Match (CTC) Mode
Die Formel ist als "waveform frequency" angegeben !
Eine Formel umstellen wird bestimmt noch klappen.
#4454666
Lesenswert?

>#define wait_us 1000000

wenn ich mich recht erinner dann doch nur so:

#define wait_us 1000000UL

sonst kommt das eh nicht an

und:

>int val = wait_us/16-1;

dann so

int val = (wait_us/16UL)-1UL;

oder

int val = (int)((unsigned long)(wait_us/16UL)-(unsigned long)1UL);

wenns richtig ankommen soll, evtl. habe ich irgendwo doppelt gemoppelt, 
aber vergessene cast sind schlimmer
Gast #4454668
Lesenswert?

wie gesagt :
Mit wie viel MHZ läuft der Chip ?

bei 16MHz
durch 256 = 62500 Takte pro Sekunde
also muss 62500 als Vergleichswert im OCRA stehen !
Denk ich mal

Datenblatt mal lesen !!!
steht meist alles drin !!
Gast #4454681
Lesenswert?

Du kannst übrigens in C direkt OCR5A = uint16_t value schreiben, also 
z.B. OCR5A = 65000;.

Weiterhin wie bereits erwähnt: Du hast den Input Capture Interrupt an! 
Aber du hast nur den COMPA Interruptvektor definiert.
ISR gehört btw. über die main() wegen C forward Deklaration aber kann 
sein dass das in C++ umwichtig ist.
Gast #4454684
Lesenswert?

Karl M. schrieb:
> C-Grundlagen !
>
> was kommt hier wohl bei 16 Bit Arithmetik heraus ?#define wait_us
> 1000000
> int val = wait_us/16-1;

62499, was ein valider Wert für OCRxA ist. Aber wenn man es in einen int 
packt, führt der Überlauf zu...32767 oder so? In dem Fall wäre der 
Interrupt einfach doppelt so schnell wie gewollt. Das kanns nicht sein.
Gast #4454699
Lesenswert?

Ok, ich habe versucht mal alles zu berücksichtigen, doch es klappt immer 
noch nicht. Nur bei einem prescaler kleiner 64 gibt es mir 'x' aus.
1
#define wait_us 1000000UL
2
#define F_CPU 16000000UL
3

4
bool new_data=0;
5

6
void setup() {
7
  //Init Serial
8
  Serial.begin(9600);
9

10
  //init Timer
11
  TCCR5B |=(1<<CS52)+(1<<CS50)+(1<<WGM52);          //1024 prescaler -> 64us cycles, activate CTC
12
  TIMSK5 |=(1<<OCIE5A);                             //Interupt enable and compare A enable
13
  uint16_t val = (wait_us/64UL)-1;
14
  OCR5A = val;
15
  sei();                                            //global Interrupt
16
}
17

18
void loop() {
19
    if(new_data) {
20
        new_data=0;
21
        Serial.write('x');
22
    }
23
}
24

25
ISR(TIMER5_COMPA_vect) {
26
    new_data=1;   
27
}
Gast #4454714
Lesenswert?

Nur mal so interessehalber...was hindert dich daran, deine Setup-Routine 
mal mit Serial.Write zuzupflastern und bei jeder Zeile zu gucken was 
hinten rauskommt? Das ist ein Debugging-Tool, das sollte man zum 
debuggen verwenden!

Fuse-Settings wären ganz gut, Vielleicht ist die Watchdog-Fuse gesetzt. 
Aber der Rest ist auch interessant.



  uint16_t val = (wait_us/64UL)-1;
  OCR5A = val;

Geht auch OCR5A = 15624;.
wait_us/64UL (unsigned long! Für 6 bit!) wird beim kompilieren eh genau 
dazu.

Bist du dir sicher, dass es nur mit Werten kleiner 64 geht?

  TCCR5B |=(1<<CS52)+(1<<CS50)+(1<<WGM52);

Das mit den Pluszeichen wäre mir neu. Guck dir mal das AVR-GCC Tutorial 
OBEN LINKS an.
Moderator Persönliche Seite #4454742
Lesenswert?

S. Landolt schrieb:
> Au contraire, das ist steinalt, so habe ich es vor Ewigkeiten in der
> Schule in Schaltalgebra gelernt.

Die hat aber nichts mit der Programmiersprache C zu tun. ;-)

Allerdings ist das in diesem Falle egal, wenn man zwei Zahlen addiert,
in denen je ein Bit gesetzt ist, welches in beiden Zahlen verschieden
ist (das ist hier gegeben), dann hat eine Addition das gleiche Ergebnis
wie eine VerODERung.

Ja, volatile für die Auswertevariable ist auf jeden Fall Pflicht.
Gast #4454746
Lesenswert?

Dann ist es unsinnig den Timer5 schon zu starten
1
TCCR5B |=(1<<CS52)+(1<<CS50)+(1<<WGM52);
bevor er vollständig initialisiert und konfiguriert ist.

Und so schreibe ich den TOP-Zählerwert von Timer5 richtig hin:
1
#include <stdint.h>
2
const uint32_t T5_FREQUENCY = 1ul; // Hz
3
const uint32_t T5_PRESCALER = 1024ul;
4
const uint32_t T5_PRELOAD = uint32_t(1.0 * F_CPU /T5_PRESCALER /T5_FREQUENCY+.5)-1;
5
if (T5_PRELOAD >= 65536) 
6
{
7
 // error("T5 Prescaler !");
8
}
9
OCR5A = T5_PRELOAD;
10
; wichtig T5_PRELOAD < 65536 !
Gast #4454751
Lesenswert?

> Die hat aber nichts mit der Programmiersprache C zu tun. ;-)
> Allerdings ist das in diesem Falle egal

Wir wollen Assembler nicht vergessen.
Und einen kleinen Unterschied gibt es: ein Zehnfingerschreiber tut sich 
mit dem '+' leichter als mit dem '|'.
Gast #4454772
Lesenswert?

S. Landolt schrieb:
>> Die hat aber nichts mit der Programmiersprache C zu tun. ;-)
>> Allerdings ist das in diesem Falle egal
>
> Wir wollen Assembler nicht vergessen.
> Und einen kleinen Unterschied gibt es: ein Zehnfingerschreiber tut sich
> mit dem '+' leichter als mit dem '|'.

Eigentlich nicht, aber ich habe ein QWERTY Keyboard auf meinem Thinkpad 
drauf und da gibts die Taste nicht. Daher trotzdem danke.

Und ja: Es ist boolsche Algebra und ja, das Pluszeichen ergibt Sinn. Ich 
habe ja auch nur gesagt, dass es mir NEU ist.
Gast #4454775
Lesenswert?

Habe das nochmal überarbeitet, doch am Wert im OCR5A liegt es nicht, der 
ist richtig. Sobald der prescaler zu hoch ist, ändert sich der Wert in 
TCNT5 nicht mehr.
1
#define wait_us 1000000UL
2
#define F_CPU 16000000UL
3
#define prescaler 1024UL
4

5
volatile bool new_data=0;
6
int val;
7

8
void setup() {
9
  //Init Serial
10
  Serial.begin(9600);
11

12
  //init Timer
13
  val = int(wait_us*(F_CPU/1000000)/prescaler+0.5)-1;
14
  if(val > 65536) {
15
      Serial.write("error");
16
  } else {
17
    TCCR5B |=(1<<CS52)+(1<<CS50)+(1<<WGM52);          //8 prescaler -> 0.5us cycles, activate CTC
18
    TIMSK5 |=(1<<OCIE5A);                             //Interupt enable and compare A enable
19
    OCR5A = val;
20
    sei();                                            //global Interrupt
21
  }
22
}
23

24
void loop() {
25
    if(new_data) {
26
        new_data=0;
27
        Serial.write('x');
28
    }
29
}
30

31
ISR(TIMER5_COMPA_vect) {
32
    new_data=1;   
33
}
Gast #4454781
Lesenswert?

Diese Ignoranz !

Ich habe Dir die Formel noch hingeschrieben und was machst Du daraus ?
Code der falsch ist !

Was steht wohl in val ?

Welchen Prescaler verwendest Du ?

//8 prescaler -> 0.5us cycles, activate CTC
Gast #4454797
Lesenswert?

if(val > 65536) {
      Serial.write("error");

Also wenn val ein int ist, ist der auf AVR wenn ich mich nicht irre ein 
signed int 16 bit. Wie kann der bitte größer als 65536 werden? Nichtmal 
ein uint16_t könnte das!

  val = int(wait_us*(F_CPU/1000000)/prescaler+0.5)-1;

Hier wüsste ich selbst nicht so wirklich was dabei rauskommt. Versuch:
wait_us*(F_CPU/1000000)= 16 Mio. Furchtbar schlau, erst eine Million 
wegzudividieren, um sie danach wieder draufzumultiplizieren.

16 Mio / prescaler = 15625

+0.5 = vermutlich 15625.

Das ganze in einen int casten (wobei ein cast eher 
(uint16_t)ZuCastenderWert) ist): 15625

1 abziehen: 15624. Wie gut dass ich das vor einer Stunde schon so 
geschrieben hatte. Systematisch Fehlerquellen zu eliminieren (wie deine 
komplizierten Berechnungen) ist nicht so dein Ding, was.



Und was hindert dich an Serial.write(val)? Oder falls das nicht klappt, 
die bekannte itoa-Angelegenheit.
Gast #4454816
Lesenswert?

Garry,

schreibe doch bitte die Timer 5 OCR5A Init genau so und berichte bitte:
1
; im kopfbereich
2
#include <stdint.h>
3

4
; im code direkt nacheinander.
5
const uint32_t T5_FREQUENCY = 1ul; // Hz
6
const uint32_t T5_PRESCALER = 1024ul;
7
const uint32_t T5_PRELOAD = uint32_t(1.0 * F_CPU /T5_PRESCALER /T5_FREQUENCY+.5)-1;
8
OCR5A = T5_PRELOAD;

Dann poste bitte das Assemblerlisting hier.
Gast #4454886
Lesenswert?

S. Landolt schrieb:
> itoa(OCR1AL,buff,10);
> itoa(OCR1AH,buff,10);

Das gibt mit 8 für OCR1AL und 0 für OCR1AH.
Wenn ich OCR1A=256 setzte, gibt mir das 0 für OCR1Al und 0 fürOCR1AH,
als wäre kein OCR1AH vorhanden.

Mir ist aufgefallen, dass bei meinem Arudino Mega 2560 made in china 
draufsteht, sollte doch made in italy sein, habe den auf ebay gekauft 
und der war nicht gerade billig. Wenn es sich um eine Fälschung handelt, 
ist es möglich, dass die irgendetwas verbaut haben, dass nur 8bit Timer 
hat?
Gast #4454912
Lesenswert?

Bevor ich mich in die Fasnet (sic!) verabschiede, eine Bitte:

OCR5A= 0x1234;
itoa(OCR5AL,buff,10);
Serial.println(buff);
itoa(OCR5AH,buff,10);
Serial.println(buff);

Welche Ausgabe liefert dies?



(Hoorig isch die Katz)
Gast #4454913
Lesenswert?

Zunächst solltest du deine Registerzugriffe wie folgt abändern, da 
dieses Verodern mit dem zuvor enthaltenen Inhalt zu Fehlern führen kann. 
Dabei bitte auch Jörgs Anmerkung mit einarbeiten:
1
TCCR5A = 0;
2
TCCR5B = (1<<CS52) | (1<<CS50) | (1<<WGM52);          //8 prescaler -> 0.5us cycles, activate CTC
3
TIMSK5 = (1<<OCIE5A);                             //Interupt enable and compare A enable

Das bitte einmal ausprobieren. Dieses Verodern bei Arduino war in einem 
anderen Beitrag schonmal der Fehler.
Gast #4454935
Lesenswert?

Arduino belegt den Inhalt wohl als Vorbereitung für irgendwas vor. Daher 
beim ersten schreibenden Zugriff auf Register besser nur ein "=" statt 
"|=" benutzen und besser alle Register initialisieren.
Gast #4454941
Lesenswert?

Hallo,

nicht dass jemand noch schreib, dass das nicht genannt wurde:

Karl M. schrieb:
> Dazu muss man die richtigen Bits in den Registern TCCR5A und TCCR5B
> setzen.
> Mode 4: CTC WGM5[3:0] = 0100

Karl M. schrieb:
> Noch eine Anmerkung,
>
> guter Stil ist es alle relevanten Register von Timer5 zu initialisieren.
>
> TCCR5A = ...
> dort finden wir die Bits WGM51 WGM50.


Es kommt leider nur bei Gary mit der Holzhammer Methode an.
Gast #4454951
Lesenswert?

Hallo,

vielleicht stand da vorher einer dieser Mode drin:

Timer 5
17.9 Modes of Operation

1: PWM, Phase Correct, 8-bit
5: Fast PWM, 8-bit

oder eine Mode mit TOP: ICR5
8 : PWM, Phase and Frequency Correct
10: PWM, Phase Correct
12: CTC Mode
Moderator Persönliche Seite #4455033
Lesenswert?

Gary schrieb:
> Karl M. schrieb:
>> schreibe doch bitte die Timer 5 OCR5A Init genau so und berichte bitte:
>
> Habe das nun so gemacht und hier dazu noch das .ELF
>
> OCR5AH bleibt immer noch 0.

Hier das entsprechende Stück aus dem Disassembler:
1
00000144 <setup>:
2
 144:   26 e0           ldi     r18, 0x06       ; 6
3
 146:   40 e8           ldi     r20, 0x80       ; 128 ; 0x2580 => 9600
4
 148:   55 e2           ldi     r21, 0x25       ; 37
5
 14a:   60 e0           ldi     r22, 0x00       ; 0  ; 0x22D => Adresse von Serial (Objekt, this-Pointer)
6
 14c:   70 e0           ldi     r23, 0x00       ; 0
7
 14e:   8d e2           ldi     r24, 0x2D       ; 45
8
 150:   92 e0           ldi     r25, 0x02       ; 2
9
 152:   60 d1           rcall   .+704           ; 0x414 <HardwareSerial::begin(unsigned long, unsigned char)>
10
 154:   88 e0           ldi     r24, 0x08       ; 8  ; 0x3D08 => 15624
11
 156:   9d e3           ldi     r25, 0x3D       ; 61
12
 158:   90 93 2b 02     sts     0x022B, r25          ; 22A => Variable "val" (warum wird die nicht wegoptimiert?)
13
 15c:   80 93 2a 02     sts     0x022A, r24
14
 160:   e1 e2           ldi     r30, 0x21       ; 33 ; 0x121 => TCCR5B
15
 162:   f1 e0           ldi     r31, 0x01       ; 1
16
 164:   80 81           ld      r24, Z               ; nach R24 laden
17
 166:   8d 60           ori     r24, 0x0D       ; 13 ; WGM52, CS51 und CS50 setzen
18
 168:   80 83           st      Z, r24               ; nach TCCR5B zurückschreiben
19
 16a:   e3 e7           ldi     r30, 0x73       ; 115 ; 0x73 => TIMSK5
20
 16c:   f0 e0           ldi     r31, 0x00       ; 0
21
 16e:   80 81           ld      r24, Z
22
 170:   82 60           ori     r24, 0x02       ; 2   ; OCIE5A setzen
23
 172:   80 83           st      Z, r24                ; nach TCCR5B zurückschreiben
24
 174:   8f ef           ldi     r24, 0xFF       ; 255 ; 65536 laden
25
 176:   9f ef           ldi     r25, 0xFF       ; 255
26
 178:   90 93 29 01     sts     0x0129, r25           ; nach OCR5A schreiben
27
 17c:   80 93 28 01     sts     0x0128, r24
28

Danach folgt noch die Ausgabe von val und OCR5AH.

Da wird OCR5A auf 0xFFFF (oder 65535) gesetzt, und nicht auf den
Wert von val!

Irgendwie passt das Compilat nicht zum Sourcecode.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren