Forum: Mikrocontroller und Digitale Elektronik Atmega8 Timer1 Interrupt Routine wird nicht angesprungen


von M. H. (siddd)


Lesenswert?

Hallo,

Ich möchte eine PWM erzeugen. Am Ausgang OCA1 ist eine LED 
angeschlossen. Der AD-Wandler ADC0 soll im 10-Bit Modus laufen.

Die Interrupt Routine wird nicht angesprungen. Ich finde den Fehler 
nicht. Wenn mir jemand helfen könnte dem wäre ich dankbar.


1
#define F_CPU 160000000
2
#include <avr/io.h>
3
#include <stdint.h>
4
#include <avr/interrupt.h>
5
6
7
volatile uint16_t buffer;
8
volatile int8_t flag;
9
10
ISR(TIMER1_COMPA_vect)
11
{
12
  //Hilfsport setzen
13
  PORTD |= 1<<PD0;
14
  
15
  // Wandlung vom analogen Eingang starten
16
  ADCSRA |= (1<<ADSC);
17
18
  // Warten bis die AD-Wandlung abgeschloßen ist
19
  while ( !(ADCSRA & (1<<ADIF)) );
20
  
21
  //Hilfsport zurücksetzen
22
  //  PORTD &= ~(1 << PD0);
23
  PORTD = 0;
24
  
25
  buffer = ADC;
26
  OCR1A=buffer * 4;
27
}
28
29
30
31
int main()
32
{  
33
  //Alle Interrupts ausschalten
34
  cli();
35
  
36
  //Hilfsport als Ausgang definieren und zuruecksetzen
37
  DDRD = DDRB | (1 << PD0);
38
  PORTD = 0;
39
  
40
  
41
  // Timer für PWM initialisieren
42
  // OC1A auf Ausgang
43
  DDRB = (1 << PB1 );
44
  TCCR1A = (1<<COM1A1);
45
  TCCR1B = (1<<WGM13)  | (1<<CS10);
46
    
47
  // PWM Frequenz auf 2000 Hz stellen bei 16 Mhz Oscillator
48
  ICR1 = 4000;
49
    
50
  //Timer Interrupt initialisieren und globale Interrupts enable  
51
  TIMSK = TIMSK | (1 << OCIE1A);
52
  sei();
53
  
54
  // AD-Wandler initialisieren und auf 10 Bit stellen
55
  ADMUX = (1<<REFS0);
56
  ADCSRA = (1<<ADEN);
57
  
58
  //Startwert für PWM festlegen
59
  OCR1A = 1000;
60
61
  while( 1 )
62
  {  
63
    //Hauptschleife
64
  }
65
}


Gruss
Michael

von Högli (Gast)


Lesenswert?

Was mißt denn der ADC?
Im gewählten Timermodus ist ICR1=1000 der TOP Wert. Hier:

buffer = ADC;
OCR1A=buffer * 4;

kann der Comparewert für den Interrupt aber locker über 1000 gesetzt 
werden, so daß der Interrupt sich quasi selber an der Ausführung 
hindert.

von M. H. (siddd)


Lesenswert?

Auf ADC0 ist am Mittelabgriff eines Potentiometer mit 5 V Versorgung 
angeschlossen.

Wenn ich den Code aus der Interrupt-Routine in der Hauptschleife des 
Hauptprogramms ausführe, dann wird die PWM mit 2kHz Frequenz ausgeführt. 
Über das Potentiometer läßt sich dann die Pulsbreite der PWM stufenlos 
einstellen. Nur der Atmega8 führt dann die AD-Wandlung permanent aus.

Ich möchte allerdings, dass die AD-Wandlung nur einmal pro Interrupt 
Zyklus ausgeführt wird.

von Högli (Gast)


Lesenswert?

Högli schrieb:
> Im gewählten Timermodus ist ICR1=1000 der TOP Wert. Hier:

Sorry, muß ICR1=4000 heißen. Kann aber immer noch vom ADC * 4 überboten 
werden.

> Ich möchte allerdings, dass die AD-Wandlung nur einmal pro Interrupt
> Zyklus ausgeführt wird.

In der ISR die ADC Messung starten. Dann in der Hauptschleife "// Warten 
bis die AD-Wandlung abgeschloßen ist" oder den ADC Interrupt nutzen. In 
der ISR auf Abschluß der Wandlung warten ist selten sinnvoll.

von C. W. (chefkoch)


Lesenswert?

