PFS154 - Problem mit Timerinterrupt

#7777904
Lesenswert?

Vielleicht gibt es hier ja noch jemand anderen der mit Padauk-Controllern immer wieder mal "spielt". Mir vergeht aber für den Moment etwas die Laune, weil ich ein Problem mit dem Timerinterrupt beim PFS154 habe... immer wieder einmal.

Ich möchte ein an sich sehr einfaches Würfelprogramm erstellen. Unter anderem soll das Programm auch Zahlensystem "lehren" und weil es gebräuchliche Zahlensystem zur Basis 2, 10 und 16 genügend gibt hier dann natürlicherweise zur Basis 6.

Also möchte ich einen 3-fach Würfel erstellen und dann eben nicht nach guter, alter Väter Sitte jede einzelne Würfelstelle bis 6 zählen und dann die nächste Stelle erhöhen, sondern von 0..215 (das sind (6^3)-1).

Hierzu habe ich dann etwas einfaches programmiert:

1
void int2dices(uint8_t val, uint8_t *buf)
2
{
3
  volatile uint8_t i, dice, divi;
4

5
  __disgint();
6
  divi= 36;
7
  for (i= 0; i< 3; i++)
8
  {
9
    dice= 0;
10
    while(val >= wivi)
11
    {
12
      dice++;
13
      val -= divi;
14
    }
15
    *buf= dice+1;
16
    buf++;
17
    wivi = divi / 6;
18
  }
19
  __engint();
20
}

Die Volatile-Variablen sind der "Fehlersuche" geschuldet, genauso wie das ab- und anschalte der globalen Interrupts.

Diese Funktion funktioniert auf Systemen (getestet) mit MCS-51, AVR, STM8, STM32.

Auch auf dem Padauk PFS154 läuft das, wenn auf dem Padauk KEIN Timerinterrupt läuft.

Das angehängte C-Programm ist in der Main etwas "zerfahren" weil ich hier Nebeneffekte untersuchen möchte.

Ein Hochzählen der einzelnen Stellen (wie ich es eben nicht möchte) funktioniert. Ginge es nur um eine Würfel, könnte man diesen so realisieren (aber eigentlich will ich die Zufallszahl nicht durch eine unterbrochene Zählschleife, sondern über einen Pseudozufallszahlengenerator mit linear rückgekoppeltem Schieberegister realisieren).

Soweit so gut (oder schlecht). Wird die Funktion "int2dices" in die Endlosschleife der Main eingehängt, hängt sich diese auf. Der Interrupt wird jedoch weiter abgearbeitet, weil die Würfelanzeigen weiterhin gemultiplext werden. Ich gehe (fälschlicherweise?) davon aus, dass auch keine 16-Bit Operation unterbrochen wird, weil ich schlicht keine verwende. Selbst ein Abschalten der Interrupts in "int2dices" hilft nicht.

Ich mache (so glaube ich) garantiert etwas mit der Timerinitialisierung und oder im Interruptvektor falsch:

1
/* --------------------------------------------------------
2
                       interrupt
3

4
     der Interrupt-Handler. TM2 ruft den Interrupt mit
5
     ca. 1 kHz auf.
6

7
     Hier muss das Multiplexen der Anzeige eingehaengt
8
     werden.
9
   -------------------------------------------------------- */
10
void interrupt(void) __interrupt(0)
11
{
12
  static uint8_t mpx = 0;
13

14
  __disgint();
15

16
  // Interruptquelle ist Timer2
17
  if (INTRQ & INTRQ_TM2)
18
  {
19
    volatile uint8_t bmp;
20
    PB = 0xf0;     // alle MPX-Leitungen 1 = kein Digit angewaehlt
21
    switch (mpx)
22
    {
23
      case 0:
24
        {
25
          bmp= dices[2];
26
          PB = 0xe0 | dice_bmp[bmp];
27
          break;
28
        }
29
      case 1:
30
        {
31
          bmp= dices[1];
32
          PB = 0xd0 | dice_bmp[bmp];
33
          break;
34
        }
35
      case 2:
36
        {
37
          bmp= dices[0];
38
          PB = 0xb0 | dice_bmp[bmp];
39
          break;
40
        }
41
      default : break;
42
    }
43
    mpx++;
44
    mpx = mpx % 3;
45
    INTRQ &= ~INTRQ_TM2;          // Interruptanforderung quittieren
46
  }
47
  __engint();
48
}
49

50
/* --------------------------------------------------------
51
                            timer2_init
52

53
     initialisiert den Timer2 (8-Bit) fuer einen Interrupt-
54
     intervall mit 1 kHz
55
   -------------------------------------------------------- */
