Forum: Mikrocontroller und Digitale Elektronik TimerInterrupt von ATmega328p-pu


von Felix (Gast)


Angehängte Dateien:

Lesenswert?

Hallo allerseits,

Ich habe ein Problem und hoffe, ihr könnt mir helfen.
Und zwar folgendes:

Ich habe einen Mikrocontroller (ATmega328p-pu (der gleiche wie im 
Arduino Uno)), und habe vor in naher Zukunft einen Motor mittels 8-Bit 
Timer0 anzusteuern. Für Motoren gestaltet sich Phasenrichtige PWM als 
nützlich, also hab ich´s damit versucht. Hier einmal mein C-Code
1
//ATmega328p-pu
2
 #include <avr/io.h>
3
 #include <avr/interrupt.h>
4
5
void phasecorrect_pwm();
6
7
 int main(void)
8
 {
9
   DDRD |= (1<<PIND6);
10
   phasecorrect_pwm();
11
   
12
   while(1);       
13
 }
14
15
 // Interrupt subroutine timer 0 overflow
16
 ISR(TIMER0_OVF_vect)
17
 {
18
   PORTD ^= (1<<PIND5); 
19
   // Toggle PD5
20
 }
21
 
22
void phasecorrect_pwm()
23
 {
24
25
  OCR0A = 63;
26
  // Set Dutycycle to 75 %
27
    
28
  TCCR0A |= (1<<WGM00)|(1<<COM0A1)|(1<<COM0A0);
29
  // Start timer0 (PWM->Phasenkorrekt) mit TOP von 0xFF
30
  // in non inverting phase correct PWM mode
31
    
32
  TCCR0B |= (1<<CS00);
33
  //CPU CLock & Prescaller von 0
34
     
35
  TIMSK0 |= (1<<OCIE0B)/*|(1<<TOIE0)*/ ;
36
  // Enable Timer Compare match und overflow-Interrupt
37
    
38
  sei();
39
  // Set the I-bit in SREG -> start interrupts
40
41
 }

Was mich wundert ist, dass das ganze überhaupt funktioniert. Die 
Interrupt-Subroutine hat ja den Vektor "TIMER0_OVF_vect", d.h. es ist 
ein overflow-vector, d.h. die Subroutine wird jedesmal aufgerufen, wenn 
ein Timeroverflow stattfindet, was laut Datenblatt immer dann geschieht, 
wenn BOTTOM, also der Zählerstatus von 0 erreicht wird.
D.h. Der Timer Zählt: 0(BOTTOM) rauf-> 255(TOP) runter-> 0(BOTTOM)
Aber weil ich meinen Mikrocontroller mit dem Oszilloskop am PIND6 
ausgemessen habe, bin ich draufgekommen, dass die Interrupt-Subroutine 
wohl nicht nur im Falle des erreichens von BOTTOM aufgerufen wird(was 
einen Duty-Cycle von 50% zur Folge hätte), sondern dass ich den 
Duty-Cycle sehrwohl durch beschreiben (wie im obigen Codesnippet) des 
OCR0A - Registers steuern kann.

Also warum wird die Interrupt-Subroutine doch immer durchlaufen, wenn 
der Counter bei 63 angekommen ist, und nicht wenn ein einfacher Overflow 
(BOTTOM) stattfindet ??
Anm.: im Datenblatt steht folgendes:

"The Timer/Counter Overflow Flag (TOV0) is set each time the counter 
reaches BOTTOM. The
Interrupt Flag can be used to generate an interrupt each time the 
counter reaches the BOTTOM
value."


Ich hoffe, ihr könnt mir helfen,

Danke schon mal im Voraus,

LG Felix

von Oliver S. (oliverso)


Lesenswert?

Felix schrieb:
> Was mich wundert ist, dass das ganze überhaupt funktioniert.

Stimmt.

Du hast eine ISR für den Overflow, gibst aber den Compare Match 
Interrupt frei. Damit startet dein Prozessor bei jedem Compare Match von 
vorne.
Gleichzeitig ist das ein prima Beispiel, wofür Kommentare hilfreich sind 
;)

In der OVF-ISR (die eh nie aufgerufen wird) schaltest du an PD 5 herum, 
allerdings ohne Wirkung, weil Pin als PWM-Pin vom Timer gesteuert wird. 
Wenn du den als PWM-Ausgang nutzen möchtest, dann fehlt die 
Initialisierung als Ausgang. Wenn nicht, dann auch ;)

Oliver

: Bearbeitet durch User
von Felix (Gast)


Lesenswert?

Hi, danke vielmals,
Jetzt habe ich den Code einigermaßen umgearbeitet, habe aber wieder 
einen Fehler, bei dem ich hängen bleibe.
Hier mal der neue Code
1
//ATmega328p-pu
2
 #define F_CPU 8000000UL
3
 #include <avr/io.h>
4
 #include <avr/interrupt.h>
5
 #include <util/delay.h>
6
7
#define DUTYCYCLE 75
8
9
void phasecorrect_pwm();
10
11
 int main(void)
12
 {
13
   //DDRD |= (1<<PIND6); //Der Timer wird als Output konfiguriert
14
   DDRB |= (1<<PINB0);
15
   phasecorrect_pwm();
16
   
17
   while(1)
18
   {
19
     for(int i=0; i < 256; i++) // Dutycycle sollte bei 100 % anfangen und immer schmäler werden 
20
     {
21
       OCR0A = i;
22
       _delay_ms(30);
23
     }   
24
   }
25
 }
26
27
   //Interrupt subroutine timer 0 compare match
28
 ISR(TIMER0_COMPA_vect)
29
 {
30
  PORTB ^= (1<<PINB0);                    
31
  // Toggle PB0
32
 }
33
 
34
void phasecorrect_pwm()
35
 {
36
  //OCR0A = (255-(dutycycle*(2.55)));
37
    
38
  TCCR0A |= (1<<WGM00)|(1<<COM0A1)|(1<<COM0A0);
39
  // Start timer0 (PWM->Phasenkorrekt) mit TOP von 0xFF
40
  // im nicht invertierenden Phasenrichtigen PWM-Modus
41
    
42
  TCCR0B |= (1<<CS00);
43
  //CPU CLock & Prescaller von 0
44
     
45
  TIMSK0 |= (1<<OCIE0A);
46
  // Enable Timer Compare Match-Interrupt
47
    
48
  sei();
49
  // Set the I-bit in SREG -> start interrupts
50
 }

Und zwar folgendes: Ich habe in der Main-Funktion eine for-Schleife, die 
mir eigentlich meinen Duty-Cycle von 100% anfangend, langsam immer 
schmäler machen sollte, bis der Duty-Cycle nur mehr sehr gering bzw. 0 % 
ist.
Wenn diese for-Schleife abgearbeitet ist, sollte die software durch die 
übergeordnete while(1)-schleife, sofort wieder in die for-Schleife 
springen, und meinen Dutycycle wieder von 100% auf 0 % herunterdrücken.
Hier mein Problem:
Der Dutycycle geht zwar von 100% zu 0%, aber geht dann wieder in der 
selben Geschwindigkeit von 0 auf 100%.
Dabei sollte er immer nur von 100 auf 0 gehen. Dann wieder zu 100 
springen, und wieder auf 0 gehen usw.
Hab das ganze aber mit dem Oszi angesehen, und festgestellt , dass er 
wieder von 0 auf hundert raufzählt, genauso wie er runtergezählt hat.

Wie kann das sein ???

Ich hoffe Ihr könnt mir helfen,
Danke schon mal im voraus,

LG Felix
Wenn die Schleife komplett abgespult ist,

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.