PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [ERLEDIGT] Feste Größe ändert sich beim Programmablauf (="Suchspiel" Memoryüberschneidung)



oberallgeier
20.01.2014, 12:51
Bitte um Hilfe

Die Größe int16_t tdek4 zur Dimensionierung einer Geschwindigkeitsrampe wird zu Beginn eines Programmslauf im main in einer Routine zur Dimensionierung verschiedener Größen mit einem Wert belegt und aktuell nicht weiter verwendet (wurde früher verwendet, aktuell inaktiv, wird später wieder benutzt) bis auf die hier erwähnte Ausgabe. Diese Größe belegt das Ende der dokumentierten Memorymap.

Die Ausgabe der Größe in einer Liste zusammen mit anderen Werten zeigt unerklärliche Änderungen bei verschiedenen Ausgabeaufrufen >>obwohl<< sie, wie erwähnt, nur ein Mal mit einem festen Wert belegt wird. Sie nimmt dabei offensichtlich zufällige, soweit feststellbar stets andere Werte an. Auch ohne Aufruf der erwähnten Dimensionierungsroutine sind diese Schwankungen festzustellen.

Die sinngleiche Größe tdek1 für eine andere Geschwindigkeit liegt in der memorymap weit entfernt; sie ist stabil. Auch die der tdek4 in der memorymap benachbarte Größe - die vor ihr liegt, zeigt nicht solche Veränderungen.

Hier ein Auszug aus dem zugehörigen *.map-File, die "E E E.."-Einschübe markieren entfernte Passagen.


Archive member included because of file (symbol)
E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E
-------------------------------------------------------------
Allocating common symbols
Common symbol size file
iyp12 0x2 R5MoCo.o
kend1t1 0x2 R5MoCo.o
E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E
-------------------------------------------------------------
m34vorlauf 0x2 R5MoCo.o
nenc1 0x1 R5MoCo.o
tdek4 0x2 R5MoCo.o <<==
Memory Configuration
Name Origin Length Attributes
text 0x00000000 0x00020000 xr
data 0x00800060 0x0000ffa0 rw !x
eeprom 0x00810000 0x00010000 rw !x
fuse 0x00820000 0x00000400 rw !x
lock 0x00830000 0x00000400 rw !x
signature 0x00840000 0x00000400 rw !x
*default* 0x00000000 0xffffffff
Linker script and memory map
Address of section .data set to 0x800100
LOAD
E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E
-------------------------------------------------------------
c:/programme/winavr-20100110/bin/../lib/gcc/avr/4.3.3/../../../../avr/lib/avr5/crtm328p.o
0x000000a2 __vector_22
0x000000a2 __vector_12
0x000000a2 __bad_interrupt
0x000000a2 __vector_6
0x000000a2 __vector_3
0x000000a2 __vector_23
0x000000a2 __vector_25
0x000000a2 __vector_11
0x000000a2 __vector_13
0x000000a2 __vector_17
0x000000a2 __vector_5
0x000000a2 __vector_4
0x000000a2 __vector_9
0x000000a2 __vector_15
0x000000a2 __vector_10
0x000000a2 __vector_16
0x000000a2 __vector_20
.text 0x000000a6 0x25d6 R5MoCo.o
0x00000430 TC2TMR_init
E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E
-------------------------------------------------------------
0x00000120 __vector_18
0x0000024a itxt_rn
0x000002ee init_twi_slave
.text 0x0000267c 0x0 c:/programme/winavr-20100110/bin/../lib/gcc/avr/4.3.3/avr5\libgcc.a(_udivmodhi4.o)
.text 0x0000267c 0x0
E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E
-------------------------------------------------------------
*(.fini0)
.fini0 0x0000281e 0x4 c:/programme/winavr-20100110/bin/../lib/gcc/avr/4.3.3/avr5\libgcc.a(_exit.o)
*(.fini0)
0x00002822 _etext = .
.data 0x00800100 0x3b6 load address 0x00002822
0x00800100 PROVIDE (__data_start, .)
*(.data)
.data 0x00800100 0x0 c:/programme/winavr-20100110/bin/../lib/gcc/avr/4.3.3/../../../../avr/lib/avr5/crtm328p.o
.data 0x00800100 0x3b6 R5MoCo.o
0x008004b3 Idelta1
0x008004a8 m12v
0x008004a9 m12z
E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E
-------------------------------------------------------------
0x008008a3 m34vorlauf
0x008008a5 nenc1
0x008008a6 tdek4 <<==
0x008008a8 PROVIDE (__bss_end, .)
0x00002822 __data_load_start = LOADADDR (.data)
0x00002bd8 __data_load_end = (__data_load_start + SIZEOF (.data))
.noinit 0x008008a8 0x0
0x008008a8 PROVIDE (__noinit_start, .)
*(.noinit*)
0x008008a8 PROVIDE (__noinit_end, .)
0x008008a8 _end = .
0x008008a8 PROVIDE (__heap_start, .)
.eeprom 0x00810000 0x0
*(.eeprom*)
0x00810000 __eeprom_end = .
.fuse
*(.fuse)
*(.lfuse)
*(.hfuse)
*(.efuse)

