Forum: Mikrocontroller und Digitale Elektronik ATmega32U4: HS-Speed ISR(TIMER4_COMPA_vect)


von M. K. (sylaina)


Lesenswert?

Ich hab zwei kleine Fragen zum Compare-Match-Interrupt des 
Hi-Speed-Timers 4 (mit dem Overflow-Interrupt sieht es übrigens genauso 
aus) und zwar hab ich da heute den ganzen Tag probiert aber es klappt 
nicht wie ich mir das vorstelle (und mit Datenblatt/Google bin ich auch 
nicht weiter gekommen). Unten seht ihr mein Programm, ich benutze als 
Testboard ein Arduino Leonardo. Fuses sind wie beim Leonardo-Board 
eingestellt. PWM klappt übrigens hervorragend, daher weiß ich, dass der 
Timer zumindest läuft (beachte 2. Frage).

1. Anhand meines Tests weiß ich, dass die ISR nicht angesprungen wird 
denn PB5 toggelt nicht so wie ich will (mit 50 kHz), aber was habe ich 
hier vergessen zu konfigurieren? Es geht mir hierbei nicht darum einen 
OC4x-Pin anzusteuern.

2. Die while-Schleife, in der ich das PLOCK Polle (soll man lt. 
Datenblatt) funktioniert auch nicht (egal ob auf 1 oder 0 geprüft). 
Nehme ich, wie es unten steht, die Zeile 27 rein bleibt das Programm da 
hängen. Kommentiere ich es aus, dann läuft auch das Programm weiter. 
Verstehe ich irgendwie auch nicht wirklich. Sieht jemand was da das 
Problem ist?

Es sind wahrscheinlich völlig lächerliche Probleme aber ich hab nen 
Knoten im Kopf und sehe das Problem nicht. Das Programm unten liefert 
mir ein ca. 800 Hz Signal, sollte so aber eigentlich ein 50 kHz Signal 
generieren.
1
#include <avr/io.h>
2
#include <util/delay.h>
3
4
void setCompareValue(uint8_t compareValue){
5
  uint8_t sreg = SREG;
6
  cli();
7
  TC4H = (compareValue >> 8);
8
  OCR4A = (compareValue & 0xff);
9
  SREG = sreg;
10
}
11
12
void main(void){
13
  // Testpin sei PB5
14
  DDRB |= (1 << PB5);
15
16
  //PLL einstellen
17
  // uC laeuft mit 16MHz, PINDIV daher setzen (Kapitel 6.11.5)
18
  PLLCSR = (1 << PINDIV);
19
20
  // PLL fuer Timer einstellen (Kapitel 15.3.1)
21
  // Enable PLL
22
  PLLCSR = (1 << PLLE);
23
  // wait 100us for stabilize PLL
24
  _delay_us(100);
25
  // pull PLOCK-Bit until it is set
26
  while( !(PLLCSR & (1 << PLOCK)) );
27
  // Postscaler auf 1, PLL fuer 72 MHz konfigurieren
28
  PLLFRQ |= (1 << PLLTM0)|(1 << PDIV2)|(1 << PDIV1)|(1 << PDIV0);
29
  
30
  // Timer einstellen
31
  // TOP-Value Timer einstellen
32
  TC4H = 720 >> 8;
33
  OCR4C = 720 & 0xff;
34
  // Compare-Value einstellen
35
  setCompareValue( 0x32 );
36
  // Compare Match Interrupt einschalten
37
  TIMSK4 = (1 << OCIE4A);
38
  // Timer starten
39
  TCCR4B = (1 << CS40);
40
  // Interrupts einschalten
41
  sei();
42
43
  for(;;){
44
    // nix zu tun, Test HS-Timer-ISRs
45
  }
46
}
47
48
ISR(TIMER4_COMPA_vect){
49
  PORTB ^= (1 << PB5);
50
}

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Hallo,

inkludiere einmal die Interrupt Lib.

von M. K. (sylaina)


Lesenswert?

Veit D. schrieb:
> Hallo,
>
> inkludiere einmal die Interrupt Lib.

