Forum: Mikrocontroller und Digitale Elektronik Interrupt Problem ATm16 C


von Jens-Frederik M. (elomt)


Angehängte Dateien:

Lesenswert?

Hallo

Ich habe ein Problem mit einem Interrupt. Mein Programm soll nach ca. 1s 
eine Messung durchführen und dann die Werte ausgeben. (Es soll später 
mal eine Batterieladungsanzeige für die Batterien im Segelboot meines 
Opas werden.)

Dies funktioniert auch soweit, doch alle 42 Interrupts (Timer1) wird auf 
einmal nur Mist angezeigt und die Variablen MENUE und LADUNG auf ihren 
Anfangswert gesetzt.

Ich glaube nicht, dass ein Reset ausgeführt wird, denn sonst würden die 
LEDs der Menüauswahl kurz alle leuchten, da sie in der "main()" die alle 
eingeschaltet werden und erst nach der ersten Messung im Unterprogramm 
"menue()" passend ausgeschaltet werden.

Der Watchdog ist es vermutlich auch nicht, da ich ihn schon mal in der 
"main()" mit folgendem Befehl deaktiviert hatte.
1
WDTCR &= ~(1<<WDE);

Am Rande wollte ich noch erwähnen, das nur das dritte 7-Segmentmodul im 
Takt des Interrupts leicht aufflackert, als würden einmal kurz alle 
Segmente eingeschaltet werden. Dies kann ich mir aber nicht erklären.

Ich hoffe mein Code ist einigermaßen verständlich.

Verwende ein ATMega16 mit 1MHz internal clock.
Die Ausgabe erfolgt über 7-Segmentanzeigen.

Schon mal vielen Dank im Voraus

Gruß Jens

von Karl H. (kbuchegg)


Lesenswert?

Du hast viel zu viel in die ISR gepackt!

Du musst die ISR als eine Art Funktionsauslöser sehen, die 
Bearbeitungsschritte in der Hauptschleife in main auslösen sollen.