Ich bitte um Hinweise woran es liegen könnte und wie ich das abstellen kann.

Ist das RAM so voll, dass der Stackpointer überlauft?
Wie könnte ich besser bzw. eindeutig feststellen, was die Ursache ist?

Ich danke im Voraus für eure Tips und Hilfen.

markusj
20.01.2014, 13:08
Wie könnte ich besser bzw. eindeutig feststellen, was die Ursache ist?
Kannst du uns den Quelltext zeigen? Der Fehler liegt vermutlich an anderer Stelle, es wäre ein großer Zufall wenn der Stack dir tdek4 "frisst" aber nenc1 verschont.

Grüße,
Markus

oberallgeier
20.01.2014, 13:27
Kannst du uns den Quelltext zeigen? Der Fehler liegt vermutlich an anderer Stelle ...Klar - im Prinzip. Aber sechstausend Zeilen Quelltext ist halt etwas viel . . . Vielleicht reichen diese Auszüge:

Auszug main

// - - - - - - - - - - - - - - -
// Ports+Pins als Ein- (0) oder Ausgänge (1) konfigurieren, Pull Ups (1) aktiv.
// A = Ausgang, E = Eingang ohne , EU = Eingang MIT PullUp
DDRB = 0b11111111; // siehe aktuell oben oder Fortschrittsbericht
PORTB = 0b00000000; // und Port/Pull Ups (1) aktivieren
//
DDRC = 0b11000010; // PC0 .. 6 , kein PC6+7-Pin bei m168/328 in THT
PORTC = 0b00000000; // PC0, ~2 und ~3 sind ADC-Eingänge ##>> OHNE Pullup !!
//
DDRD = 0b11000000; // RX+TX Eingang mit Pullup
PORTD = 0b00111111; // Pull Ups aktivieren, mit pololuMot AUCH bei INT0/~1
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
for(i=0; i<10; i++) // gLED2(PB5) blinken lassen bevor Interrupts erlaubt sind
{ // um ungewollte Resets u.ä. besser erkennen zu können
SetBit(PgLED, L1g); // gnLED1 schalten EIN, HELL, aktuelle PC1
SetBit(PORTB, 3 ); // ###>>> TEST
wms (3); // ###>>> Die onBoard-LEDs schalten A<->Portpin <<<###
ClrBit(PgLED, L1g); // gnLED1 schalten AUS, Dunkel
ClrBit(PORTB, 3 ); // ###>>> TEST
wms (97); //
} // Ende von for(i=0; i<10; i++)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Izthrznt = 20000; // Der ZeitHorizont für ISR(TIMER2_COMPA_vect)
//ncx0 = 128; // Messwert-Pointer auf "keine Messwerterfassung"
ncxP = 200; // Prescaler für 1/100 sec bei Messwerterfassung
ncxF = 1; // Messertflag; 1 <=> Wert darf gespeichert werden
Izeit_1 = Izthrznt; //
Isecundn = 1; // Sekundenzähler, max 9 Stunden - NUR hier nullen
TC2TMR_init(); // Init Timer/Cntr2-Interrupt 20 kHz/50 µsec tmr
// ISR gibt auf PB2 ein Servosignal aus
// - - - - - - - - - - - - - - -// Hier die notwendigen Werte für Motorbetrieb=Encoder, Regelfaktoren etc
// .. erforderlich VOR Initialisierung der externen Interrupts und Regelung
nenc0 = 0; // Encoder-Prescaler, nur jeden nenc0-ten Übergang zählen
nenc1 = 0;
Kp12 = 1; // Regelfaktor P-Anteil !!! Divisor, lt fahrtest = 4 (1/4)
Kp34 = 1; // Regelfaktor P-Anteil !!! Divisor ### war 5
Ki12=Ki34 = 22; // Regelfaktor I-Anteil !!! Divisor
Kd12=Kd34 = 4; // Regelfaktor D-Anteil !!! Divisor
//ATSfak = 1856; // AnTriebsStrang-faktor => Umrechnung von mm/s auf stupsi
ATSfak = 619; // AnTriebsStrang-faktor => Umrechnung von mm/s auf stupsi
regel12=regel34 = 0; // Ein-Ausschalter Regelung, regel = 1 => Regelung ist EIN
rglsv12 = regel12; rglsv34 = regel34; // Regelflag, Sicherung
XTI_0_1_init( ); // Init extINT 0 und 1 mot
TC0CMPit(); // Init TmrCnt0 CmprMtchA, Regelungsaufruf 100Hz/Kanal tmr
TC1PWM_init(); // Init Tmr/Cntr1 für PWMs/Mot12 + Mot34 mot
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sei(); //Globalen Interrupt freigeben
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
inKOlst(); // Initialisierung, KOnstanten-Liste = div. Vorgaben kal
// Aufruf NACH sei(), weil evtl irDME´s abgefragt werden.
init_uart0 ( F_CPU / BAUD / 16 - 1 ); // Init UART0 (NICHT nach PDannegger)
I2C_init (); // I2C-Buffer initialisieren, I2C-Slave-Init i2c
// - - - - - - - - - - - - - - -
wms ( 1000); // Zeit fürs Startinfo - NUR FÜR TESTphase ! ! !
info01(); // Startinfo über USART ausgeben main

