Forum: Mikrocontroller und Digitale Elektronik [Glaskugel] Funktionsaufruf lässt Controller hängen


von D a v i d K. (oekel) Benutzerseite


Lesenswert?

Hi,

wie die Überschrift vermuten lässt erwarte ich keine Wunder.
Aber vielleicht fällt dem ein oder anderem etwas dazu ein?
(Falls nicht bitte einfach weiter gehen :P)

...wieso der folgende Code beim einmaligen und zweimaligen Aufruf 
passiert werden kann, jedoch bei 3-maligem Aufruf den Atmega16 zum 
hängen bringt.
(Meine Annahme, dass er hängen bleibt beruht auf der Ausgabe auf einem 
2x16 Zeilen-Display bzw. der NICHT Ausgabe der sofort folgendem 
Zeilenausgabe auf demselbigen.)
1
//mein "Werk"
2
void lcd_printInt(uint16_t value, uint8_t c, bool leadingZeros) {
3
  char str[c];
4
  utoa(value, str, 10);
5
  if (leadingZeros) {
6
    for (uint8_t i = getDigitCount(value); i < c; i++)
7
      lcd_print_P(PSTR("0"));
8
  }
9
  lcd_print(str);
10
}
11
12
//-  Print string to cursor position
13
void lcd_print(char *string) {
14
15
  while(*string)  {
16
    lcd_putchar(*string++);
17
  }
18
}
19
//-  Put char to atctual cursor position
20
void lcd_putchar(char lcddata) {
21
22
  lcd_write((lcddata >> 4) | CMD_RS);
23
  lcd_write((lcddata & 0x0F) | CMD_RS);
24
}

Mein Aufruf:
1
#define IN2 !(PINA & (1<<PA6))//PIN34
2
#define JP1 !(PINA & (1<<PA0))//PIN39
3
#define JP2 !(PINA & (1<<PA1))//PIN40
4
5
lcd_printInt(JP1, 1, false);
6
lcd_printInt(JP2, 1, false);
7
lcd_printInt(IN2, 1, false);

Mein Workarround:
1
uint8_t combi = JP1*100+JP2*10+IN2;
2
lcd_printInt(combi, 3, true);

Grüße Oekel

von Felix U. (ubfx)


Lesenswert?

D a v i d K. schrieb:
> char str[c];

also in c bytes kannst du schon mal nicht c Zeichen plus 1 Nullbyte für 
die Terminierung unterbringen. Vielleicht liegt es daran?

von Jim M. (turboj)


Lesenswert?

Deine lcd_printInt hat einen Buffer Overflow bei
1
char str[c];


Der muss das ganze Ergebnis von utoa inklusive nachfolgender Null 
enthalten können.

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Du machst da ja auch ganz grossen Mist.
1
#define IN2 !(PINA & (1<<PA6))//PIN34
2
3
4
lcd_printInt(JP1, 1, false);  // <-- !!!
5
6
7
void lcd_printInt(uint16_t value, uint8_t c, bool leadingZeros) {
8
  char str[c];               // <-- !!!
9
  utoa(value, str, 10);      // <-- !!!
10
  if (leadingZeros) {
11
    ...
12
  }
13
  lcd_print(str);
14
}
15
16
17
void lcd_print(char *string) {
18
  while(*string)  {          // <-- !!!
19
    ...
20
  }
21
}

Schau dir mal die Stellen an, die ich markiert habe...
Dazu noch das hier:
http://www.atmel.com/webdoc/AVRLibcReferenceManual/group__avr__stdlib_1gad50d835af7e358518fd100179732e948.html
1
The function utoa() converts the unsigned integer value from val into
2
an ASCII representation that will be stored under s. The caller is
3
responsible for providing sufficient storage in s.
4
5
6
Note
7
8
The minimal size of the buffer s depends on the choice of radix. For
9
example, if the radix is 2 (binary), you need to supply a buffer with a
10
minimal length of 8 * sizeof (unsigned int) + 1 characters, i.e. one
11
character for each bit plus one for the string terminator. Using a larger
12
radix will require a smaller minimal buffer size.
13
14
15
Warning
16
17
If the buffer is too small, you risk a buffer overflow.

