Forum: Mikrocontroller und Digitale Elektronik AVR ATMEGA328PB mit UART.h stürzt ab


von Slav (Gast)


Lesenswert?

Hallo,
ich beschäftige mich seit ein paar Wochen mit Mikrocontroller 
Programmierung und verwende dazu einen ATMEGA328PB welcher auf einem 
Board über UART mit dem Rechner kommuniziert. (Eigentlich sind es insg. 
10 Mikrocontroller aber für das beschriebene Problem sollte nur der 
Master Controller relevant sein)

Im eigentlichen Skript geht es darum einen Datensatz mit Positionswerten 
zu generieren, aber ich bin auf einen Fehler gestoßen welcher den 
Mikrokontroller abstürzen lässt (zumindest springt er in die Main 
zurück).

Der Fehler ist auch mit dem unten aufgeführten Minimalbeispiel 
vorführbar, daher muss ich scheinbar irgendwas fundamentales 
missverstehen das nichts mit der Wertgenerierung an sich zu tun hat.

Im aktuellen Zustand gibt das Skript des mikrokontrollers beim Start die 
Standardmeldung "Master ready" über UART aus und ich erhalte die Ausgabe 
über hTerm am Rechner.
Dann geht das Skript in eine Warteschleife in der es auf einen 
beliebigen Input über UART wartet.
Sobald ich irgendein Zeichen sende müsste die generate_ramp Funktion 
durchlaufen und mit der Ausgabe "Exit function" abschließen.
Das tut sie auch, allerdings nur für ramp_freq >= 2.0
Für ramp_freq <= 1.0 stürzt das Skript ab und der Kontroller startet 
neu, was sich dadurch äußert, dass wieder die Standardmeldung "Master 
ready" ausgegeben wird.
Der "tolerierbare Bereich" rutscht tiefer wenn ich die globale Variable 
DATA_FREQ auf 256 setze, dann gehen also auch Werte zwischen 1.0 und 2.0 
aber nicht kleiner 1.0.

Ich habe also versucht manuell Werte für seq_Length vorzugeben und dabei 
festgestellt:
- Werte <= 452 funktionieren problemlos
- Werte >=  453 führen zu fehlerhafter UART Ausgabe aber es sind 
korrekte Zeichen dabei
- Werte zwischen 456 und 506 führen zu einer Endlosschleife der UART 
Ausgabe)
- Werte >=  507 führen zum altbekannten Neustart

Gebe ich den Wert von seq_Length direkt ohne Variable vor so 
funktioniert
das Skript problemlos auch für Werte weit über 512.



Ich hoffe ich habe alles beschrieben was hilfreich sein kann und dass 
irgendwer besser versteht was hier vor sich geht.
Für hilfreiche Kommentare wäre ich sehr dankbar.

1
#include <stdlib.h>
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#include <util/delay.h>
5
#include <time.h>
6
#include <stdbool.h>
7
#include <stdint.h>
8
#include "uart.h"
9
#include "linsin16.h"
10
11
/* define CPU frequency in Hz in Makefile */
12
#ifndef F_CPU
13
#error "F_CPU undefined, please define CPU frequency in Hz in Makefile"
14
#endif
15
16
/* Define UART buad rate here */
17
#define UART_BAUD_RATE      200000  //115200    
18
19
#define RESET  PC4
20
#define RESET_SLAVES      {(PORTC |= (1 << RESET)); (DDRC |= (1 << RESET)); _delay_ms(100); (PORTC &= ~(1 << RESET)); _delay_ms(100);}
21
22
#define T_TRIGGER_512      37499
23
#define T_TRIGGER_256      9374
24
#define DATA_FREQ_512      (19200000/(T_TRIGGER+1))
25
#define DATA_FREQ_256      (2400000/(T_TRIGGER+1))
26
27
#define T_TRIGGER      T_TRIGGER_512
28
#define DATA_FREQ      DATA_FREQ_512
29
30
/***********************************************
31
    Functions
32
***********************************************/
33
struct Pos_Array { uint8_t   data[4];};
34
35
void generate_ramp(float ramp_freq){
36
37
  int seq_Length    = (int)((float)DATA_FREQ/ramp_freq);
38
  //int seq_Length  >= 507;    // Klappt nicht
39
  //int seq_Length  >= 453;    // Klappt aber nicht richtig
40
  //int seq_Length  = 452;    // Klappt
41
  struct Pos_Array   ramp_pos_arr[seq_Length];
42
  
43
  for(int i=0;i<seq_Length;i++){
44
      ramp_pos_arr[i].data[0] = 0x00;    
45
  }
46
47
  uart_puts("Exit function");
48
  uart_puts("\r\n");
49
  return;
50
}
51
52
53
int main(void)
54
{
55
  uint16_t   c;
56
57
  RESET_SLAVES; // Sets reset signal for other controllers
58
  
59
  uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );           // Initialize UART library, pass baudrate and AVR cpu clock with the macro 
60
  
61
  sei();                                  // Enable interrupt, since UART library is interrupt controlled
62
  
63
  uart_puts("Master ready");
64
  uart_puts("\r\n");
65
66
  for(;;){
67
    do{  
68
      c   = uart_getc();
69
      if ( c & UART_NO_DATA ){/* no data available from UART*/}
70
      else{
71
        generate_test( 1.0);
72
      }
73
    }while (!( c & UART_NO_DATA ));
74
  }
