mikrocontroller.net

Forum: Compiler & IDEs Probleme mit Zugriff auf externen Speicher


Autor: Alex S. (alex_1)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo erstmal,

ich habe ein Problem mit meinem Programm und verstehe einfach nicht 
wodurch der Fehler entsteht.
Vielleicht hat ja jemand von euch erfahren damit.

Es geht um folgendes:
Mein Programm liest alle 200us einen Sensorwert von einem 16bit 
AD-Wandler über die SPI-Schnittstelle ein und speichert den Wert in 
einem Ringpuffer,welcher im externen RAM (32KB) liegt.
Das einlesen der Werte und abspeichern wird über ISR realisiert.
Die gespeicherten Werte werden über die USART1 an den PC gesendet.

Folgendes Problem tritt nun auf:
Während des Einlesen der Werte und des Füllens des Ringpuffers sind die 
meisten der über USART gesendeten Werte Schwachsinn.
Wenn der Ringpuffer voll ist wird zwar der Interrupt der SPI 
Schnittstelle noch ausgelößt, aber kein neuer Wert mehr abgespeichert. 
Ab diesem Zeitpunkt kommen dann konstant gültige Werte am PC an.
Genauso sind alle vom PC empfangenen Werte gültig/richtig wenn ich mit 
der Übertragung warte bis der Ringpuffer gefüllt wurde und kein Wert 
mehr hineingeschrieben wird.
Bin ratlos wieso das so ist...


Hier der relevante Code:
(unvollständig)

Initialisierungen:

...
#define top 0x7DC0
#define bottom 0x2000

volatile typedef struct SData 
{
   uint8_t     Sensorvalue_high;
   uint8_t     Sensorvalue_low;
   uint8_t     Zeitstempel1;
   uint8_t     Zeitstempel2;
   uint8_t     Zeitstempel3;
   uint8_t     Zeitstempel4;
}Sensordaten;

volatile Sensordaten * Schreibpointer= NULL;
volatile Sensordaten * Lesepointer= NULL;
volatile Sensordaten * Hilfspointer= NULL;
volatile Sensordaten * bottompointer= NULL;
volatile Sensordaten * toppointer= NULL;


Schreibpointer = (Sensordaten *)bottom;
Lesepointer = (Sensordaten *)bottom;
toppointer= (Sensordaten *)top;
bottompointer =(Sensordaten *)bottom;



...


ISR(SPI_STC_vect)
{
//Ruft die Schreibfunktion auf um den Wert aus dem SPI-Dataregister in den Ringpuffer zu übertragen
if (error == 0)
{
         //Einlesen des High-Byte des Sensorwerts und starten der    Übertragung des Low-Bytes          
  
  (Schreibpointer->Sensorvalue_high) = SPDR;
  SPDR=0;
        //Einlesen des Low-Byte des Sensorwerts
        //warten bis LOW Byte übertragen ist
  while ( !( SPSR & (1<<SPIF)) );
  Schreibpointer->Sensorvalue_low = SPDR;
        //Zeitstempel zu Wert abspeichern
  Schreibpointer->Zeitstempel1 = Zeit>>24;
  Schreibpointer->Zeitstempel2 = Zeit>>16;
  Schreibpointer->Zeitstempel3 = Zeit>>8;
  Schreibpointer->Zeitstempel4 = Zeit;
  //Ringpuffermanagment
        Schreibpointer++;
  Anzahl_Pufferelemente++;
  if (Schreibpointer == toppointer) Schreibpointer=bottompointer;
  
  if ((Schreibpointer == Lesepointer)&&(Anzahl_Pufferelemente != 0))
    {
    //Überlauf-Fehler: Rote LED zeigt Fehler an                    
    PORTF |= (1<<PF1);
    error = 1;
    };
};
}


...