Den Multiplex in der ISR ist in Ordnung. Der muss dort sein, damit er 
sauber und regelmässig läuft. Aber der Rest gehört nicht dorthin.
(Und den Multiplex macht man Codetechnisch auch anders. Ersetzte deine 
switch-cases durch Arrayzugriffe, wobei du bereits in der Funktion, die 
eine Zahl an den Multiplex übergibst, die Umformung auf die 7-Segment 
Codes machst.

von Jens-Frederik M. (elomt)


Lesenswert?

Hallo

Danke für die schnelle Antwort.

Ich dachte eigentlich das durch den Aufruf messen() das Programm aus der 
ISR Routine rausspringt. Wie könnte ich es den am einfachsten umändern? 
Mir fällt momentan keine andere Lösung ein.

Wenn ich dich jetzt richtig verstehe Wird also die messen() und die 
nachfolgenden Unterprogramme alle bis zum nächsten Interrupt in der ISR 
Routine ausgeführt?

Gruß Jens

von Karl H. (kbuchegg)


Lesenswert?

Den Multiplex in der ISR ist in Ordnung. Der muss dort sein, damit er
sauber und regelmässig läuft. Aber der Rest gehört nicht dorthin.
(Und den Multiplex macht man Codetechnisch auch anders. Ersetzte deine
switch-cases durch Arrayzugriffe, wobei du bereits in der Funktion, die
eine Zahl an den Multiplex übergibst, die Umformung auf die 7-Segment
Codes machst.

Und noch was:
In einer ISR hast du weder cli() noch sei()!
Das ist ganz wichtig, weil du verhindern musst, dass sich ISR Aufrufe
ineinander schachteln. Der sei() in menu() wird wahrscheinlich der Punkt
sein, warum dir alles den Bach runter geht.

Kurz und gut. dein ganzer Programaufbau stimmt nicht.

du brauchst eine ISR die, sagen wir mal 300 mal in der Sekunde
aufgerufen wird. Die genaue Anzahl ist nicht so wichtig. Es darf nur 
nicht zu wenig sein, sonst blinkt die Anzeige. Die ISR sieht dann so 
aus:
1
volatile uint8_t SegPattern[3];
2
uint8_t SegIndex;
3
4
ISR(.....)
5
{
6
  PORTD &= ~((1<<PD0) | (1<<PD1) | (1<<PD2) );
7
8
  SegIndex++;
9
  if( SegIndex == 3 )
10
    SegIndex = 0;
11
12
  PORTC = SegPattern[SegIndex];
13
14
  if( SegPattern == 0 )
15
    PORTD |= (1<<PD0);
16
  else if( SegPattern == 1 )
17
    PORTD |= (1<<PD1);
18
  else
19
    PORTD |= (1<<PD2);
20
}
Das ist deine Multiplexroutine! Solange die ISR von einem Timer
regelmässig aufgerufen wird, zeigt die alles als 3-stelliges 'Muster'
an, was auch immer im Array steht. zb das Leuchtmuster, welches eine
andere Funktion anhand einer Dezimalzahl da reinstellt
1
uint8_t digitPattern[] =
2
  {  0b10010000,    // 0
3
     0b11010111,    // 1
4
     0b10100010,    // 2
5
     0b10000110,    // 3
6
     0b11000101,    // 4
7
     0b10001100,    // 5
8
     0b10001000,    // 6
9
     0b11010110,    // 7
10
     0b10000000,    // 8
11
     0b10000100,    // 9
12
   };
13
14
void puti( uint8_t i )
15
{
16
  SegPattern[2] = i % 10;
17
  i /= 10;
18
  SegPattern[1] = i % 10;
19
  i /= 10;
20
  SegPattern[0] = i;
21
}
Wann immer du einen Zahlenwert ausgeben willst, rufst du die Funktion
puti() auf, die dann die Zahl für die Multiplexroutine aufbereitet. Die 
Multiplexroutine zeigt die dann an, solange bis mittels puti etwas 
anderes ausgegeben wird.


Und das ist alles was in den ISR passiert.
Im Sekundentakt setzt du dir noch ein Flag, welches dann in der
Hauptschleife ausgewertet wird und zb eine Messung anleiert.
1
volatile uint8_t performMeasurement;
2
3
ISR(TIMER1_COMPA_vect)
4
{
5
  PORTB ^= (1<<PB3);
6
  performMeasurement = 1;
7
}
8
9
10
int main()
11
{
12
  ...
13
14
  puti( 0 );
15
16
  sei();
17
18
  while( 1 ) {
19
20
    if( performMeasurement )
21
    {
22
      performMeasurement = 0;
23
24
      // ADC abfragen
25
    }
26
27
    // Menüsteuerung
28
    // und Auswahl des Wertes, der angezeigt werden soll
29
    // der jeweilige Wert wird mittels puti an die Anzeige
30
    // gegeben.
31
  }
32
}



Edit: puti müsste man noch erweitern, damit die den Kommapunkt an der 
jeweilige Stelle mit einkodiert wird.

von Jens-Frederik M. (elomt)


Lesenswert?

Vielen Dank für die Top Tipps.

Werde mir den Code erst mal in Ruhe anschauen, damit ich auch alles 
richtig verstehe.

Nochmals danke.

Gruß Jens

von Karl H. (kbuchegg)


Lesenswert?

Schrittweise arbeiten.

Erst mal den Multiplex in Gang bringen und die Timer-ISR anschliessen, 
damit erst mal die Anzeige läuft.

dein erstes Testprogramm könnte dann so aussehen
1
....
2
3
...
4
5
6
int main()
7
{
8
  uint8_t i;
9
10
  // Timer initilaisieren
11
  // und die ISR anhängen
12
13
  i = 0;
14
  sei();
15
16
  while( 1 ) {
17
    puti( i );
18
    i++;
19
    _delay_ms( 500 );
20
  }
21
}

Auf deiner Anzeige muss dann eine Zahl sichtbar sein (kontrollieren ob 
die Zahlen korrekt dargestellt werden!), die sich alle halbe Sekunde 
erhöht. Das ist dein erstes Ziel, um zu sehen, ob die Ausgabe und der 
Multiplex korrekt laufen.
Und von dort gehts dann weiter.

Und PS: Tastenauswertung macht man nicht mittels Interrupt

von Jens-Frederik M. (elomt)


Angehängte Dateien:

Lesenswert?

Hallo

Hab deine Tipps mal eingearbeitet. Läuft auch schon ganz gut.

Was ich mir aber nicht erklären kann ist, warum die Variable "Ladung" 
nur alle 11 Interrupts subtrahiert wird, an statt aller 2.

Die Interrupt Takt-Led blinkt weiter im Sekundentakt.

Kann es sein das sich die beiden Timer(INTs) in die quere kommen?

Gruß Jens

von Helmut (Gast)


Lesenswert?

Hallo Jens,
soweit ich sehen kann wird in puti die übergebene Zahl auf drei Ziffern 
aufgeteilt: Zehntausender, Tausender und Hunderter.
Am Anfang steht in Ladung 4800 drin. Die drei angezeigten Ziffern sind 
dann 0, 4 und 8.
In messen() wird immer 10 abgezogen. An der Anzeige ändert sich aber
erst etwas wenn 100 abgezogen wurden. Also nach 10 messzyklen.

Ich hoffe ich habe nichts übersehen, aber vielleicht ist das dein 
Problem.

von Jens-Frederik M. (elomt)


Lesenswert?

Danke das war es wirklich.

Mann sollte so spät auch nicht mehr logisch denken, doch beim 
Programmieren verliere ich immer den Sinn für die Zeit.

Nochmals danke an alle.

Gruß Jens

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.