Warum genau lässt Du nicht den ADC frei laufen und machst das PWM-Update 
in der ISR "ADC Conversion complete"?

von M. H. (siddd)


Lesenswert?

Vielen Dank für die Kommentare!

Högli schrieb:
> In der ISR die ADC Messung starten. Dann in der Hauptschleife "// Warten
> bis die AD-Wandlung abgeschloßen ist" oder den ADC Interrupt nutzen. In
> der ISR auf Abschluß der Wandlung warten ist selten sinnvoll.

Ich habe den Kommentar umgesetzt, und siehe da es funktioniert. Dabei 
habe ich der Übersicht halber noch einzelne Änderungen durchgeführt. Der 
neue C-Code lautet folgendermassen:
1
#define F_CPU 160000000
2
#include <avr/io.h>
3
#include <stdint.h>
4
#include <avr/interrupt.h>
5
6
7
volatile uint16_t buffer;
8
volatile int8_t flag;
9
10
ISR(TIMER1_COMPA_vect)
11
{
12
  flag=1;
13
}
14
15
16
17
int main()
18
{  
19
  //Alle Interrupts ausschalten
20
  cli();
21
  
22
  //Hilfsport als Ausgang definieren und zuruecksetzen
23
  DDRD = DDRB | (1 << PD0);
24
  PORTD = 0;
25
  
26
  
27
  // Timer für PWM initialisieren
28
  // OC1A auf Ausgang
29
  DDRB = DDRB | (1 << PB1 );
30
  TCCR1A = TCCR1A | (1 << COM1A1);
31
  TCCR1A = TCCR1A | (0 << COM1A0);
32
  TCCR1B = TCCR1B | (1 << WGM13);
33
  TCCR1B = TCCR1B | (0 << WGM12);
34
  TCCR1B = TCCR1B | (1 << CS10);
35
  TCCR1B = TCCR1B | (0 << CS11);
36
  TCCR1B = TCCR1B | (0 << CS12);
37
  TCCR1A = TCCR1A | (0 << WGM10);
38
  TCCR1A = TCCR1A | (0 << WGM11);
39
    
40
  // PWM Frequenz auf 1953 Hz stellen bei 16 Mhz Oscillator
41
  ICR1 = 4095;
42
    
43
  //Timer Interrupt initialisieren und globale Interrupts enable  
44
  TIMSK = TIMSK | (1 << OCIE1A);
45
  sei();
46
  
47
  // AD-Wandler initialisieren und auf 10 Bit stellen
48
  ADMUX = (1<<REFS0);
49
  ADCSRA = (1<<ADEN);
50
  
51
  //Startwert für PWM festlegen
52
  OCR1A = 1000;
53
54
  while( 1 )
55
  {  if (flag==1)
56
    {
57
        //Hilfsport setzen
58
        PORTD |= 1<<PD0;
59
        
60
        // Wandlung vom analogen Eingang starten
61
        ADCSRA |= (1<<ADSC);
62
63
        // Warten bis die AD-Wandlung abgeschloßen ist
64
        while ( !(ADCSRA & (1<<ADIF)) );
65
        
66
        //Hilfsport zurücksetzen
67
        //  PORTD &= ~(1 << PD0);
68
        PORTD = 0;
69
        
70
        buffer = ADC;
71
//        OCR1A = buffer * 4;
72
        OCR1A = (buffer<<2);
73
        flag = 0;
74
    }
75
76
  }
77
}

Den ADC möchte ich nicht frei laufen lassen, um für spätere Anwendungen 
Rechenkapazität zu sparen.

von C. W. (chefkoch)


Lesenswert?

??? Du willst Rechenzeit sparen indem Du durch den Timer ein Flag setzt, 
in der Mainschleife und dann wartest bis der ADC fertig ist???

von M. H. (siddd)


Lesenswert?

So ist es! Vielleicht mache ich noch einen Denkfehler.

Was ist denn aus Deiner Sicht der Vorteil, wenn man den ADC frei laufen 
läßt ?

von Uwe (de0508)


Lesenswert?

Guten Tag,

es gibt noch ein weiteres Problem mit diesem Codeabschnitt:
1
// Wandlung vom analogen Eingang starten
2
ADCSRA |= (1<<ADSC);
3
4
// Warten bis die AD-Wandlung abgeschloßen ist
5
while ( !(ADCSRA & (1<<ADIF)) );

