So, das Programm ist trotz deutlich erweiterter Funktionalität auf ca. die Hälfte der Original-Größe geschrumpft (von 2032 auf 1088 Bytes)! Und vor allem läuft es jetzt fehlerfrei!

realloc() ist einem statischen Array gewichen, was den Löwenanteil der Optimierung ausmacht. Ansonsten habe ich in der Hinsicht nur kleinere Verbesserungen vorgenommen (Variablen).

Mit einem Taster kann ich mir nun den restlichen Speicher (SRAM) anzeigen lassen.

Die wohl größte Veränderung ist aber beim Beschreiben des EEPROM zu finden. Da dieser Speicher nur ca. 100000 mal beschreibbar ist, habe ich zwei Funktionen geschrieben, die ihn fortlaufend (immer im Kreis) beschreiben. Den Tipp hat mir ein arbeitskollege gegeben. So wird das EEPROM geschont und der AVR hält länger. Beim Testen vielleicht egal, im produktiven Betrieb aber doch schon von großem Vorteil.

Da ich mir vorstellen kann, dass es noch andere AVR-Anfänger wie mich gibt, denen das Programm vielleicht eine Hilfe sein kann, poste ich hier nochmal den aktuellen Code:

Code:
/*  T E S T P R O G R A M M  (USART+EEPROM)
 *  ---------------------------------------
 *  
 *  Dieses Testprogramm (geschrieben für ATTiny2313) empfängt und sendet Zeichen über die
 *  RS232-Schnittstelle (UART/USART) und ermöglicht die folgenden Funktionen:
 *
 *  SW0 (Taster 0, bei mir an PIND2): empfangene Zeichen im EEPROM speichern
 *
 *  SW1 (Taster 1, bei mir an PIND3): gespeicherte Zeichen wieder auslesen und ans Terminal senden
 *
 *  SW2 (Taster 2, bei mir an PIND4): Anzahl aktuell eingelesener Zeichen ans Terminal senden
 *
 *  SW3 (Taster 3, bei mir an PIND5): freien Speicher (SRAM) ans Terminal senden
 *
 *  Zum Testen dieses Programm benötigt man ein Terminal-Programm (ich nutze HTerm), mit dem man
 *  Daten zum AVR schicken und von ihm empfangen kann.
 *
 *  Wenn das Terminal-Programm verbunden ist, sieht man, dass kontinuierlich ein Punkt ('.') gesendet
 *  wird. Dies ist als Lebenszeichen des AVR zu verstehen. Bricht der Datenstrom plötzlich ab, ist
 *  das Programm wahrscheinlich hängengeblieben (z.B. wegen Speicherüberlauf o.ä.).
 *
 *  Da das SRAM in seiner Größe sehr beschränkt ist (beim ATTiny2313 nur 128 Bytes), habe ich eine
 *  maximale Größe für den empfangenen String festgelegt. Sie kann über IUSART_INP_MAX geändert werden.
 *
 *  Das Beschreiben des EEPROM ist in der Hinsicht kritisch, als dass der Speicher nur
 *  ca. 100000 mal beschreibbar ist. Deshalb ist es nicht sinnvoll, jedesmal wieder ab
 *  Byte 0 zu schreiben. Deshalb habe ich die Funktionen eeprom_get_last_byte() und
 *  eeprom_get_first_written_byte() geschrieben. Mithilfe dieser Funktionen kann man so
 *  navigieren, dass das EEPROM kontinuierlich beschrieben wird, so dass alle Bytes
 *  gleichmäßig beansprucht werden und die Lebensdauer deutlich erhöht wird. Erreicht man
 *  das Ende des EEPROM (beim ATTiny2313 128 Bytes, änderbar über EEPROM_MAX_BYTE), so
 *  beginnt man wieder bei Byte 0, wobei ein String auch überlappen kann (z.B. ein
 *  5-Zeichen-String in den Bytes 126, 127, 0, 1 und 2).
 *
 *  Bei meinem Testboard (STK500) habe ich die Taster nicht nach Standardkonfiguration angeschlossen,
 *  wie oben an der Funktionsübersicht zu sehen ist (SW0 auf PIND2 statt PIND0 usw.). Wer ganz normal
 *  SW0 an PIND0 hat, muss lediglich diese wenigen Stellen im Code abändern.
 *
 *  Beispiel:
 *  ---------
 *    
 *  if( !(PIND & (1<<PIND3)) && iUSART_inp>0 && cBoolSaved) {    //SW1 gedrückt
 *                       -
 *  ändern in
 *
 *  if( !(PIND & (1<<PIND1)) && iUSART_inp>0 && cBoolSaved) {    //SW1 gedrückt
 *                       -
 *
 *  Tipp: Wer HTerm benutzt, sollte in der oberen Ansicht (vom AVR gesendete Zeichen) nur die ASCII-
 *        Ausgabe zulassen (also Häkchen weg bei Hex, Dec und Bin). So behält man den Überblick.
 *
 *
 *  Wer (wie ich gerade) in die AVR-Programmierung einsteigt und an der Kommunikation zwischen AVR
 *  und PC sowie am Beschreiben des EEPROM interessiert ist, wird vielleicht (hoffentlich) mithilfe
 *  dieses Programms weiterkommen.
 *
 *
 *  Matthias Marschhausen (2007-07-20)
 *
 */
