Forum: Mikrocontroller und Digitale Elektronik Phänomen tinyAVR-series(R) 0/1


von Ralph S. (jjflash)


Angehängte Dateien:

Lesenswert?

Beim Evaluieren und Migrieren von tinyAVR(R) 0/1  habe ich jetzt ein 
sehr (doofes) merkwürdiges Phänomen.

AVR-GCC Compiler Version 7.3.0
1
/* ------------------------------------------------------------
2
                          putint
3
4
     gibt einen Integer dezimal aus. Ist Uebergabe
5
     "komma" != 0 wird ein "Kommapunkt" mit ausgegeben.
6
7
     Bsp.: 12345 wird als 123.45 ausgegeben.
8
     (ermoeglicht Pseudofloatausgaben im Bereich)
9
   ------------------------------------------------------------ */
10
void putint(int16_t i, char komma)
11
{
12
  typedef enum boolean { FALSE, TRUE }bool_t;
13
14
  static uint16_t zz[]      = { 10000, 1000, 100, 10 };
15
  bool_t          not_first = FALSE;
16
17
  uint8_t    zi;
18
  int16_t    z, b;
19
20
  komma= 5-komma;
21
22
  if (!i)
23
  {
24
    my_putchar('0');
25
  }
26
  else
27
  {
28
    if(i < 0)
29
    {
30
      my_putchar('-');
31
      i = -i;
32
    }
33
34
    for(zi = 0; zi < 4; zi++)
35
    {
36
      z = 0;
37
      b = 0;
38
39
      if  ((zi== komma) && komma)
40
      {
41
        if (!not_first) my_putchar('0');
42
        my_putchar('.');
43
        not_first= TRUE;
44
      }
45
46
      while(z + zz[zi] <= i)
47
      {
48
        b++;
49
        z += zz[zi];
50
      }
51
      if(b || not_first)
52
      {
53
        my_putchar('0' + b);
54
        not_first = TRUE;
55
      }
56
      i -= z;
57
    }
58
    if (komma== 4) my_putchar('.');
59
    my_putchar('0' + i);
60
  }
61
}
putint funktioniert auf den klassischen Controllern genau so, wie es 
soll. Getestete Conroller waren ATmega328p, ATMega168, ATtiny44 und 
ATtiny2313.

Binde ich genau denselben Code nun in der tinyAVR 0/1 series ein 
(getestete Controller ATtiny214 und ATtiny1604), dann gibt mir putint 
nur Zahlen von 0..9 korrekt aus, bei Werten oberhalb von 10 "spinnt" er 
herum zählt einfach die Ascii-Zeichen weiter, die nach Ascii-9 kommen.

Für 10 also den Doppelpunkt, für 11 den Strichpunkt etc.

Wo liegt hier der Hund begraben?

Ich gehe schlicht davon aus, dass in meinem putint etwas "unsauber" ist, 
aber ich finde das nicht !

By the way:

die aufgerufene Funktion my_putchar(uint8_ch) funktioniert problemlos, 
einzelne Aufrufe in der Art:
1
[code]  my_putchar('0'+5);
gibt auch eine '5' aus!

von Peter D. (peda)


Lesenswert?

Ich kann mich dunkel erinnern, beim ATtiny26 gab es einen Bug mit dem 
"LPM Rd, Z+" Befehl.
Vielleicht haben sie den Bug nun erneut eingebaut.

: Bearbeitet durch User
von Frank K. (fchk)


Lesenswert?

Ralph S. schrieb:

> Wo liegt hier der Hund begraben?
>
> Ich gehe schlicht davon aus, dass in meinem putint etwas "unsauber" ist,
> aber ich finde das nicht !

MPLABX und ein PICKIT4 oder SNAP zu nehmen und den Code im Singlestep 
durchzugehen ist zuviel verlangt?

fchk

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Hier mal die Diskussion zu dem ATTiny26 Bug:
Beitrag "Problem mit ltoa"

von Ralph S. (jjflash)


Lesenswert?

Frank K. schrieb:
> MPLABX und ein PICKIT4 oder SNAP zu nehmen und den Code im Singlestep
> durchzugehen ist zuviel verlangt?

Mir ist der Aufwand für die kleinen tinyAVR series 0/1 zu groß und ich 
werde mir hierfür kein PicKit4 zulegen (ich habe nur einen alten PicKit2 
für die PIC Controller). MPLAB ist mir, wie ich in einem anderen Thread 
schon geschrieben hatte, zu groß und zu "schwerfällig", vor allem, wenn 
man sowieso nach guter alter (veralteter) Sitte sich seine Makefiles 
selbst zusammen bastelt und das ganze damit auch noch komplett auf der 
Konsole lauffähig sein soll.
Im "Normalfall" bin ich eher mit STM32 unterwegs, hier dann mit einen 
originalen ST-Link und Debugger.