56
void timer2_init(void)
57
{
58

59
  TM2C = (uint8_t)TM2C_CLK_IHRC;
60
  TM2S = (uint8_t)TM2S_PRESCALE_DIV16 | TM2S_SCALE_DIV4;
61
  TM2B = 0x80;
62

63
  __engint();                   // grundsaetzlich Interrupt zulassen
64
  INTEN |= INTEN_TM2;           // Timerinterrupt zulassen
65
}

Wie gesagt: auf anderen Systemen funktioniert die Vorgehensweise ohne zu murren, nur auf dem PFS154 nicht (hier habe ich aber auch glaube ich nicht alle Informationen, die es evtl. bräuchte).

Verwendeter Compiler sind SDCC 4.1.0 und SDCC 4.3.0 (beide zeigen gleiches Verhalten, so denke ich, liegt der Fehler bei mir)

Weiß hier jemand etwas weiterführendes zu den Interrupts beim PFS154 ?

Angehängte Dateien:
#7779417
Lesenswert?

Wie schnell läuft der PFS154? Könnte es sein, dass die Interruptroutine soviel Zeit braucht, dass der Prozessor nicht mehr dazu kommt, das Hauptprogramm auszuführen?

Wozu das __disgint() und __engint() im Interrupt-Handler? Letzteres würde doch Interrupts wieder einschalten, bevor der Interrupthandler zurückkehrt, so dass dieser, falls er nicht schnell genug ist, durch einen weiteren Interrupt unterbrochen würde, was dann einen Stack-Overflow ergäbe.

#7779477
Lesenswert?

Ralph S. schrieb:

Wird die Funktion "int2dices" in die Endlosschleife der Main eingehängt, hängt sich diese auf.

Ja, warum wohl? Nachdem die Schleife zwei Durchläufe gemacht hat, steht in "val" der Wert der niedrigsten Stelle. Also kein Grund ein drittes mal die Schleife zu durchlaufen. Wenn die niedrigste Stelle null ist, bleibt das Programm im "while" hängen.

#7779723
Lesenswert?

Hallo Philipp, super vielen Dank fürs melden...

Philipp Klaus K. schrieb:

Wie schnell läuft der PFS154? Könnte es sein, dass die Interruptroutine soviel Zeit braucht, dass der Prozessor nicht mehr dazu kommt, das Hauptprogramm auszuführen?

Das wäre eine Möglichkeit, an die ich auch gedacht hatte. Laut EasyPdk und den Compilerangaben sollte der Chip mit 8 MHz laufen. Dies ist die Standardeinstellung meiner Toolchain und hab ich bei anderen Programmen schon überprüft, werde ich hier aber auch machen in dem ich per Timerinterrupt einen Pin wackeln lasse und oszillographiere. Auf einem AVR und einem STM8 lief die Funktion mit natürlich anderem Interruptvektor unauffällig.

Philipp Klaus K. schrieb:

Wozu das __disgint() und __engint() im Interrupt-Handler? Letzteres würde doch Interrupts wieder einschalten, bevor der Interrupthandler zurückkehrt, so dass dieser, falls er nicht schnell genug ist, durch einen weiteren Interrupt unterbrochen würde, was dann einen Stack-Overflow ergäbe.

:-) , das ist der "Verzweiflung" geschuldet und habe ich hier noch nicht rausgenommen. Beim ersten Entwurf waren __disgint und __engint noch nicht drin. Ich hatte hier "einfach nur" verschiedene Dinge ausprobiert um zu nachzuforschen. Von daher: Wenn man "dumm" ist (like me) und nicht weiß, warum etwas nicht funktioniert, macht man Dinge, von denen man weiß dass sie auch nicht funktionieren, probiert sie aber dennoch aus (ich sag ja: dumm).

Mario M. schrieb:

Ja, warum wohl? Nachdem die Schleife zwei Durchläufe gemacht hat, steht in "val" der Wert der niedrigsten Stelle. Also kein Grund ein drittes mal die Schleife zu durchlaufen. Wenn die niedrigste Stelle null ist, bleibt das Programm im "while" hängen.

Auch hier hat sich beim Einstellen hier ein Fehler eingeschlichen (die gepostete Funktion würde sich gar nicht übersetzen lassen, weil das umbenennen von Variablen meinerseits nicht vollständig war). Aber, das nicht korrekte Funktionieren ist nicht die Schuld von "int2dices". Überprüft wurde das mit (Konsolenprogramm Linux):

