Hi zusammen
Kurz und einfach gesagt: Ich habe hier ein ganz seltsames Verhalten beim
Debuggen eines SPI Treibers aud FreeRTOS basierend.
Wichtigster Code-Ausschnitt (vollständige Datei im Anhang):
```c
static volatile SemaphoreHandle_t spiMutex = NULL;
static volatile StaticSemaphore_t spiMutexStatic;
static inline error_t spi_mutexTake()
{
if (spiMutex == NULL) // <- Break-Point 1
spiMutex =
xSemaphoreCreateMutexStatic((StaticSemaphore_t*)&spiMutexStatic);
if (xSemaphoreTake(spiMutex, SPI_CONFIG_PORT_DELAY) != pdTRUE) // <-
Break-Point 2
return SPI_ERROR_MUTEX_TAKE_FAILED;
else
return SPI_ERROR_SUCCESS;
}
static inline error_t spi_mutexGive()
{
if (xSemaphoreGive(spiMutex) != pdTRUE)
return SPI_ERROR_MUTEX_GIVE_FAILED;
else
return SPI_ERROR_SUCCESS;
}
error_t spi_run(/* params */)
{
ON_ERROR_RETURN(spi_mutexTake());
spi_doStuff(); // ...
ON_ERROR_RETURN(spi_mutexGive());
}
```
Ich habe nun 2 tasks laufen. Task 1 greift als erstes auf den SPI
Treiber zu. Er sendet und empfängt wenige bytes indem zwei mal
nacheinander `spi_run()` aufgerufen wird. Hier läuft alles einwandfrei,
beim ersten aufruf von `spi_mutexTake()` wird das statische Semaphore
initialisiert und `spiMutex` enthält ab Break-Point 2 die Adresse
`0x02f0`.
Nun wird zu Task 2 gewechselt, welcher ebenfalls `spi_run()` aufruft.
Doch hier geschiet was ganz interessantes. `spi_mutexTake()` wird
aufgerufen und ist zu Break-Point 1 noch adresse `0x02f0` (die Bedingung
`if (spiMutex == NULL)` müsste also false sein), doch ab Break-Point 2
zeigt dieser ins völlige Nirwana von `0x2001` ausserhalb des Speichers
und nach dem Aufruf von `spi_mutexGive()` zeigt spiMutex sogar auf
`NULL` (hier wird aber glücklicherweise auch
`SPI_ERROR_MUTEX_GIVE_FAILED` zurückgegeben).
Ich habe leider wenig Ahnung, was der Compiler alles hier versucht zu
(über-)optimieren, doch beim besten Willen kann ich mir nicht erklären,
was da geschiet.
Ausserhalb von `spi_MutexTake()` findet nie eine Zuweisung für spiMutex
statt. Darum habe ich auch keinen Schimmer, wieso sich dessen Wert auf
einmal ändert.
Falls jemand einen Tipp hat, gerne her damit...
Hab keine Ahnung von FreeRTOS, aber wo ist die Mutex, die den Zugriff auf spiMutex schützt? Die volatiles bringen nichts. Sieh lieber zu, dass spiMutex initialisiert wird, bevor du die Tasks startest.
foobar schrieb: > Hab keine Ahnung von FreeRTOS, aber wo ist die Mutex, die den Zugriff > auf spiMutex schützt? Die volatiles bringen nichts. Die volatiles sind nur dazu da damit ich das zeug anständig debuggen kann. Im Anhang siehst du das ursprünglich `taskENTER_CRITICAL()` dabei ist im Makro `_SPI_MUTEX_TAKE(m,s,e)`. Das wäre der Schutz vor dem Zugriff. foobar schrieb: > Sieh lieber zu, dass spiMutex initialisiert wird, bevor du die Tasks > startest. Hat bisher eigentlich immer ganz gut so geklappt, aber in der aktuellen Situation tatsächlich gar nicht verkehrt. Probier ich später gleich aus, danke!
> taskENTER_CRITICAL ... Das wäre der Schutz vor dem Zugriff.
Hmmm... ich denke, das reicht nicht. Laut Doku sperrt das nur
Interrupts und dadurch das preemptive Scheduling, nicht das explizite.
Bei SMP reicht das eh nicht, da braucht man Spinlocks (keine Ahnung, ob
die dann in taskXX_Critical drin sind, Doku äußert sich nicht dazu).
Insb steht da auch noch: "FreeRTOS API functions must not be called from
within a critical section." ...
foobar schrieb: > Insb steht da auch noch: "FreeRTOS API functions must not be called from > within a critical section." ... Stimmt, weil das nämlich in den APIs selbst aufgerufen wird (ist mir gerade bei genaueren debuggen aufgefallen). Die API functions sind also bereits selbst thread-safe. (Erklärt auch warum in den Docs in keinem Beispiel irgend ein mechanismus dazu verwendet wird) Wenn das preemptive Scheduling nämlich deaktiviert ist und die API Funktion selbst keinen Taskwechsel erzeugt, dann muss der aktuelle Task zwangsweise die Funktion vollständig auführen ohne eine Gefahr... Den Fehler selbst habe ich derweil gefunden. Man sollte nicht zu sparsam sein bei der Stackzuweisung, auch wenn dieser AVR nicht gerade der speicherreichste ist. Der Stackoverflow hat hat perfekt den angrenzenden Speicherbereich überschrieben, in dem gerade der Semaphore-Pointer versorgt war. Mein Fehler... @foobar danke dir trotzdem: Die enter und exit critical sections hab ich jetzt raus genommen. Sie sind nicht unbedingt gefährlich, aber nach der Überprüfung zumindest tatsächlich überflüssig an dieser Stelle. Gefährlich wird es erst wenn man die critical section tatsächlich benötigt, denn wenn man darin eine API aufruft, dann hebt diese API die critical section auf (deswegen auch "FreeRTOS API functions must not be called from within a critical section.").
Wie gesagt, hab bisher nichts mit FreeRTOS gemacht, aber: > Gefährlich wird es erst wenn man die critical section tatsächlich > benötigt, denn wenn man darin eine API aufruft, dann hebt diese API > die critical section auf Laut der Doku sind Critical Sections (CS) verschachtelbar und damit heben sie die äußere CS nicht auf. Das Verbot dürfte andere Gründe haben, z.B. dass innerhalb einer CS keine blockierenden Funktionen aufgerufen werden dürfen.
foobar schrieb: > Laut der Doku sind Critical Sections (CS) verschachtelbar und damit > heben sie die äußere CS nicht auf. Das Verbot dürfte andere Gründe > haben, z.B. dass innerhalb einer CS keine blockierenden Funktionen > aufgerufen werden dürfen. Ups, da hast du recht... Habs gerade selbst durchgelesen. Dann wird es ganz sicher daran liegen, danke dir!
Typisch implementiert man Interfaces als Stream. Benötigt eine Task ein Interface, wird es mit fopen belegt und nach Ende mit fclose wieder freigegeben. Eine andere Möglichkeit ist, keine Task darf das Interface direkt benutzen. Es werden entsprechend viele Puffer angelegt, die ein gemeinsamer Interrupthandler der Reihe nach abarbeitet. Jede Task schreibt und liest dann ihren Puffer.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.