Von daher erübrigt sich die Frage nach dem zu viel verlangt sein.

Außerdem hoch verrückt: Nach der Installation von avr-gcc 12.2.0 macht 
putint das, was es soll. Also laß ich diesen Compiler installiert.

-------------------------------------------------

Peter D. schrieb:
> Hier mal die Diskussion zu dem ATTiny26 Bug:
> Beitrag "Problem mit ltoa"

Danke Peda (und das Danke meine ich auch uneingeschränkt als Danke). 
ltoa habe ich nicht ausprobiert, aber itoa. Witzigerweise funktioniert 
das mit der Version 7.3.0 !!!

Aber das Problem hat sich mit der Version 12.2.0 jetzt eh erledigt. Ich 
hatte noch nie einen wirklich aktuellen avr-gcc installiert gehabt, weil 
ich mir bisher damit dann immer andere Probleme eingefangen hatte.

von Ralph S. (jjflash)


Lesenswert?

Dank Peter mit dem Hinweis auf:

Peter D. schrieb:
> Beitrag "Problem mit ltoa"

habe ich mir dort den Thread und dem Problem mit der Adressierung sehr 
aufmerksam durchgelesen.

Danach habe ich meinen eigenen Code genauer angesehen (und natürlich 
wieder verstanden was ich da gemacht habe, auch wenn es nicht 
kommentiert ist - ich sollte mehr sprechende Variablennamen verwenden).

Nimmt man das Array zz[] mit den Zehnerpotenzen aus der Funktion heraus 
und macht dieses Array global, funktioniert putint wie gewünscht auch 
unter avr-gcc 7.3 .

Allerdings darf das Array nicht als static definiert werden, weil auch 
dann dieselbe Fehlfunktion auftritt.

Deshalb hier der funktionierende Code:
1
uint16_t zz[]      = { 10000, 1000, 100, 10 };
2
3
/* ------------------------------------------------------------
4
                            putint
5
     gibt einen Integer dezimal aus. Ist Uebergabe
6
     "komma" != 0 wird ein "Kommapunkt" mit ausgegeben.
7
8
     Bsp.: 12345 wird als 123.45 ausgegeben.
9
     (ermoeglicht Pseudofloatausgaben im Bereich)
10
   ------------------------------------------------------------ */
11
void putint(int16_t i, char komma)
12
{
13
14
  typedef enum boolean { FALSE, TRUE }bool_t;
15
16
  bool_t          not_first = FALSE;
17
18
  uint8_t    zi;
19
  int16_t    z, b;
20
21
  komma= 5-komma;
22
23
  if (!i)
24
  {
25
    my_putchar('0');
26
  }
27
  else
28
  {
29
    if(i < 0)
30
    {
31
      my_putchar('-');
32
      i = -i;
33
    }
34
35
    for(zi = 0; zi < 4; zi++)
36
    {
37
      z = 0;
38
      b = 0;
39
40
      if  ((zi== komma) && komma)
41
      {
42
        if (!not_first) my_putchar('0');
43
        my_putchar('.');
44
        not_first= TRUE;
45
      }
46
47
      while(z + zz[zi] <= i)
48
      {
49
        b++;
50
        z += zz[zi];
51
      }
52
      if(b || not_first)
53
      {
54
        my_putchar('0' + b);
55
        not_first = TRUE;
56
      }
57
      i -= z;
58
    }
59
    if (komma== 4) my_putchar('.');
60
    my_putchar('0' + i);
61
  }
62
}

von Peter D. (peda)


Lesenswert?

Ralph S. schrieb:
> Nimmt man das Array zz[] mit den Zehnerpotenzen aus der Funktion heraus
> und macht dieses Array global, funktioniert putint wie gewünscht auch
> unter avr-gcc 7.3 .

Nun, dann wird es im Initcode vor Main geladen.
Innerhalb einer Funktion wird es aber erst direkt beim Eintritt geladen, 
d.h bei jedem Eintritt neu.
Man könnte es auch per PROGMEM in den Flash legen, spart dann SRAM ein.

Ein Fehler im Chip ist weiterhin nicht auszuschließen, d.h. eine 
Instruktion hat einen Seiteneffekt. Der avr-gcc 12.2.0 könnte diesen 
Befehl anders realisiert haben, so daß der Bug sich nicht auswirkt.
Ein Bug im avr-gcc 7.3 erscheint mir unwahrscheinlich, da ja ATmega168 
usw. gehen.

von Christian B. (casandro)


Lesenswert?

Probier doch mal den Code zu minimieren und schau Dir dann mal an was 
der Compiler daraus macht. So als Komplettwust wird das niemand 
verstehen können.