1
#include <stdio.h>
2
#include <stdint.h>
3

4
uint8_t dices[3];
5

6
void int2dices(uint8_t val, uint8_t *buf)
7
{
8
  volatile uint8_t i, dice, divi;
9

10
  divi= 36;
11
  for (i= 0; i< 3; i++)
12
  {
13
    dice= 0;
14
    while(val >= divi)
15
    {
16
      dice++;
17
      val -= divi;
18
    }
19
    *buf= dice+1;
20
    buf++;
21
    divi = divi / 6;
22
  }
23
}
24

25
/* ---------------------------------------------------------------------------
26
                                    M A I N
27
   --------------------------------------------------------------------------- */
28
int main(int argc, char **argv)
29
{
30
  uint8_t i;
31
  uint8_t w;
32
  uint8_t col;
33

34
  int2dices(w, &dices[0]);
35
  col= 0;
36
  for (i= 0; i< 216; i++)
37
  {
38
    int2dices(i, &dices[0]);
39
    if ((i % 6)== 0) printf("\n\r");
40
    col++;
41
    col= col % 6;
42
    printf("w: %.3d;  %d |  %d |  %d\t", i, dices[0], dices[1], dices[2]);
43
  }
44
  printf("\n\r");
45

46
  return 0;
47

48
}

Und die Ausgabe kannst du auf dem Screenshot sehen (alle 216 mögliche Kombinationen)

Angehängte Dateien:
#7779801
Lesenswert?

Ralph S. schrieb:

Auch hier hat sich beim Einstellen hier ein Fehler eingeschlichen

Deswegen fügt man immer genau den vollständigen Quelltext als Anhang ein, den man wirklich compiliert und getestet hat! Mehrere Dateien kann man in ein Zip packen.

Das Einfügen in den Post ist nur in seltenen Ausnahmen sinnvoll. Und auch da immer nur mit C&P, nie aus dem Gedächtnis nachgeschrieben und auf keinen Fall ungetestet!

#7780915
Lesenswert?

Kaum macht man alles (hoffentlich) richtig, schon funktioniert auch alles !

Nachdem ich mein Linux erneuert habe auf Kernel 6.5.5 und GLIBC 2.35 (ich weiß, wir sind momentan bei Kernel 6.12.1 und GLIBC 2.40) konnte ich dann auch den SDCC Version 4.4.4 #15109 installieren.

An der SDCC-Version lag es aber (wie ich eh angenommen hatte) aber nicht.

Der Fehler war, dass mein Interrupt eine Delay-Schleife unterbrochen hat, die die Taktimpulse zaehlt und in Assembler codiert ist. Hierin hat sich mein Programm dann tatsächlich aufgehängt.

Gelöst habe ich das dann in der Art, dass im Timerinterruptvektor ein Millisekundenzähler implementiert ist. Eine neue Delay-Funktion bedient sich dann aus diesem Zähler und zählt nicht die Taktimpulse.

Was habe ich gelernt?

  • Bei scheinbar einfachen Sachen ist meistens weder das System noch der Compiler schuld. Nach der Korrektur läuft mein Programm auch noch, wenn es mit SDCC 4.0.3 compiliert wird.

  • wenn der Fehler logisch nicht an den Stellen sein kann an denen ich suche: Suche woanderst.

  • traue dir selbst nicht und auch deiner eigenen Toolchain nicht. Es kann irgendwo ein Fehler sein, der noch nicht aufgetreten ist.

  • poste immer alles was zum Programm gehört, damit andere dir helfen können (ich hoffe ich halte mich daran !)

Bei der Fehlersuche hatte ich den 16-Bit Timer verwendet (Timer 2 war, wie ich jetzt weiß nicht das Problem) und habe diesen im Code belassen und Timer 2 entfernt.

Im Anhang ist die komplette Toolchain inklusive abgespecktem SDCC 4.4.4 welcher nur Padauk Controller übersetzen kann, sowie die Uploadprogramme für EasyPdkProgrammer und den Arduino basierenden Programmer. Die Lizensbedingungen zum SDCC sind natürlich enthalten.

Mit dem Installieren von SDCC 4.4.4 tut sich ein neues - kleineres Problem - mit der Assemblerzählschleife "delay" auf, die mir eine Warnung ausgibt, aber dennoch funktioniert. Bevor ich das "Problem" schildere versuche ich dem aber erst auf den Grund zu gehen.

Vielen Dank an Philipp Klaus K. (pkk) und Peda D. (peda)

Angehängte Dateien:
Beitrag #7780917 wurde von einem Moderator gelöscht.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren