Forum: Mikrocontroller und Digitale Elektronik PFS154 - Problem mit Timerinterrupt


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Ralph S. (jjflash)


Angehängte Dateien:

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 ?

von Philipp Klaus K. (pkk)


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.

von Mario M. (thelonging)


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.

von Ralph S. (jjflash)


Angehängte Dateien:

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)

von Peter D. (peda)


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!

von Ralph S. (jjflash)


Lesenswert?

Peter D. schrieb:
> 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.

Asche über mein Haupt, du hast eben recht

von Mario M. (thelonging)


Lesenswert?

Ralph S. schrieb:
> Aber, das
> nicht korrekte Funktionieren ist nicht die Schuld von "int2dices".

Ja, jetzt sehe ich es auch. Divi ist beim letzten Durchlauf "1", die 
While-Schleife bleibt nicht hängen. Unnütz ist er trotzdem, der Rest der 
"Division" ist schon in "val".

von Ralph S. (jjflash)


Angehängte Dateien:

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)

Beitrag #7780917 wurde vom Autor gelöscht.
von Ralph S. (jjflash)


Lesenswert?

Sehe ich gerade:

Der Kommentar zum T16_init

 // ( F_CPU  16  512) = 8 MHz / 256 = 976Hz

ist falsch.

Muß heißen:

// ( F_CPU  16  512) = 8 MHz / 8192 = 976Hz

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.