#ifndef F_CPU
	#define F_CPU 4000000
#endif
#ifndef UART_BAUD_RATE
	#define UART_BAUD_RATE 9600
#endif
//
#define IUSART_INP_MAX 20   //maximale Anzahl an Zeichen, die vom AVR empfangen werden darf
#define EEPROM_MAX_BYTE 127 //letztes Byte des EEPROM (beim ATTiny2313 128 Byte großer Speicher)
//
extern unsigned char __heap_start;
//
#include <avr/io.h>
#include <stdlib.h>
#include <avr/eeprom.h>
#include <util/delay.h>
/*
#include <avr/interrupt.h>
#include <stdio.h>
#include <stdint.h>
*/
//
//PROTOTYPEN
//----------
          void USART_init(unsigned int baud);
          void USART_transmit(unsigned char cData);
          void USART_transmit_str(char *cStr);
          void long_delay_ms( unsigned long ms );
          void clearArray(char cArray[],int iMax);
          void __attribute__ ((naked, section (".init8"))) __init8_mem (void);
       uint8_t eeprom_get_next_byte(uint8_t iLastByte);
       uint8_t eeprom_get_first_written_byte(uint8_t iLastByte,uint8_t iWrittenBytes);

//GLOBALE VARIABLEN
//-----------------
uint8_t i = 0;
uint8_t iEEPROM_lastByte = 0;


/***************/
int main(void) {
/***************/
                char cUSART_inp[IUSART_INP_MAX];  //Array für das Zwischenspeichern von über USART empfangenen Zeichen
      unsigned short iUSART_inp = 0;  //Index für das Array cUSART_inp[], entspricht der Anzahl der empfangenen Zeichen
      unsigned short iDot = 0;  //zum ständigen Senden eines Punktes (als Lebenszeichen)
                char cTmpZahl[5] = {0};  //Temp-Variable für Konvertierungen mit itoa()
       unsigned char cBoolSaved = 0;  //wurden die empfangenen Zeichen im EEPROM gespeichert?
	//
	DDRB = 0xff;	//Port B Pins als Ausgänge definieren
	PORTB = 0xff;
	//DDRD = 0x00;	//Port D Pins als Eingänge definieren. Wohl nicht notwendig, da die Bits standardmäßig sowieso 0 sind
	//
	USART_init(UART_BAUD_RATE);   //USART initialisieren
  //
	for(;;) {   //Endlosschleife (bei AVR-Programmierung gewollt/nötig)
    if( ++iDot % 1000 == 0 ) { //alle 1000 Durchläufe einen Punkt ans Terminal senden
      USART_transmit('.');  //Punkt senden
      iDot = 0;  //iTest natürlich zurücksetzen, um einen Überlauf zu verhindern
    }
    if( iUSART_inp > IUSART_INP_MAX ) {  //string wäre zu lang, also alles zurücksetzen
      USART_transmit_str(" WARNUNG: >");                     //Warnung/Information ans
      USART_transmit_str(itoa(IUSART_INP_MAX,cTmpZahl,10));  //Terminal senden
      USART_transmit_str(" Zeichen - RESET ");            //
      clearArray(cUSART_inp,IUSART_INP_MAX);  //Array zurücksetzen
      iUSART_inp = 0;   //Array-Index zurücksetzen
      long_delay_ms(500);   //kurze Pause...
    }
    //
		if( UCSRA & (1<<RXC) ) {	//Zeichen werden empfangen und im char-array gespeichert
      //USART_transmit(UDR);  //sendet jedes empfangene Zeichen direkt wieder and Terminal
                              //(kann zum grundsätzlichen Testen der Kommunikation aktiviert werden)
      cUSART_inp[++iUSART_inp-1] = UDR; //empfangenes Zeichen im Array speichern
      cBoolSaved = 0;   //"gespeichert"-Status auf 0 (false) setzen
		}
    //
    if( !(PIND & (1<<PIND2)) && iUSART_inp>0) {    //SW0 gedrückt
      for(i=0;i<iUSART_inp;i++) {
        //char-array (cUSART_inp) in EEPROM speichern
        eeprom_write_byte((uint8_t*)(unsigned int)eeprom_get_next_byte(iEEPROM_lastByte),cUSART_inp[i]);
      }
      //
      clearArray(cUSART_inp,IUSART_INP_MAX);  //Array zurücksetzen
      //
      USART_transmit_str(" SAVED ");  //damit man was sieht...
      cBoolSaved = 1;   //"gespeichert"-Status auf 1 (true) setzen
      long_delay_ms(500);   //kurze Pause...
    }
    //
    if( !(PIND & (1<<PIND3)) && iUSART_inp>0 && cBoolSaved) {    //SW1 gedrückt
      //
      for(i=0;i<iUSART_inp;i++) {
        //Inhalt des EEPROM rurück in das char-array schreiben
        cUSART_inp[i] = eeprom_read_byte((uint8_t*)(unsigned int)eeprom_get_first_written_byte(iEEPROM_lastByte,iUSART_inp-1-i));
      }
      //
      USART_transmit_str(cUSART_inp);  //Array ans Terminal senden
      //
      iUSART_inp = 0; //Array-Index zurücksetzen
      cBoolSaved = 0; //"gespeichert"-Status auf 0 (false) setzen
      long_delay_ms(500);   //kurze Pause...
    }
    if( !(PIND & (1<<PIND4)) ) {  //SW2 gedrückt: iUSART_inp (Anzahl eingegebener Zeichen) ausgeben
      USART_transmit_str(" Anzahl Zeichen: ");                //ans Terminal
      USART_transmit_str(itoa(iUSART_inp,cTmpZahl,10));   //senden
      USART_transmit(32);                                 //
      long_delay_ms(500);   //kurze Pause...
    }
    if( !(PIND & (1<<PIND5)) ) {          //SW3 gedrückt: freien Speicher (SRAM) ermitteln
      USART_transmit_str(" FREE MEM: ");                //und ans Terminal senden
      USART_transmit_str(itoa(SP - (uint16_t) &__heap_start,cTmpZahl,10));
      USART_transmit_str(" Bytes ");
      long_delay_ms(500);   //kurze Pause...
    }
	}
	//
	return(0);  //wird nie erreicht, da Endlosschleife...
}

