Forum: Mikrocontroller und Digitale Elektronik Problem mit Interrupt bei Timer ATtiny24


von Chris (Gast)


Lesenswert?

Ich wollte eine PWM-Zeitschaltuhr bauen - PWM wird durch Potti geregelt 
und mit ADC gelesen und umgesetzt. Funzt gut.

Jetzt soll der 2. Timer dazu dienen Das Signal An/Aus zu schalten nach 
einer Festen Zeit.

Momentan wollte ich nur mal den Interrupt Testen und die Schaltung EIN 
schalten. Aber nichts passiert.

Der Interrupt scheint nicht ausgeführt zu werden.

Hier mein Code:
1
#include "headers.h"
2
3
// Globaler Status AN/Aus
4
  int status;
5
6
7
8
ISR(SIG_OUTPUT_COMPARE1A)
9
{
10
11
    status = 1;
12
}
13
14
15
16
17
int main (void)
18
19
{
20
21
  uint16_t MyADC;
22
  uint8_t tast;
23
24
  // OC0A is Output
25
  DDRB |= (1<<PB2);
26
27
  // Timer 1 Vorbereiten für Ein/Aus
28
  // Clear on Compare Match, Vorteiler 256
29
  TCCR1B = (1<<WGM12) | (1<<CS12);
30
31
  // Compare Match Interrupt an!
32
  TIMSK1 = (1<<OCIE1A);
33
34
  // Setze Compare Wert für genaue Sekunde
35
  OCR1A = 0x3BFF;
36
37
38
  sei();
39
40
41
42
  while (1)
43
  {
44
45
46
    if (status==1)
47
    {
48
49
      // ADC holen, konvertieren und PWM einstellen
50
      MyADC = getADC(PWM);
51
52
      tast = convertADC(MyADC);
53
54
      SetPWM(tast);
55
56
    }
57
    else
58
    {
59
    // Schalte PWM ab!
60
    TCCR0A = 0;
61
    }
62
63
64
  }
65
66
  return 0;
67
}

und hier die Funktionen:
1
uint16_t getADC(uint8_t mux)
2
{
3
4
  // Kleinste gemessene Spannung 2,64 V
5
6
  uint8_t i;
7
  uint16_t result;
8
 
9
  ADMUX = mux;                      // Kanal waehlen
10
  
11
 
12
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS0);    // Frequenzvorteiler 
13
                               // setzen auf 32 (1) und ADC aktivieren (1)
14
 
15
  /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
16
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
17
  ADCSRA |= (1<<ADSC);              // eine ADC-Wandlung 
18
  while ( ADCSRA & (1<<ADSC) ) {
19
     ;     // auf Abschluss der Konvertierung warten 
20
  }
21
  result = ADCW;  // ADCW muss einmal gelesen werden,
22
                  // sonst wird Ergebnis der nächsten Wandlung
23
                  // nicht übernommen.
24
 
25
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */
26
  result = 0; 
27
  for( i=0; i<4; i++ )
28
  {
29
    ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
30
    while ( ADCSRA & (1<<ADSC) ) {
31
      ;   // auf Abschluss der Konvertierung warten
32
    }
33
    result += ADCW;        // Wandlungsergebnisse aufaddieren
34
  }
35
  ADCSRA &= ~(1<<ADEN);             // ADC deaktivieren (2)
36
 
37
  result /= 4;                     // Summe durch vier teilen = arithm. Mittelwert
38
 
39
  return result;
40
}
41
42
43
44
void SetPWM (uint8_t intense)
45
{
46
47
48
49
    // Phase Correct PWM
50
    TCCR0A = (1<<COM0A1) | (1<<WGM00);
51
52
    // Clock / 64 = 121 Hz
53
    TCCR0B = (1<<CS01) | (1<<CS00);
54
 
55
     
56
    //Tastverhältnis ändern
57
    OCR0A = intense;
58
59
    TIMSK0 = (1<<OCIE0A);
60
61
    sei();
62
63
}
64
65
66
uint8_t convertADC(uint16_t MyADC)
67
{
68
69
  uint16_t dummy16;
70
  uint8_t dummy8;
71
72
  // Minimaler ADC wert in dieser Schaltung ist 490
73
  dummy16 = MyADC - 490;
74
75
  // Um eine 10er Potenz anheben
76
  dummy16 = dummy16*10;
77
78
  // So teilen, dass max. 255 herauskommen kann!
79
  dummy8 = dummy16 / 21;
80
81
82
  return dummy8;
83
84
}

Ich hab auch schon im Datenblatt geschaut - der richtige Interrupt 
Vector ist Vector 7 - das deckt sich mit den io files und stimmt.

Warum geht das nicht?
Kann jemand einen Fehler entdecken oder weiß jemand was ich falsch 
mache?

Gruß
Chris

von Stefan E. (sternst)


Lesenswert?

status muss volatile sein.

von Chris (Gast)


Lesenswert?

Hab es geändert, leider ohne Erfolg.
Trotzdem danke für den Tip.

Jetzt steht:
1
volatile int status;

Nach wie vor schaltet sich das PWM nicht ein.


Gruß
Chris

von Hagen R. (hagen)