So als Skizze würde ich das wie folgt machen:
void print_ziffer(int *zahl, const int stelle, int *print_null)
{
  int cnt=0;
  while (zahl>=stelle) {
     cnt=cnt+1;
  }
  if ((cnt!=0) || (*print_null!=0)) {
    my_putchar(cnt+'0');
    *print_null=1;
  }
}

Das rufst Du dann so auf:

void print_zahl(const int zahl_)
{
   int zahl=zahl_;
   int print_null=0; //keine führenden Nullen
   print_ziffer(&zahl, 10000, &print_null);
   print_ziffer(&zahl, 1000, &print_null);
   print_null=1; //Ab der "einer"-Stelle führende Nullen
   print_ziffer(&zahl, 100, &print_null);
   my_putchar('.');
   print_ziffer(&zahl, 10, &print_null);
   print_ziffer(&zahl, 1, &print_null);
}

Damit hast du das Problem in kleinere Teilprobleme zerlegt und kannst 
sehen wo  was schief läuft. Nebenbei brauchst Du da auch kein LPM. 
Funktionsaufrufe sind auch was, dass der Compiler gut auflösen kann.

: Bearbeitet durch User
von Ralph S. (jjflash)


Lesenswert?

Peter D. schrieb:
> Nun, dann wird es im Initcode vor Main geladen.
> Innerhalb einer Funktion wird es aber erst direkt beim Eintritt geladen,
> d.h bei jedem Eintritt neu.

... das war der Sinn, weshalb ich das global gemacht habe.

Peter D. schrieb:
> Man könnte es auch per PROGMEM in den Flash legen, spart dann SRAM ein.

Hm, ob sich das rentiert? Das Array belegt im SRAM 8 Byte.

von Christian B. (casandro)


Lesenswert?

Christian B. schrieb:
> void print_ziffer(int *zahl, const int stelle, int *print_null)
...
>      cnt=cnt+1;
       *zahl=*zahl-stelle; //Fehlte vorher.
>   }

von Ralph S. (jjflash)


Lesenswert?

Christian B. schrieb:
> Christian B. schrieb:
>> void print_ziffer(int *zahl, const int stelle, int *print_null)
> ...
>>      cnt=cnt+1;
>        *zahl=*zahl-stelle; //Fehlte vorher.
>>   }

ich werde deine Vorschläge ausprobieren und sehen, wie groß der Code 
wird. In einem ATtiny214 hat man nur 2 kByte Speicher, von daher bin ich 
da mit der Codegröße extrem geizig.

In jedem Fall werde ich die Variablennamen ändern, damit man die 
Funktion besser verstehen kann.

von Christian B. (casandro)


Lesenswert?

Die Option -Os ist dann Dein Freund, die optimiert das auf Größe, und 
Compiler sind heute sehr gut darin Dinge zu optimieren.

von Ralph S. (jjflash)


Lesenswert?

Christian B. schrieb:
> Die Option -Os ist dann Dein Freund, die optimiert das auf Größe, und
> Compiler sind heute sehr gut darin Dinge zu optimieren.

Die Option -Os ist bei mir standardmäßig gesetzt!

von Frank K. (fchk)


Lesenswert?

Ralph S. schrieb:

> Mir ist der Aufwand für die kleinen tinyAVR series 0/1 zu groß und ich
> werde mir hierfür kein PicKit4 zulegen (ich habe nur einen alten PicKit2
> für die PIC Controller). MPLAB ist mir, wie ich in einem anderen Thread
> schon geschrieben hatte, zu groß und zu "schwerfällig", vor allem, wenn
> man sowieso nach guter alter (veralteter) Sitte sich seine Makefiles
> selbst zusammen bastelt und das ganze damit auch noch komplett auf der
> Konsole lauffähig sein soll.
[...]
> Von daher erübrigt sich die Frage nach dem zu viel verlangt sein.

Professionell ist anders. Aber ok, die Chance, wirklich die Stelle zu 
finden, wo das Problem genau ist, hast Du Dir damit genommen. Ist dann 
wohl auch nicht so wichtig.

fchk

von Ralph S. (jjflash)


Lesenswert?

Frank K. schrieb:
> Professionell ist anders. Aber ok, die Chance, wirklich die Stelle zu
> finden, wo das Problem genau ist, hast Du Dir damit genommen. Ist dann
> wohl auch nicht so wichtig.

Na ja, ich habe die Stelle ja auch so gefunden !
Zugegeben: Mit Debugger ist vieles einfacher, auf STM32 bin ich damit 
auch unterwegs. Wirklich immer muß das aber nicht sein.

Außerdem: Ich bin kein professioneller Softwareentwickler

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.