/***************/
void long_delay_ms( volatile unsigned long ms ) {
/***************/
  while( ms-- )
    _delay_ms( 1 );
}

/***************/
void USART_init(unsigned int baud) {    //nochmal genau ansehen und schöner formulieren!
/***************/
/*
UBRRH = (unsigned char)(baud>>8);		//Baudrate setzen
UBRRL = (unsigned char)baud;				//
//
UCSRB = (1<<RXEN)|(1<<TXEN);		//Receiver und Transmitter aktivieren
//
UCSRC = (1<<USBS)|(3<<UCSZ0);	//8 Datenbits, 2 Stopbits (?)
*/
UCSRA=0x00;
//UCSRB=0x08;
UCSRC=0x86;
UBRRH=0x00;
UBRRL=0x17;
//
UCSRB = (1<<RXEN)|(1<<TXEN);		//Receiver und Transmitter aktivieren
}

/***************/
void USART_transmit(unsigned char cData) {  //sendet ein Zeichen ans Terminal
/***************/
	while( !(UCSRA & (1<<UDRE)) ) {
		;//warten, bis der transmit buffer leer ist, so dass wieder übertragen werden kann
	}
	UDR = cData;
}

/***************/
void USART_transmit_str(char *cStr) {   //sendet einen String (char-Array) ans Terminal
/***************/
  while(*cStr) {
    USART_transmit(*cStr++);
  }
}

/***************/
void clearArray(char cArray[],int iMax) { //ersetzt alle Werte in einem Array durch binäre Nullen ('\0')
/***************/
  for(i=0;i<iMax;i++) {
    cArray[i]='\0';
  }
}

/***************/
uint8_t eeprom_get_next_byte(uint8_t iLastByte) {
/***************/
  uint8_t iNextByte = iLastByte + 1;
  //
  if(iNextByte > EEPROM_MAX_BYTE) {
    iNextByte = 0;
  }
  //
  iEEPROM_lastByte = iNextByte;
  return(iNextByte);
}

/***************/
uint8_t eeprom_get_first_written_byte(uint8_t iLastByte,uint8_t iWrittenBytes) {
/***************/
  uint8_t iFirstByte;
  if( iLastByte - iWrittenBytes < 0 ) {
    iFirstByte = EEPROM_MAX_BYTE + iLastByte - iWrittenBytes;
  } else {
    iFirstByte = iLastByte - iWrittenBytes;
  }
  return(iFirstByte);
}
Danke nochmal an alle, die mir mit ihren Tipps geholfen haben!

Weitere Anregungen zur speicheroptimierten Programmierung sind erwünscht!

Gruß Matze