Auszug *com*.h

volatile s16 M12ocr; // temporärer Stellwert für OCR1A -255...255
volatile s16 M12inc; // vorzeichenbehaft. Incr, default: -1, +1
volatile s16 tdek1; // Timer-Dekrement = Vorgabe für tmot1
volatile s16 P4soll; // PWMsollwert bei Aufruf wird aus speed übernommen,
// speed wird nicht weiter verwendet
volatile s16 M34ocr; // temporärer Stellwert für OCR1A -255...255
volatile s16 M34inc; // vorzeichenbehaft. Incr, default: -1, +1
volatile s16 tdek4; // Timer-Dekrement = Vorgabe für tmot4

inKOlst

// ================================================== ============================ =
void inKOlst (void) // Initialisierungen
{ //
// war 50 bis 30. Dez. 2013
tdek1 = 6; // default Startwert PWM-Timer - wird dekrementiert
tdek4 = 6; // default Startwert PWM-Timer - wird dekrementiert
return; //
} //
// ================================================== ============================ =

Auszug Timer

// ================================================== =========================== =
// === Nicht unterbrechbare ISR für timer2 =================================== */
// Takt 20 Ips bei 20Mhz - Routine für regelmässige Abläufe mit
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ISR(TIMER2_COMPB_vect) // Vekt 0x008, Timerroutine für "allgemeine Zwecke"
{ // .. die nicht so eng zeitgebunden sind
// - - - - - - - - - - - - - - - -
Izeit_2 --; // ###>>> Izeit_2 ist aktuell int16_t ==>>
if ( !Izeit_2 ) // WENN Izeit_2 != Null => wahr => Anweisung ausgeführen
{ // Eine Sekunde ist voll => Izeit_2 auf Hrznt_2 setzen
Izeit_2 = Hrznt_2; // ansonsten: Rückstellen auf Zeithorizont2
} // Ende if ( !Izeit_2 )
// - - - - - - - - - - - - - - - -
// PWM-Rampe für Motor1/Mot12 - wird über tmoti/tdeki gesteuert
if ( tmot1 ) // Ist der Rampencounter != 0 ?
{ //
tmot1 --; // ... dann dekrementiere ihn bis
if ( !tmot1 ) // ... der Rampenwert abgelaufen ist
{ //
M12ocr = M12ocr + M12inc; //##>>Übegang zu NEGATIVEn M12ocr möglich
// Es folgt Drehrichtungsumkehr NACH Nulldurchgang
// war if (( M12ocr < 0) && (-1 == M12ocr)) Mrechtszur (); und sinngemäß //
if (-1 == M12ocr) Mrechtszur (); //
if ( 1 == M12ocr) Mrechtsvor (); //
// Setze OCR1A aus vorzeichenbehaftetem M12ocr
if (M12ocr >= 0) OCR1A = M12ocr; //
else OCR1A = -1*M12ocr; //
if (M12ocr == P1soll) tmot1 = 0; // Rampe12: Ende mit Sollwert erreicht
else tmot1 = tdek1; // ansonsten ReInit Timer auf Rampenwert
} // Ende if ( !tmot1 )
} // Ende von if ( tmot1 )
// - - - - - - - - - - - - - - - -
// PWM-Rampe für Motor4/Mot34
if ( tmot4 ) // Rampencounter > 0
{ //
tmot4 --; //
if ( !tmot4 ) // Wenn Rampenwert abgelaufen ist
{ //
M34ocr = M34ocr + M34inc; //##>>Übergang zu NEGATIVEn M34ocr möglich
// Es folgt Drehrichtungsumkehr NACH Nulldurchgang (siehe auch oben)
if (-1 == M34ocr) Mlinksvor (); //
if ( 1 == M34ocr) Mlinkszur (); //
// Setze OCR1A aus vorzeichenbehaftetem M34ocr
if (M34ocr >= 0) OCR1B = M34ocr; //
else OCR1B = -1*M34ocr; //
if (M34ocr == P4soll) tmot4 = 0; // Rampe34: Ende mit Sollwert erreicht
else tmot4 = tdek4; // ansonsten ReInit Timer auf Rampenwert
} // Ende if ( !tmot4 )
} // Ende von if ( tmot4 )
// - - - - - - - - - - - - - - - -
return;
} // Ende ISR(TIMER2_COMPA_vect)
// ================================================== =========================== =