Lesenswert?

TIMSK0 = (1<<OCIE0A)

Du aktivierst den IQR für Output Compare Kanal A vom Timer 0, aber wo 
ist die ISR in deinem Source dazu ? Es wird bei nicht deklarierten ISR 
Handlern ein Default Vector durch GCC erzeugt der auf den RESET Vektor 
zeigt. Beim OC0A-IRQ machst du also einen Reset.

Statt das Status Register zu setzen probiere mal

DDRB &= ~(1 << PB2);

und mache ansonsten per ADC eine Dauermessung. Dh. dein ADC Code + PWM 
läuft immer nur der Ausgang PB2=OC0A wird aktiviert und deaktiviert.

Gruß Hagen

von Chris (Gast)


Lesenswert?

Hi,

danke das war schon mal ein Teil des Fehlers! Ich wollte TIMSK0 gar 
nicht setzten! Danke.

Habe also das TIMSK0 in der Funktion entfernt und die Main so umgebaut:
1
ISR(_VECTOR(6))
2
{
3
4
PORTB |= (1<<PB2);
5
}
6
7
8
9
10
int main (void)
11
12
{
13
14
  uint16_t MyADC;
15
  uint8_t tast;
16
17
  // OC0A is Output
18
  DDRB |= (1<<PB2);
19
20
  // Timer 1 Vorbereiten für Ein/Aus
21
  // Clear on Compare Match, Vorteiler 256
22
  TCCR1B = (1<<WGM12) | (1<<CS12);
23
24
  // Compare Match Interrupt an!
25
  TIMSK1 = (1<<OCIE1A);
26
27
  // Setze Compare Wert für genaue Sekunde
28
  OCR1A = 0x3BFF;
29
30
31
32
  sei();
33
34
35
36
  while (1)
37
  {
38
39
40
41
42
  }
43
44
  return 0;
45
}

Er tut zwar jetzt etwas, aber laut Code sollte PB2 angeschalten werden 
und an bleiben. Es gibt nichts gegenteiliges - es soll in einer 
Dauerschleife "Enden".

Was ich bekomme ist aber ein Blinken - anscheinend wird jedesmal ein 
Reset ausgelöst.

Im Datenblatt steht InterruptVector 7 - es geht aber nur mit Vector 6.

Irgendwie ist da noch der Wurm drinn.

Weiß noch jemand etwas?

Gruß
Chris

von Chris (Gast)


Lesenswert?

Oh man, ich wollte gerade meinen Platz aufräumen und alles aus machen - 
da mach ich den ISP stecker von der Schaltung und siehe da - es läuft 
wie erwartet!

Was kann der ISP Stecker damit zu tun haben? Hab ich trotzdem noch nen 
Fehler in der Software?

Gruß
Chris

von Stefan E. (sternst)


Lesenswert?

> Im Datenblatt steht InterruptVector 7 - es geht aber nur mit Vector 6.

Das ist bloß eine unterschiedliche Zählung.
Benutze einfach den richtigen Namen (in diesem Fall TIM1_COMPA_vect) und 
kümmer dich nicht um die Nummern.

> Was kann der ISP Stecker damit zu tun haben?

Nun, der hängt immerhin auch mit an Reset. Entweder der Programmer macht 
da Dinge, die er nicht machen sollte, oder du fängst dir einfach über 
die Leitung Störungen ein, die zu Resets führen.

von Hagen R. (hagen)


Lesenswert?

in der jetzigen ISR hast du

PORTB |= (1<<PB2);

stehen, warum nicht

DDRB &= ~(1 << PB2);

wie von mir vorgeschlagen ? Du möchtest doch das an diesem Pin eine 
zuschaltbare PWM möglich wird. Dann wirst du mit PORTB |= (1<<PB2); nur 
diesen Pin auf High Pegel setzen statt ihn als Ausgang ein/aus zu 
schalten.

Wenn das später funktioniert musst du dich fragen ob das vom Timing her 
so passt. Mit dieser Lösung schaltest du uU. mitten in einem PWM Zyklus 
diese am Pin ein oder aus. Das könnte uU. störend sein, hätte aber den 
Vorteil das du deine PWM exakt im Timing vom Timer1 ein/aus schalten 
kannst. Möchtest du die PWM phasensynchron ein/aus schalten so musst du 
im TIM1_COMPA_vect erstmal eine globale als volatile deklarierte 
Variable setzen. Dann benutzt du den TIM0_OVF_vect um diese Variable 
abzufragen und entsprechend das DDRB.PB2 Bit setzen.

Gruß Hagen

von Chris (Gast)


Lesenswert?

Danke Leute, Danke Hagen.

Also dass ich den Pin gesetzt habe, war jetzt einfach nur mal zum 
testen.

Wenn ich das Programm jetzt fertig mache, werd ich es Phasenrichtig 
machen, da es Schaltzeiten von mehreren Sekunden, bzw. Minuten sind - da 
kommt es auf das 10tel da nicht an. Ist völlig unkritisch hier.

Daher werd ich wohl Phasenrichtig abschalten.

Jetzt funktioniert auf jeden Fall alles so, wie es soll.

Gruß
Chris

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.