Ach, das hab ich beim Abtippen gestern vergessen. Die ist inkludiert.

von Veit D. (devil-elec)


Lesenswert?

Okay, also Stand heute ist, der Pin taktet mit 800Hz statt 50kHz?
Ich frage weil du eingangs schreibst das nichts taktet. Bitte nochmal 
klar stellen.

von M. K. (sylaina)


Lesenswert?

Veit D. schrieb:
> Okay, also Stand heute ist, der Pin taktet mit 800Hz statt 50kHz?

Richtig. Es funktioniert nur so wie ich es erwarte.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

Timer 4 kann nur bis 64MHz, egal ob die PLL noch höher takten könnte.
Das muss man bei der gesamten Kette von Einstellungen beachten.
Desweiteren verkraftet die PLL Eingangsseitig nur max. 8MHz.

Das heißt du musst die 16MHz CPU Takt am Eingang der PLL erstmal 
halbieren.
Im Manual unter 6.9 gibts den Aufbau der PLL zu sehen.
Das Ergebnis was hinten an clkTMR rauskommt bekommt der Timer 4 zu 
sehen.

PLOCK brauchte meinem Verständnis nach nicht. Das delay damit eigentlich 
auch nicht. Sollte nur für PLOCK Nutzung wichtig sein.

Vorschlag, probier mal
1
/*
2
    PDIV3 .... PLL Output Frequency 96MHz
3
    PDIV1 .... PLL Output Frequency 96MHz
4
    PLLTM1 ... Postscaler Factor 1,5 for High Speed Timer
5
*/
6
7
  // 96MHz durch 1,5 = 64MHz // 
8
  PLLFRQ = _BV(PLLTM1) | _BV(PDIV3) | _BV(PDIV1);
9
10
  // 16MHz Eingangstakt halbieren und PLL starten 
11
  PLLCSR = _BV(PINDIV) | _BV(PLLE);
12
  
13
  // wait 100us for stabilize PLL
14
  _delay_us(100);

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Hallo,

nochwas, der Grundaufbau sollte so lauten.

int main(void)
{

  while(1)
  {

  }

}

von M. K. (sylaina)


Angehängte Dateien:

Lesenswert?

Veit D. schrieb:
> Das heißt du musst die 16MHz CPU Takt am Eingang der PLL erstmal
> halbieren.

Das macht das PINDIV-bit (Zeile 18) ;)

Veit D. schrieb:
> PLOCK brauchte meinem Verständnis nach nicht. Das delay damit eigentlich
> auch nicht. Sollte nur für PLOCK Nutzung wichtig sein.

Ich verstand das so, dass man das PLOCK pollen soll, sodass man sicher 
sein kann, dass die PLL auch mit der Wunschfrequenz stabil läuft. 
Bräuchte ich in der aktuellen Anwendung nicht, richtig, aber es wäre 
halt schön wenn es funktionieren würde.

Das mit den 64 MHz ist richtig, ich hab mal die PLL auf 48 MHz 
umgestellt. Das sah zunächst gut aus (SHS00001.BMP). Dann hab ich 
versucht den Pin nicht mehr zu toggeln sondern im Compare-Interrupt auf 
0 zu setzen und im Overflow-Interrupt auf 1 zu setzen und dann kam das 
raus, was hier in SHS00002.BMP zu sehen ist.