Quelltext sprung (kplt) - hier wurde der falsche Wert entdeckt:

// ================================================== =========================== =
// == Fahre Sprungfunktion, messe Sprungantwort Aufruf aus r1n/do_MOSTL2
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void sprung ( u8 m_buff[14] ) // Fahre Sprungfunktion, messe Sprungantwort
{ //
// - - - - - - - - - - - - - - - -
//char abc[12]; // Übersetzungsfeld für Werteausgabe uart
char s[10]; // lok. String zum Dekodieren der SOLLFAHRT mm/sec
s16 s16mot = 0; // SOLLfahrt Motor, 2 Bytes : unsigned int16_t
u8 i = 0; // Variable
u8 motNR; // MotorNummer 1 (mot12) oder 4 (mot34)
u8 vz = 0; // Drehrichtungscode: 45 = li, 43 = re, 112 = aus
// - - - - - - - - - - - - - - - -
// ncx0 wird mit Wert 255 im main initialisiert und damit "abgeschaltet"
// WENN ncx0 < 128 ist (erstes Bit ist 0) dann wird im Timer0 incrementiert
//ncx0 = 255; // Initialisiere Pointer auf 1 vor Überlauf
// - - - - - - - - - - - - - - - -
// Die aufrufenden Parameter als Kontrolle/Bestätigung auf uart ausgeben
for ( i=0; i<9; i++) { s[i] = m_buff [i]; } // Aufrufparameter kopieren
s[9] = 0; // Markiere Stringende "0"=letzten Bitfeldelement
uputs0 ("\r\t>>\t");uputs0 ( s );
// - - - - - - - - - - - - - - - -
// Regelungen und sonstige Parameter abschalten:
regel12 = regel34 = 0; // BEIDE REGELUNGen ausschalten

// - - - - - - - - - - - - - - - -
// Dekodiere PWM-Vorzeichen und PWM-Stellwert
vz = m_buff[3]; // Drehrichtungscode: 45 = '-' => math neg
for ( i=0; i<=2; i++) { s[i] = m_buff [i+3]; } // im Byte 2 bis 5 {0-5}
s[3] = 0; // Markiere Stringende "0"
s16mot = atoi ( s ); // vorzeichenbehafteter Motor-/PWM-Stellwert
// - - - - - - - - - - - - - - - -
// Motor NR dekodieren und Daten melden
motNR = m_buff[1] - 48; // nmot ist die Motornummer 1 (12) oder 4 (34)
if ( !(motNR == 1) && !(motNR == 4) ) return; // NUR mot12 oder mot34
uputs0 ("\r\tmotNR "); uputs0i ( motNR ); // Nenne Motorkennung
uputs0 ("\tSprung=> ");
uputs0 ("\tPWM/tdek ");
if ( s16mot < 0 ) s16mot = 0 - s16mot; // s16mot MUSS positiv werden

// - - - - - - - - - - - - - - - -
// Motor auswählen, einschalten und Test fahren;
// Anmerkung: die Auswahl "5"<=> beide Motoren ist für die Messung der
// Sprungantwort nicht sinnvoll wegen der Datenerfassung
switch ( motNR ) // Fallunterscheidung nach Motornummer
{ //
case 1: // Anmerkung: ASCIIcode: 45 = '-' => math neg
Mrechtsaus (); // Zur Sprungaufnahme erstmal stoppen und
wms ( 1000); // Auslauf abwarten
uputs0i(s16mot);uputs0 ("/");uputs0i(tdek1);
break; //
case 4: //
Mlinksaus (); // Zur Sprungaufnahme erstmal stoppen und
wms ( 1000); // Auslauf abwarten
while ( Izeit_1 <= 15000 ) { } // Nix tun, wenn Zeit zu kurz
uputs0i(s16mot);uputs0 ("/");uputs0u(tdek4); // #####
break; //
default:
break;
} // Ende switch ( motNR )
//uputs0 ("\t\tMot=on"); // Gefahr von Zeitverzug beim Starten, daher
// diese Meldung lieber erstmal nicht ausgeben

// - - - - - - - - - - - - - - - -
// Warten um nicht mit der Messung in einen Zeitüberlauf zu kommen
while ( Izeit_1 <= 15000 ) { } // Nix tun, wenn Zeit zu kurz, damit
// die Messung nicht in einen Time-Überlauf läuft

// Fünf Messungen IM STILLSTAND VOR DEM MOTORSTART
switch ( motNR ) // Fallunterscheidung nach Motornummer
{ //
case 1: // Anmerkung: ASCIIcode: 45 = '-' => math neg
for ( i = 0; i < 6; i++ ) //
{ //
messdat1 [ i] = Izeit_1; // Zeit = volatile s16 messdat1..3[100]
messdat2 [ i] = IencB0; // s16 - Encoderstand
// messdat3 [ i] = OCR1A; // PWM-Wert
wms ( 2); // Ungefährer Messwert-Abstand
} // Ende for ( i = 1; i < 100; i++ )
if (vz==45) Mrechtszur (); // Motortreiber 1 auf '+' math pos stellen
else Mrechtsvor (); // Motortreiber 1 auf '-' math neg stellen
OCR1A = s16mot; //
break; //
case 4: //
for ( i = 0; i < 6; i++ ) //
{ //
messdat1 [ i] = Izeit_1; // Zeit = volatile s16 messdat1..3[100]
messdat2 [ i] = IencB1; // s16 - Encoderstand
// messdat3 [ i] = OCR1B; // PWM-Wert
wms ( 2); // Ungefährer Messwert-Abstand
} // Ende for ( i = 1; i < 100; i++ )
if (vz==45) Mlinksvor (); // Motortreiber 4 auf '+' math pos stellen
else Mlinkszur (); // Motortreiber 4 auf '-' math neg stellen
OCR1B = s16mot; //
break; //
default:
break;
} // Ende switch ( motNR )
// - - - - - - - - - - - - - - - -
// ####>>>> Der Motor läuft jetzt, Daten also SOFORT beim Hochlaufen erfassen
// danach ne WEile warten !!! UND den Motor wieder ausschalten !!!
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Es werden jetzt die aktuellen Daten festgehalten für Messfahrt
// Beispiel von ..C2\D01-3_40\D01-3_40_mot_x23.c vom 15. Oktober 2009, 11:12:46
// ##>> Der folgende, aktuelle Code ist neu gestaltet
switch ( motNR ) // Fallunterscheidung nach Motornummer
{ //
case 1: // Anmerkung: ASCIIcode: 45 = '-' => math neg
for ( i = 6; i < 30; i++ ) //
{ //
messdat1 [ i] = Izeit_1; // Zeit = volatile s16 messdat1..3[100]
messdat2 [ i] = IencB0; // s16 - Encoderstand
// messdat3 [ i] = OCR1A; // PWM-Wert
wms ( 2); // Ungefährer Messwert-Abstand
} // Ende for ( i = 1; i < 100; i++ )
break; //
case 4: //
for ( i = 6; i < 30; i++ ) //
{ //
messdat1 [ i] = Izeit_1; // Zeit = volatile s16 messdat1..3[100]
messdat2 [ i] = IencB1; // s16 - Encoderstand
// messdat3 [ i] = OCR1B; // PWM-Wert
wms ( 2); // Ungefährer Messwert-Abstand
} // Ende for ( i = 1; i < 100; i++ )
break; //
default:
break;
} // Ende switch ( motNR )

switch ( motNR ) // Fallunterscheidung nach Motornummer
{ //
case 1: // Anmerkung: ASCIIcode: 45 = '-' => math neg
for ( i = 30; i < 100; i++ ) //
{ //
messdat1 [ i] = Izeit_1; // Zeit = volatile s16 messdat1..3[100]
messdat2 [ i] = IencB0; // s16 - Encoderstand
// messdat3 [ i] = OCR1A; // PWM-Wert
wms ( 4); // Ungefährer Messwert-Abstand
} // Ende for ( i = 1; i < 100; i++ )
uputs0 ("\r\tiMessg\tIzeit_0\tIencB0\tOCR1A");
break; //
case 4: //
for ( i = 30; i < 100; i++ ) //
{ //
messdat1 [ i] = Izeit_1; // Zeit = volatile s16 messdat1..3[100]
messdat2 [ i] = IencB1; // s16 - Encoderstand
// messdat3 [ i] = OCR1B; // PWM-Wert
wms ( 4); // Ungefährer Messwert-Abstand
} // Ende for ( i = 1; i < 100; i++ )
uputs0 ("\r\tiMessg\tIzeit_0\tIencB1\tOCR1B");
break; //
default:
break;
} // Ende switch ( motNR )
// - - - - - - - - - - - - - - - -
//uputs0 ("\r\tiMessg\tIzeit_0\tIencB0");
for ( i = 0; i < 100; i++ )
{ //
uputs0 ("\r\t"); uputs0i ( i );
uputs0 ("\t"); uputs0u (messdat1[i]);
uputs0 ("\t"); uputs0u (messdat2[i]);
// uputs0 ("\t"); uputs0u (messdat3[i]);
} // Ende for ( i = 0; i < 100; i++ )
wms ( 2000); // Warte Sprungantwort ab
switch ( motNR ) // Motor aus mit Fallunterscheidung nach Motornummer
{ //
case 1: //
Mrechtsaus (); // mot
break;
case 4: //
Mlinksaus (); // mot
break;
default: //
break;
} // Ende switch ( motNR )
uputs0 ("\r\tSprungantwort steht\r");
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
} // Ende void sprung ( void )
// ================================================== =========================== =