von zer0 (Gast)


Lesenswert?

"Zeilenausgabe" - Hat das Ding Scrolling eingebaut oder sowas? Auf einem 
2-Zeilen display verschwindet die 3. sonst vermutlich im Nirvana.

Ansonsten: Die Timings für das Ding alle penibel beachtet? Sonst kann 
schon mal irgendetwas verschluckt werden - irgendwann...

Wenn der Code echt irgendwo hängen bleibt würde ich tippen, er wartet 
auf ein Zeichen vom LCD, das niemals kommt.

von Axel S. (a-za-z0-9)


Lesenswert?

Jim M. schrieb:
> Deine lcd_printInt hat einen Buffer Overflow bei
>
>
1
char str[c];
>
> Der muss das ganze Ergebnis von utoa inklusive nachfolgender Null
> enthalten können.

Nicht zu vergessen der Irrsinn, einen solchen Puffer überhaupt mit 
dynamischer(!) Länge anzulegen. Ein uint16_t hat maximal 5 
Dezimalstellen, dazu noch das terminierende \0, macht einen Buffer Länge 
6. Den alloziert man einmal, vorzugsweise als
1
static char str[6];

und nicht bei jedem Aufruf der Funktion wieder.

Mal wieder ein Traumbeispiel für den immer "korrekten Code" des TE.

von zer0 (Gast)


Lesenswert?

Felix U. schrieb:
> D a v i d K. schrieb:
>> char str[c];
>
> also in c bytes kannst du schon mal nicht c Zeichen plus 1 Nullbyte für
> die Terminierung unterbringen. Vielleicht liegt es daran?

Wenn man das so betrachtet klingt das plausibel. Allerdings - was sollte 
da denn auf dem Stack kaputt gehen, was 2 mal aber nicht 3 mal gut geht? 
Schreibt doch immer eine 0 also müsste alles deterministisch ablaufen...

von Stefan E. (sternst)


Lesenswert?

zer0 schrieb:
> Allerdings - was sollte
> da denn auf dem Stack kaputt gehen, was 2 mal aber nicht 3 mal gut geht?

Die Return-Adresse.

von zer0 (Gast)


Lesenswert?

Stefan E. schrieb:
> zer0 schrieb:
>> Allerdings - was sollte
>> da denn auf dem Stack kaputt gehen, was 2 mal aber nicht 3 mal gut geht?
>
> Die Return-Adresse.

Und deshalb erscheint nichts auf dem Display?

von zer0 (Gast)


Lesenswert?

zer0 schrieb:
> Stefan E. schrieb:
>> zer0 schrieb:
>>> Allerdings - was sollte
>>> da denn auf dem Stack kaputt gehen, was 2 mal aber nicht 3 mal gut geht?
>>
>> Die Return-Adresse.
>
> Und deshalb erscheint nichts auf dem Display?

Ach so, nach dem 2. ist er im Nirvana... Das könnte durchaus sein.

von D a v i d K. (oekel) Benutzerseite


Lesenswert?

Axel S. schrieb:

> Mal wieder ein Traumbeispiel für den immer "korrekten Code" des TE.
Diese Aussage kreidest du mir wohl jetzt noch eine Weile an? :)

Ich bin nicht perfekt aber ebenso wenig nun eingeschnappt, daher gerne 
mehr davon, solange ich gleichzeitig viele hilfreiche Infos bekomme.

Nur SO lerne ich etwas. Danke.

von MaWin (Gast)


Lesenswert?

Vom Überlauf abgesehen wird der Code effizienter, wenn du den Puffer 
nicht dynamisch, sonder statisch auf dem Stack alloziierst. Als Größe 
einfach die Anzahl der Ziffern der größten Zahl, die utoa ausgeben kann, 
plus eins für NUL.

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.