aktuelles Programm
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <util/delay.h>
4
5
void setCompareValue(uint8_t compareValue){
6
  uint8_t sreg = SREG;
7
  cli();
8
  TC4H = (compareValue >> 8);
9
  OCR4A = (unsigned char)compareValue;
10
  SREG = sreg;
11
}
12
13
int main(void){
14
  // Testpin sei PB5
15
  DDRB |= (1 << PB5);
16
17
  //PLL einstellen
18
  // uC laeuft mit 16MHz, PINDIV daher setzen (Kapitel 6.11.5)
19
  PLLCSR = (1 << PINDIV);
20
21
  // PLL fuer Timer einstellen (Kapitel 15.3.1)
22
  // Enable PLL
23
  PLLCSR = (1 << PLLE);
24
  // wait 100us for stabilize PLL
25
  _delay_us(100);
26
  // pull PLOCK-Bit until it is set
27
  while( !(PLLCSR & (1 << PLOCK)) );
28
  // Postscaler auf 1, PLL fuer 72 MHz konfigurieren
29
  PLLFRQ |= (1 << PLLTM0)|(1 << PDIV2);
30
  
31
  // Timer einstellen
32
  // TOP-Value Timer einstellen
33
  TC4H = 720 >> 8;
34
  OCR4C = 720 & 0xff;
35
  // Compare-Value einstellen
36
  setCompareValue( 360 );
37
  // Compare Match Interrupt einschalten
38
  TIMSK4 = (1 << OCIE4A)|(1 << TOV4);
39
  // Timer starten
40
  TCCR4B = (1 << CS40);
41
  
42
  // Interrupts einschalten
43
  sei();
44
  for(;;){
45
    // nix zu tun, Test HS-Timer-ISRs
46
  }
47
  return 0;
48
}
49
ISR(TIMER4_COMPA_vect){
50
  PORTB &= ~(1 << PB5);
51
}
52
ISR(TIMER4_OVF_vect){
53
  PORTB |= (1 << PB5);
54
}

von Veit D. (devil-elec)


Lesenswert?

Hallo,

was ist der Unterschied zwischen unseren Konfigurationen von PLLCSR? 
;-)
Ich denke danach sollte es laufen.

von M. K. (sylaina)


Angehängte Dateien:

Lesenswert?

Veit D. schrieb:
> was ist der Unterschied zwischen unseren Konfigurationen von PLLCSR?

Ich hab ehrlich gesagt keine Ahnung wo jetzt der | abgeblieben ist. Im 
Quellcode ist er drin in Zeile 23, vgl. Screenshot ;)

Veit D. schrieb:
> Ich denke danach sollte es laufen.

Ich denke ja eigentlich auch, dass es laufen sollte aber das tut es eben 
nicht. Und das ist ja das, was mich verwirrt. ;)

von Veit D. (devil-elec)


Lesenswert?

Hallo,

dann hilft nur noch radikaler Schritt zurück.
Was macht der Code?
Wenn das passt, dann baust du in diesen! Code das PLL ein.
Erstmal ohne weiteren Pin. Nur dem Timer den PLL zuführen.
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <util/delay.h>
4
5
void setCompareValue(const uint16_t compareValue)
6
{
7
  TC4H = (compareValue >> 8);
8
  OCR4A = (compareValue & 0xFF);
9
}
10
11
int main(void)
12
{
13
  DDRB |= (1 << PB5);     // Testpin sei PB5
14
  
15
  // Timer 4 Einstellungen
16
  // TOP-Value Timer einstellen
17
  TC4H = 720 >> 8;
18
  OCR4C = 720 & 0xFF;     // 11,1kHz
19
20
  setCompareValue(180);   // 25% Duty Cycle
21
  TIMSK4 = (1 << OCIE4A); // Compare Match Interrupt einschalten
22
  TCCR4B = (1 << CS40);   // Timer starten
23
  sei();                  // Interrupts einschalten
24
25
  while(1)
26
  {
27
    // nix zu tun
28
  }
29
}
30
31
ISR(TIMER4_COMPA_vect)
32
{
33
  PORTB ^= (1 << PB5);
34
}

von M. K. (sylaina)


Lesenswert?

Veit D. schrieb:
> Was macht der Code?

ein 50% Duty-Cycle.

Ich hab aber auch meinen Fehler entdeckt und war nicht der einzige mit 
Tomaten auf den Augen. Schau dir mal Zeile 6 meiner Variante an und 
überleg mal was da falsch ist für das Setzen eines 10 bit Registers. Ist 
mir grade aufgefallen...oh man, was für ein dämlicher Fehler...

Vielen Dank aber für deine Hilfe.

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
Noch kein Account? Hier anmelden.