markusj
20.01.2014, 18:34
Hmm, interessant. Arbeitest du in der Ecke irgendwo mit Zeigern, von denen einer vielleicht das Ziel knapp verfehlt? Testweise könntest du noch eine weitere Variable nach tdek4 anlegen und Mal beobachten ob die auch betroffen ist.
Wie sieht die Speichersituation auf dem AVR aus? Es gibt ein kleines Tool im RN-Wissen mit dem man auf eine Heap-Stack-Kollision prüfen kann: http://www.rn-wissen.de/index.php/Speicherverbrauch_bestimmen_mit_avr-gcc

mfG
Markus

oberallgeier
20.01.2014, 19:00
... Arbeitest du ... irgendwo mit Zeigern ... Wie sieht die Speichersituation auf dem AVR aus? ...Markus - erstmal vielen Dank für Deine Mühe und Geduld.

Zweitens: ich habe drei Messdaten-Felder, das dritte ist
volatile s16 messdat3[100]; // Messwertspeicher für Tests

Wenn das "an" ist, dann gibts:
Program: 11224 bytes (34.3% Full)
(.text + .data + .bootloader)
Data: 1960 bytes (95.7% Full)
(.data + .bss + .noinit)

... und die beschriebene Datenausgabepanne.

Wenn ich dieses Feld streiche (und dessen Benutzung) dann gibts:
Program: 11080 bytes (33.8% Full)
(.text + .data + .bootloader)
Data: 1762 bytes (86.0% Full)
(.data + .bss + .noinit)