int main(void) 
{

//ruft sie Initialisierung des uC (USART, Timer, SPI etc auf)

Haupt_init();


while (1)
{
  if (Anzahl_Pufferelemente > 0)
  {
  while ( !( UCSR1A & (1<<UDRE1)) || (PIND & (1<<PIND1)) );
  UDR1 = (Lesepointer->Sensorvalue_high);
  TCCR1B |= (1<<CS12);
  PORTF |= (1<<PF3);

  while ( !( UCSR1A & (1<<UDRE1)) || (PIND & (1<<PIND1)) );
  UDR1 = (Lesepointer->Sensorvalue_low);

  while ( !( UCSR1A & (1<<UDRE1)) || (PIND & (1<<PIND1)) );
  UDR1 = (Lesepointer->Zeitstempel1);
  
  while ( !( UCSR1A & (1<<UDRE1)) || (PIND & (1<<PIND1)) );
  UDR1 = (Lesepointer->Zeitstempel2);

  while ( !( UCSR1A & (1<<UDRE1)) || (PIND & (1<<PIND1)) );
  UDR1 = (Lesepointer->Zeitstempel3);

  while ( !( UCSR1A & (1<<UDRE1)) || (PIND & (1<<PIND1)) );
  UDR1 = (Lesepointer->Zeitstempel4);
  Lesepointer++;

  if (Lesepointer == toppointer) Lesepointer = bottompointer;
  Anzahl_Pufferelemente--;
  };
};
}

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Hier der relevante Code:
>(unvollständig)
>...

Hier die relevante Antwort:
(vollständig !!)
42

Oliver
P.S. Ein Problem könnte sein, das die Zugriffe auf 16-bit-Pointer nicht 
atomar sind, und damit ein Interrupt im falschen Moment zu ungünstigen 
Effekten führen kann. Ein paar cli()/sei() im Hauptprogramm können da 
nicht schaden.

Autor: Alex S. (alex_1)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das habe ich schon probiert.
Ich habe auch schon das versenden eines Sensorwerts inkl. Zeitstempel, 
also insgesamt 6 byte, in eine Interruptroutine gepackt.
Diese wird meines Wissens ja nicht unterbrochen...Trotzdem das gleiche 
Ergebnis.
Dann hatte ich das versenden nur eines Bytes inkl. dem nötigen Zugriff 
auf den Ringpuffer in die ISR gepackt. Auch kein Erfolg... :-(

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Lesepointer++;

  if (Lesepointer == toppointer) Lesepointer = bottompointer;
  Anzahl_Pufferelemente--;

In den drei Zeilen kann es schief gehen. Lesepointer als 16bit-Wert wird 
mit zwei Befehlen aus dem Speicher geholt (unkritisch, da Lesepointer in 
der in ISR nicht verändert wird), und nach der Veränderung in zwei 
Schritten wieder reingeschrieben (kritisch). Wenn der Interrrupt genau 
zwischen den Schreibbefehlen zuschlägt, liest die ISR Datensalat.

Wenn er Interrupt genau hinter der Aktualisierung von Lesepointer kommt, 
ist Anzahl_Pufferelemente noch nicht aktualisiert, und damit nicht 
konsistent zu den Pointern.

Also sollten die drei Zeilen zwischen cli() und sei().

Allerdings ist das alles mit Sicherheit nicht das einzige Problem, denn 
der Fehler kommt nur, wenn der Intrerrupt genau an der ungünstigen 
Stelle passiert. Mehr fällt mir aber an den paar Zeilen nicht auf (was 
nichts bedeuten muß :-)

Ausserdem erschliesst sich mir der Sinn des Programms nicht so ganz. Das 
Senden der 6 bytes per uart dauert viel länger als der Abfragezyklus des 
ADC's. Warum liest du den nicht nur so oft aus, wie du die Werte auch 
verarbeiten kannst? Sobald der Ringpuffer einmal voll ist, passiert ja 
genau das. Da kannst du doch geleich alles in die Schleife des 
Hauptprogramms packen, ganz ohne Ringpuffer.


Oliver

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.