Forum: Compiler & IDEs Probleme mit Zugriff auf externen Speicher


von Alex S. (alex_1)


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:

...
1
#define top 0x7DC0
2
#define bottom 0x2000
3
4
volatile typedef struct SData 
5
{
6
   uint8_t     Sensorvalue_high;
7
   uint8_t     Sensorvalue_low;
8
   uint8_t     Zeitstempel1;
9
   uint8_t     Zeitstempel2;
10
   uint8_t     Zeitstempel3;
11
   uint8_t     Zeitstempel4;
12
}Sensordaten;
13
14
volatile Sensordaten * Schreibpointer= NULL;
15
volatile Sensordaten * Lesepointer= NULL;
16
volatile Sensordaten * Hilfspointer= NULL;
17
volatile Sensordaten * bottompointer= NULL;
18
volatile Sensordaten * toppointer= NULL;
19
20
21
Schreibpointer = (Sensordaten *)bottom;
22
Lesepointer = (Sensordaten *)bottom;
23
toppointer= (Sensordaten *)top;
24
bottompointer =(Sensordaten *)bottom;



...


1
ISR(SPI_STC_vect)
2
{
3
//Ruft die Schreibfunktion auf um den Wert aus dem SPI-Dataregister in den Ringpuffer zu übertragen
4
if (error == 0)
5
{
6
         //Einlesen des High-Byte des Sensorwerts und starten der    Übertragung des Low-Bytes          
7
  
8
  (Schreibpointer->Sensorvalue_high) = SPDR;
9
  SPDR=0;
10
        //Einlesen des Low-Byte des Sensorwerts
11
        //warten bis LOW Byte übertragen ist
12
  while ( !( SPSR & (1<<SPIF)) );
13
  Schreibpointer->Sensorvalue_low = SPDR;
14
        //Zeitstempel zu Wert abspeichern
15
  Schreibpointer->Zeitstempel1 = Zeit>>24;
16
  Schreibpointer->Zeitstempel2 = Zeit>>16;
17
  Schreibpointer->Zeitstempel3 = Zeit>>8;
18
  Schreibpointer->Zeitstempel4 = Zeit;
19
  //Ringpuffermanagment
20
        Schreibpointer++;
21
  Anzahl_Pufferelemente++;
22
  if (Schreibpointer == toppointer) Schreibpointer=bottompointer;
23
  
24
  if ((Schreibpointer == Lesepointer)&&(Anzahl_Pufferelemente != 0))
25
    {
26
    //Überlauf-Fehler: Rote LED zeigt Fehler an                    
27
    PORTF |= (1<<PF1);
28
    error = 1;
29
    };
30
};
31
}


...

1
int main(void) 
2
{
3
4
//ruft sie Initialisierung des uC (USART, Timer, SPI etc auf)
5
6
Haupt_init();
7
8
9
while (1)
10
{
11
  if (Anzahl_Pufferelemente > 0)
12
  {
13
  while ( !( UCSR1A & (1<<UDRE1)) || (PIND & (1<<PIND1)) );
14
  UDR1 = (Lesepointer->Sensorvalue_high);
15
  TCCR1B |= (1<<CS12);
16
  PORTF |= (1<<PF3);
17
18
  while ( !( UCSR1A & (1<<UDRE1)) || (PIND & (1<<PIND1)) );
19
  UDR1 = (Lesepointer->Sensorvalue_low);
20
21
  while ( !( UCSR1A & (1<<UDRE1)) || (PIND & (1<<PIND1)) );
22
  UDR1 = (Lesepointer->Zeitstempel1);
23
  
24
  while ( !( UCSR1A & (1<<UDRE1)) || (PIND & (1<<PIND1)) );
25
  UDR1 = (Lesepointer->Zeitstempel2);
26
27
  while ( !( UCSR1A & (1<<UDRE1)) || (PIND & (1<<PIND1)) );
28
  UDR1 = (Lesepointer->Zeitstempel3);
29
30
  while ( !( UCSR1A & (1<<UDRE1)) || (PIND & (1<<PIND1)) );
31
  UDR1 = (Lesepointer->Zeitstempel4);
32
  Lesepointer++;
33
34
  if (Lesepointer == toppointer) Lesepointer = bottompointer;
35
  Anzahl_Pufferelemente--;
36
  };
37
};
38
}

von Oliver (Gast)


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.

von Alex S. (alex_1)


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... :-(

von Oliver (Gast)


Lesenswert?

1
Lesepointer++;
2
3
  if (Lesepointer == toppointer) Lesepointer = bottompointer;
4
  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

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.