... und keine Datenausgabepanne. Nun müsste ich dem noch etwas mehr nachlaufen und schauen, ob vielleicht andere Daten noch korrumpiert sind. Mach ich im Moment nicht (faul, Zeitnot etc, Sprungfunktion läuft primstens - und ist fast erledigt) und daher

Dank nochmal und ich höre mit dem Problem und dem leichten Verdacht auf irgendeinen Stacküberlauf auf.

markusj
20.01.2014, 21:03
Dank nochmal und ich höre mit dem Problem und dem leichten Verdacht auf irgendeinen Stacküberlauf auf.
Leichter Verdacht? 1960 von 2048 Bytes belegt erklärt alles! Da küsst der Stack ziemlich sicher deine Daten. Die verbleibenden 88 Byte verknusperst du zum Frühstück mit irgendwelchen Funktionsargumenten, Stringarrays etc. (alleine im Kontext von sprung zähle ich grob überschlagen 29 Byte und ein ISR-Aufruf sichert alle genutzten Register, was auch mal 32 Bytes + Rücksprungadresse kosten kann).

Tipp: Ich meine im Hinterkopf zu haben, dass statische Strings vom Compiler als Konstanten im RAM abgelegt werden. Du könntest also schon einiges an Speicher freimachen wenn du deine ganzen Strings im Flash ablegst (PROGMEM) und nur bei Bedarf in den RAM holst.

mfG
Markus

oberallgeier
20.01.2014, 22:11
... alleine im Kontext von sprung zähle ich grob überschlagen 29 Byte und ein ISR-Aufruf sichert alle genutzten Register, was auch mal 32 Bytes + Rücksprungadresse kosten kann ...Danke - das ist mal wieder ein Abschnitt zur Programmierung, den ich ohne solche Bemerkungen (sprich ohne einen Leitfaden) garnicht angehen kann. Deshalb freue ich mich darüber - weil ich damit wenigstens ne Chance habe mich sinnvoll durchzudenken.