Wenn man nach einen atMega Datenblatt geht, dann ist es so richtig:
1
// Wandlung vom analogen Eingang starten
2
ADCSRA |= (1<<ADSC);
3
4
// Warten bis die AD-Wandlung abgeschloßen ist
5
while ( (ADCSRA & (1<<ADSC)) ) { }

von Bastler (Gast)


Lesenswert?

Der ADC kann sich auch selbst melden, wenn er fertig ist:
1
 ISR( ADC_vect ) {}

von Högli (Gast)


Lesenswert?

Man könnte auch die ADC Wandlung wie ursprünglich in der ISR starten. In 
der Hauptschleife ohne irgendeine Abfrage oder auf irgendwas zu warten 
den ADC Wert immer einfach direkt dem OCR1A zuweisen. Wird zwar oft 
zugewiesen, ändert sich ja dann aber ohnehin nur mit der 
Interruptfrequenz mit der ja eine neue Wandlung gestartet wird. Ob das 
in dein Konzept paßt mußt du dann schauen.

von M. H. (siddd)


Lesenswert?

>
>
1
 
2
> // Warten bis die AD-Wandlung abgeschloßen ist
3
> while ( (ADCSRA & (1<<ADSC)) ) { }

Uwe S. schrieb:
> // Warten bis die AD-Wandlung abgeschloßen ist
> while ( (ADCSRA & (1<<ADSC)) ) { }

Und was passiert, wenn man die Zeile ganz weg läßt ?

Funktionieren tut es auch ohne dieser Zeile.

von Högli (Gast)


Lesenswert?

M. H. schrieb:
> Und was passiert, wenn man die Zeile ganz weg läßt ?

Was soll passieren? Kaputt geht nix. Und ... außerdem ... hab ich doch 
GERADE geschrieben ... :-) :-) :-) 
Beitrag "Re: Atmega8 Timer1 Interrupt Routine wird nicht angesprungen"

von spess53 (Gast)


Lesenswert?

Hi

>Und was passiert, wenn man die Zeile ganz weg läßt ?

>Funktionieren tut es auch ohne dieser Zeile.

Das gleiche wie bei

>while ( !(ADCSRA & (1<<ADIF)) );

Der Controller macht ohne zu warten weiter. ADIF bleibt nämlich 1, wenn 
du es nicht manuell zurück setzt.

ADSC dagegen bleibt so nur lange 1 bis der ADC mit der Conversation 
fertig ist.

MfG Spess

von Uwe (de0508)


Lesenswert?

Danke spess53,

für die Klarstellung, wenn man nur das Datenblatt zum  Verständnis lesen 
würden, wenn hätte man ein Problem weniger.

von M. H. (siddd)


Lesenswert?

Wenn ich das Datenblatt nicht lesen würde, dann wäre ich nicht soweit 
gekommen!

Eine bessere Frage wäre wohl gewesen, warum der ATMEGA8 die ADC Wandlung 
noch zu Ende führt:
> while ( (ADCSRA & (1<<ADSC)) ) { }

Meine Erwartung wäre gewesen, dass der ATMEGA8 im Code fortschreitet 
(wenn die Zeile nicht vorhanden ist)und die ADC Wandlung abbricht.

von Händeringer (Gast)


Lesenswert?

>Wenn ich das Datenblatt nicht lesen würde, dann wäre ich nicht soweit
>gekommen!

Zustimmung!

In Anbetracht der Tatsache, daß es sich um DATENBÜCHER mit mehreren
hundert Seiten handelt, in denen die zusammenhängenden Informationen
sehr gut gestreut vorliegen, kann es passieren, daß man einen Halbsatz
überliest.

Diese Überheblichkeit ist nicht angebracht!

von Oliver S. (oliverso)


Lesenswert?

Händeringer schrieb:
> In Anbetracht der Tatsache, daß es sich um DATENBÜCHER mit mehreren
> hundert Seiten handelt, in denen

die Kapitel mir der Beschreibung zu den einzelnen Modulen immer nur 
wenige Seiten umfassen, ist das durch aus machbar.

Oliver

von spess53 (Gast)


Lesenswert?

Hi

>Meine Erwartung wäre gewesen, dass der ATMEGA8 im Code fortschreitet
>(wenn die Zeile nicht vorhanden ist)und die ADC Wandlung abbricht.

Warum soll der ADC die Wandlung abbrechen? Dem ADC ist es egal ob ADSC 
abgefragt wird.

MfG Spess

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.