75
  uart_puts("Exit Main");
76
  uart_puts("\r\n");
77
}

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Davon abgesehen, dass hier keiner eine Idee hat, was du da für 
UART-Funktionen benutzt: du willst seq_Length Elemente zu je vier Byte 
speichern.

Nun rechne bitte mal aus, wieviele Byte das bei seq_Length=452 sind, und 
dann schau ins Datenblatt, wieviel RAM dein Controller so hat …

von Slav (Gast)


Lesenswert?

Achso ja klar, danke schonmal für die Antwort.
ich benutze die "UART Lib." vom Peter Flury
(http://homepage.hispeed.ch/peterfleury/avr-software.html)

Bezüglich der Speichergröße wären das dann etwa 1.8kB für die 
ramp_pos_arr Variable womit also unter 0.2kB übrig bleiben für den Rest 
des Skripts (das könnte in der Tat zu wenig sein)

Warum aber funktioniert das Skript dann vollkommen in Ordnung, wenn ich 
die Sequenz Länge direkt eingebe also so z.B. würden ja über 2kB belegt 
werden
1
  struct Pos_Array   ramp_pos_arr[512];
2
  for(int i=0;i<512;i++){
3
      ramp_pos_arr[i].data[0] = 0x01;
4
                        ramp_pos_arr[i].data[1] = 0x01;
5
                        ramp_pos_arr[i].data[2] = 0x01;
6
                        ramp_pos_arr[i].data[3] = 0x01;
7
  }

von Irgend W. (Firma: egal) (irgendwer)


Lesenswert?

Slav schrieb:
> Warum aber funktioniert das Skript dann vollkommen in Ordnung, wenn ich
> die Sequenz Länge direkt eingebe also so z.B. würden ja über 2kB belegt
> werden
Könnte damit das die variable Speicherreservierung in deiner ersten 
Variante etwas anders funktioniert als die zweite.
- https://en.wikipedia.org/wiki/Variable-length_array

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Slav schrieb:
> womit also unter 0.2kB übrig bleiben

Der UART-Code wird möglicherweise auch noch einen Puffer haben.

von Slav (Gast)


Lesenswert?

Hi, danke für den Hinweis.

An sich macht das ja Sinn, aber ganz ist der Groschen bei mir noch nicht 
gefallen.

Meine Variante aus dem zweiten Post entspricht doch der Initialisierung 
eines Feldes mit fixer Größe, was bedeutet, dass die Größe zum Zeitpunkt 
der Übersetzung festgelegt wird. Beim Übersetzen und Laden bemerkt der 
AVR Dude das ja schonmal nicht, aber wie weißt der Mikrokontroller 
während der Laufzeit 512*4 Feldern einen Byte Wert zu und gibt im 
Nachhinein noch völlig korrekt die UART Ausgabe aus, ohne etwas zu 
bemerken ??

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Du hast soeben entdeckt, was "undefined behaviour" bedeutet. ;-)

Es heißt eben nicht, dass es schief gehen muss, aber es bedeutet, dass 
alles Mögliche passieren kann. Was genau, kann man nicht vorhersagen.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

AVRDUDE weiß davon rein gar nichts, weil es sich um den SRAM ja nicht 
kümmert. Der Linker könnte es bemerken, allerdings sind die 
AVR-Linkerscripte relativ generisch gehalten und nur grob in MCU-Klassen 
eingeteilt. Daher benennen sie immer den maximal möglichen Speicher 
einer jeden MCU-Klasse. (Ansonsten müsste man mehrere Dutzend 
Linkerscripte pflegen und die Binutils wären für jeden neuen AVR extra 
anzupassen.)

Du solltest dir einfach angewöhnen, am Ende des Compilierens noch ein 
"avr-size" auf das generierte ELF-File loszulassen.

von beo bachta (Gast)


Lesenswert?

Slav schrieb:
> struct Pos_Array   ramp_pos_arr[seq_Length];

Das was ich zu bemängeln habe mag für die eine oder andere
Prozessor-Architektur nicht zutreffen, diese Einschränkung
also vorab. Vielleicht ist es auch Compiler-abhängig?

Aber es ist kein guter Stil innerhalb einer Funktion ein so
grosses Array anzulegen. Manche Applikationen (allgemein)
könnten dazu tendieren den Stack zu überlasten, zum Über-
laufen zu bringen. Was dann passiert kann sich jeder aus-
malen: es kracht. Ich hab es erlebt, aber das muss hier
nicht so sein.

my 2ct, YMMV

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

beo bachta schrieb:
> Aber es ist kein guter Stil innerhalb einer Funktion ein so
> grosses Array anzulegen.

Für so einen kleinen Controller ist es generell kein guter Stil, so ein 
großes Array anzulegen – egal ob auf dem Stack oder im Heap oder 
statisch. ;-)

Aber das sollte Slav ja jetzt verstanden haben. Bei einer MCU muss man 
sich um die Menge seiner Daten vor der Programmierung schon mal Gedanken 
machen.

von beo bachta (Gast)


Lesenswert?

Jörg W. schrieb:
> Für so einen kleinen Controller ist es generell kein guter Stil

Naja, was geht das geht .... bis es halt kracht.

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.