... im Hinterkopf ... dass statische Strings ... im RAM abgelegt werden ...Genau. Anfangs hatte ich einige im EEPROM. In meinem Servotester (https://www.roboternetz.de/community/threads/62904-Servomesstester-(Servotester-und-–messaufbau)) hatte ich festgestellt, dass merkbar lange Zugriffzeiten möglich sind, die mich dort das eine oder andere Mal wirklich gestört hatten. Deshalb sind hier die Strings (noch) im RAM - und ich war haarscharf dran, den 328er durch einen 1284er zu ersetzen - obwohl das ne neue Platine bedeutet hätte. Wenn ich über die Testphase drüber bin, dann gehen etliche Strings raus oder werden zurückgestutzt; der wesentliche Anteil davon sind Bemerkungen die für den laufenden (autonomen) Betrieb weniger bis gar keine Bedeutung haben. Da ich ausserdem das Thema PROGMEM immer noch als böhmisches Dorf vor mir herschiebe und meinem Zeitplan hinterherhinke bin ich über diese ramfressende Methode nicht hinausgekommen.

Danke für Deine Unterstützung.

sast
21.01.2014, 12:21
Hallo Joe,

das mit dem Progmem ist kinderleicht und bewahrt einen wirklich vor bösen Überraschungen. Ich hab hier mal ein kleines Beispiel, wie ich das implementiert habe um Statusmeldungen auszugeben.


int main(void)
{
unsigned char actual_page = 0;
char main_text[20];
...
get_str_from_flash(6, main_text);//"Menue 4"
tp_write_statusbar(&tp, main_text);
...


Die flash_mem.c:

#include <stdint.h>
#include <avr/pgmspace.h>
#include <string.h>

#include "main.h"
#include "flash_mem.h"

const char str1[] PROGMEM = "INFO allgemein";
const char str2[] PROGMEM = " ";//14 Leerzeichen
const char str3[] PROGMEM = "Menue 1";
const char str4[] PROGMEM = "Menue 2";
const char str5[] PROGMEM = "Menue 3";
const char str6[] PROGMEM = "Menue 4";
const char str7[] PROGMEM = "Menue 5";
...

const char *strarray1[] PROGMEM = {
str1,
str2,
str3,
str4,
str5,
str6,
str7,
str8,
str9,
str10,
str11,
str12,
...
};

void get_str_from_flash(uint16_t strnum, char* str)
{
if(strnum>0)strcpy_P(str, (const char*)(pgm_read_word(&(strarray1[strnum-1]))));
else strcpy(str, "Invalid number!");
}

und in der flash_mem.h steht nur:

#ifndef _MEM_FLASH_H_
#define _MEM_FLASH_H_

extern void get_str_from_flash(uint16_t strnum, char* str);

#endif /* _MEM_FLASH_H_ */

markusj
21.01.2014, 13:44
das mit dem Progmem ist kinderleicht und bewahrt einen wirklich vor bösen Überraschungen. Ich hab hier mal ein kleines Beispiel, wie ich das implementiert habe um Statusmeldungen auszugeben.
Leider nicht ganz, da das Timing in dem Fall sich verändert. Der EEPROM ist noch langsamer im Zugriff als der Flash, aber bei timingkritischen Anwendungen ist das eben ein relevanter Faktor. Außerdem: Warum so umständlich mit der zusätzlichen Zeigertabelle? Um von den zwei Byte des Pointers ein Byte einzusparen im Tausch gegen schlecht zu debuggende "Magic Numbers"? In dem Fall wäre es ratsam, anstatt der Zahlenwerte einen enum zu nutzen.

mfG
Markus

sast
21.01.2014, 14:47
Markus könntest du das mal bitte an einem konkreten Beispiel zeigen, was du meinst?

Danke
sast

Hubert.G
21.01.2014, 15:20
Hallo Joe
In deiner *com*.h da hast du so was definiert

volatile s16 M12ocr;
hier wirst du wohl ein int brauchen, aber bei allen anderen Variablen auch?
Mit so was habe ich mich schon einige male in ein Schlamassel gebracht und bin daher sehr vorsichtig.
Wenn nach dem compile Data mehr als 60% braucht werde ich schon hellhörig.

oberallgeier
21.01.2014, 16:37
Markus könntest du das mal bitte an einem konkreten Beispiel zeigen, was du meinst ...Ja, bitte, das würde mir auch helfen. Ich saß grad ne Weile an dem Vorschlag von sast - und bin natürlich und notwendigerweise schnell in die entsprechenden Tutorials vom RN und m-controller-net reingeschliddert. Zum Glück kam Markus - und ich hatte erstmal "Pause" gemacht - - - die Beispiele in RN und m-controller-net haben mich noch nicht zu ner funktionierenden Lösung gebracht.


... volatile s16 M12ocr ... int brauchen, aber bei allen anderen Variablen auch? ...
Hubert - mal mein "warum so" dazu.
Die MotorPWM wird aktuell mit 8 Bit gefahren - am Timer 1 (*gg* - klingt nach weiterer Vergeudung von Ressourcen).

TCCR1B |= (1<<WGM12); // Fast PWM, 8 Bit TOP=0xFF=dez255 133
TCCR1B |= (1<<CS11); // Prescaler ist clk/8 => 9,8 kHz 135
// theoretisch: 20 MHz / 8 / 255 => 9,803922 kHz, DMM-Messung 9,78 kHz
volatile s16 M12ocr; // temporärer Stellwert für OCR1A -255...255
Beabsichtigt, vermutet, ist die spätere Verwendung von mehr als 8 Bit, weil die Regelung durchgehend von -fahrt (rechtsdrehend) bis +fahrt (linksdrehend) geplant ist - und da wären mir die verbleibenden 127 Ticks zu wenig. Die Lösung mit Fallunterscheidung "vor" oder "zurück" bzw. "re" oder "li" habe ich im MiniD0 und Dottie mit den vollen 255 Ticks praktiziert - sie gefällt mir eben nicht, weil ich wegen der Hin- und Herschalterei z.B. bei Langsamfahrt so etwas - - eben unschön finde. Um mir spätere Störmöglichkeiten durch vergessene Anpassungen zu ersparen, ist die Vorgabe der Fahr-PWM auf signed 16 ausgelegt (mehrere Größen). Ich hoffe, dass mir dieses Vorhaben gelingen wird. Ich versuche schon meist das Zahlenformat klein zu halten - - genau wegen des eher begrenzten SRAM-Platzangebots.


... Wenn nach dem compile Data mehr als 60% braucht werde ich schon hellhörig ...Genau so gehts mir auch, ich wollte ja schon ne eigene Platine statt der RN-MotorControl bauen mit nem mega1284. Aber das gäbe schon wieder ne zusätzlichen Zeitaufwand ohne wirklichen Fortschritt . . . Sogar an einen 1284er-Huckepack-ersatz für den 328er hatte ich gedacht - aber nicht lange *gg*.

Hubert - ich hoffe dass damit Deine Bedenken behoben sind?

markusj
21.01.2014, 18:36
Markus könntest du das mal bitte an einem konkreten Beispiel zeigen, was du meinst?

Naja, die enum-Variante ist einfach. Im wesentlichen dein Code, nur statt uint16_t strnum ein strnum_t str. Und dann eben

typedef enum {
str_info = 0,
str_space,
str_menu1,
...
} strnum_t

// Verwendung dann so:
get_str_from_flash(str_info, str);


Damit gewinnst du etwas mehr Übersichtlichkeit, hast aber immer noch die unnötige Indirektion strarray1->*str->Flash. Der direkte Weg wäre einfach, die Strings direkt mit sprechenden Namen zu versehen und dann zu referenzieren. Damit scheidet dann auch die Möglichkeit weg, versehentlich einen nicht existierenden String zu referenzieren.

const char str_info[] PROGMEM = "INFO allgemein";
const char str_space[] PROGMEM = " ";//14 Leerzeichen
const char str_menu1[] PROGMEM = "Menue 1";

...

// Verwendung, get_str_from_flash wird überflüssig
strcpy_P(str, &str_menu1);

mfG
Markus

sast
21.01.2014, 19:01
Hallo Markus,

die erste Variante unterscheidet sich ja nicht wirklich von meiner, bis auf die Namen. Ursprünglich war bei mir auch angedacht, z.B. über eine Zählschleife einen Block aufzurufen. Da das jetzt nicht mehr so ist, gefällt mir deine Variante 2 sogar noch besser.

Danke für die Ausführung
sast

damfino
21.01.2014, 19:32
Ich habe die Strings ins Flash einfacher gehalten, mit PSTR:
zB.:
String im SRAM:
lcd_puts(" Warte GPS");

String im Flash:
#include <avr/pgmspace.h>

lcd_puts_p(PSTR(" Warte GPS "));

das "_p" zur Funktion dazuschreiben damit die Daten aus dem Flash geholt werden.

Mit der Zeigertabelle spart man etwas mehr Flash Speicher, dafür kann man hier ohne große Änderungen alles ins Flash bringen, und da man den Text direkt im Code sieht weiss man auch was man rausschickt, und muss nicht in einer Tabelle den richtigen Text suchen.

LG!

markusj
21.01.2014, 19:47
die erste Variante unterscheidet sich ja nicht wirklich von meiner, bis auf die Namen.
Habe ich auch nie behauptet ;) Die enums sollen ja nur die Wartbarkeit durch sprechende Namen verbessern.

@damfino: Danke für den Hinweis zu PSTR, damit hatte ich mich noch nicht beschäftigt.

mfG
Markus

sast
21.01.2014, 21:00
@damfino wird dann auch erkannt, wenn der gleiche String mehrmals verwendet wird, oder gibt es dann jedes mal einen neuen Eintrag?

Der Vorteil einer Tabelle liegt für mich auch darin, dass ich im nachhinein nicht alle Strings im gesamten Quelltecxt suchen muss, um eine textliche Änderung vorzunehmen. Ich muss es nur einmal ändern und sofort sind alle ensprechenden Aufrufe angepasst.

sast

damfino
22.01.2014, 14:13
Ich gehe davon aus dass es jedesmal einen neuen Eintrag gibt, aber wenn so wie hier nur 33% vom Flash belegt sind ist das kein Problem.
Wenn sich Textausgaben oft wiederholen kann man ja auch Makros oder inline-Funktionen benutzen.

Meiner Meinung nach kann man mit PSTR schnell den vorhandenen Code umschreiben, einfach mal an unwichtigen Stellen ausprobieren, etwas RAM freischaufeln, vielleicht geht es dann schon fehlerfrei. Dann kann man immer noch elegantere Lösungen einführen.

Habe selber auch Tabelle für zB Fehlermeldungen, aber kurz mal zum debuggen erweitern geht einfacher mit PSTR.

LG!