PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Semaphoren Lock für Variablenzugriff



Ritchie
14.05.2012, 20:33
Hallo Zusammen,

mein Programm greift via Interrupt auf interne Variable des Atmel atMega32 zu. Hierbei handelt es sich um die Datentypen, byte, word, long und float.

Problemlos ist eigentlich nur die Bytevariable, da diese im Schreibzugriff nicht unterbrochen werden kann.

Gibt es eine einfach Methode einen sicheren Zugriff auf Variablen innerhalb einer Interrupt-Routine sicherzustellen.

Generell habe ich alle Variablen, welche in der Interruptroutine abfrage, als "volatile" deklariert.

Nur was passiert, wenn das Hauptprogramm gerade das erste Byte Long einer Variable geschrieben hat,
und dann ein Interrupt eintritt.

Unter Linux kann ich hierbei auf Semaphoren zugreifen, gibt es sowas auf für atmega32 ?

Gruss R.

markusj
14.05.2012, 21:03
Du musst bei Zugriffen, die atomar erfolgen sollen, Interrupts kurz ausschalten. Die avr-libc bringt bereits in util/atomic.h entsprechendes Werkzeug mit.

Codebeispiel:

#include <util/atomic.h>

ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
// Tu was atomar
}


mfG
Markus

PS: Du kannst natürlich auch einfach selbst vor dem zu schützenden Bereich mit cli() Interrupts deaktivieren und danach mit sei() wieder Anschalten.
PPS: Beide Varianten müssen im Normalfall nur im Hauptprogramm eingesetzt werden, ISRs sind ohne eigenes Zutun nicht unterbrechbar und benötigen damit einen solchen Schutz nicht.

Klebwax
15.05.2012, 05:54
PS: Du kannst natürlich auch einfach selbst vor dem zu schützenden Bereich mit cli() Interrupts deaktivieren und danach mit sei() wieder Anschalten.


Besser ist es, sich den Interruptstatus zu merken, dann ihn zu deaktivieren und am Ende den alten Zustand wiederherzustellen. Das kann dann gefahrlos auch verwendet werden, wenn mal aus anderen Gründen der Interrupt gesperrt ist.

Ich kenne jetzt die avr-libc nicht, aber der Name "ATOMIC_RESTORESTATE" klingt, alsob es da so gemacht wird.

MfG Klebwax

Ritchie
15.05.2012, 08:13
Hi,
hat jemand schon mal eine Funktion geschrieben, welche einen Variablenwert "Atomic" ändert ?

Hier würde ich dann auch das ab/an Schalten des Interrupts einfügen. Wild cli() / sei() setzen ist nicht mein Ding.

markusj
15.05.2012, 14:29
Besser ist es, sich den Interruptstatus zu merken, dann ihn zu deaktivieren und am Ende den alten Zustand wiederherzustellen. Das kann dann gefahrlos auch verwendet werden, wenn mal aus anderen Gründen der Interrupt gesperrt ist.
Das erfordert mehr Aufwand, den man in manchen Situationen nicht bereit ist zu leisten.


Ich kenne jetzt die avr-libc nicht, aber der Name "ATOMIC_RESTORESTATE" klingt, alsob es da so gemacht wird.
Genau so ist es.

@Ritchie: Warum? Dafür braucht es keine Funktion.


// tu was und berechne foo
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
bar = foo; // atomare Zuweisung
}

Klebwax
15.05.2012, 16:56
Das erfordert mehr Aufwand, den man in manchen Situationen nicht bereit ist zu leisten.


Naja, so aus der Erinnerung : Das Register mit dem Globalen Enable mit PUSH auf den Stack, disable Interrupt, .... POP Register ( drei Zeilen statt zwei) und schon hat man Stunden beim Debuggen gespart.

MfG Klebwax

Ritchie
15.05.2012, 22:18
Hi,
das sieht ja doch ganz gut aus.

Ich habe jetzt nur evtl. ein Problem mit meiner Impulsmessung der Encoder. Dieser werden auch per Interrupt eingelesen. Schalte ich hier die Interrupts entsprechend ab, laufe ich Gefahr, das ich Impuls verliere (was ich noch testen müsste).

Wozu ich die ganze Sache benötige?
Ich habe einen Hauptrechner, welcher Parameter an die Motorsteuerung sendet. Dies sind z.b. aktuelle Position in der Karte, Zielposition, Ausrichtung, Geschwindigkeit, etc...

Gleichzeitig werden Werte aus der Motorsteuerung zurück an den Hauptrechner gemeldet, wie aktuelle Position, aktuelle Ausrichtung, Status, etc...

Positionen sind im Integer Format (2Byte) und Winkel in float (4byte) abgelegt.

Wenn die Motorsteuerung einen Wert aktualisiert, kann es sein, das gleichzeitig, das Hauptprogramm den gleichen Wert abfragt.
Ich habe zeitweise -30491 als Wert im Logfile, statt 2277, obwohl ich diese Abfrage im Standby mache (also Stillstand!).

Ich dachte bin jetzt immer, das hier ein Übertragungsfehler vorliegt, dieser wird aber nicht vom Hauptprogramm gemeldet.
Der falsche Wert muss also auch im Kontroller so vorhanden sein.
Wenn dies zu einem falschen Zeitpunkt passiert (Übernahme von Parameter), kommt das garnicht gut.

Gruss R.

RoboHolIC
16.05.2012, 00:10
Vielleicht kannst du die konkurrierenden nicht-atomaren Zugriffe auf die Mehrbyte-Variablen in Schattenvariablen puffern und den Austausch mit den "heissen" Variablen in einen kürzest-möglichen Programmabschnitt zusammenlegen. Die ISR setzt bei jedem Durchlauf ein Freigabeflag ("ISR was here"), auf das in der Hauptprogrammschleife gewartet wird. Man könnte dann sozusagen im zeitlichen Windschatten der ISR den Datenaustausch flink vollziehen, sobald die wieder einmal durch ist und für eine bestimmte minimale Zeitdauer garantiert nicht in die Quere kommen kann. Vor dem nächsten kritischen Zugriff wird das Flag vom Hauptprogramm gelöscht und wieder darauf gepollt, bis dieses "von Geisterhand" gesetzt wird ...
Erweiterungsmöglichkeit: Die ISR akzeptiert ihr eigenes "ISR was here"-Flag als Zugriffsverbot und überspringt ggf. den kritischen Abschnitt. Das entspräche in etwa einem acknowledge oder hand shake.

Gruß
Christian.

Ritchie
16.05.2012, 08:09
Hi,
generelle scheint mir Deine Idee ein gangbarer Weg zu sein, da nur hier die volle Kontrolle der Daten vorhanden ist. Schalte ich den Interrupt up, weiss ich nicht, welche Daten ich verliere, da auch die Messwertverarbeitung via Interrupt arbeitet.
Dies entspricht auch mehr der Vorgehensweise eines "Semaphore-Lock".
Gruss R.

sternst
16.05.2012, 11:19
Schalte ich den Interrupt up, weiss ich nicht, welche Daten ich verliere, da auch die Messwertverarbeitung via Interrupt arbeitet.Wieso glaubst du überhaupt, irgendetwas zu verlieren, wenn du Interrupts kurz ausschaltest? Welcher Interrupt soll es denn sein, der da während einer Handvoll Takte gleich mehrmals kommen soll?

Ritchie
16.05.2012, 11:48
Hi,

ich habe Impluse über Encoder (links/rechts) 255 pro Umdrehung, Analogwert Umwandung fertig, Timerüberlauf für Zeitmessung und I2C Bus Kommunikation.

Das ganze könnte wohl relativ werden, wenn die Interrupt-Anforderung stehen bleibt und sofort ausgeführt wird,
wenn Interrupts wieder aktiv sind. (Ist das so ?). Da ich derzeit noch 16Mhz verwende, sollte der Zeitunterschied minimal sein.

Würde eine Anforderung verschluckt werden, ist dies eindeutig ein "no go".


Gruss R.

sternst
16.05.2012, 12:04
Das ganze könnte wohl relativ werden, wenn die Interrupt-Anforderung stehen bleibt und sofort ausgeführt wird,
wenn Interrupts wieder aktiv sind. (Ist das so ?). ...
Würde eine Anforderung verschluckt werden, ist dies eindeutig ein "no go".Wenn Interrupts für deine Anwendung eine so große Rolle spielen, meinst du dann nicht, es wäre mal angebracht sich das Datenblatt zu diesem Thema durchzulesen, um zu verstehen, wie Interrupts auf deinem Controller im Detail funktionieren?

Ritchie
16.05.2012, 16:35
Da hast Du wohl recht.
Ich kam jedoch bis jetzt nicht dazu, mich näher mit dem Problem zu beschäftigen. Da ich durch weitere Fragen/Antworten erst tiefer auf
die Problematik gestossen bin. Auch will ich erst noch testen, was mein Programm macht, wenn ich das Hauptprogramm abschalte und die Variablen mit festen Werten belege. In diesem Fall darf sich in meinem Log-File nichts ändern. Ist der Fehler dann immer noch da, liegt noch wo anders ein Problem. Die Problematik mit dem Interrupt würde dann erst noch kommen.

Das Datenblatt, etwas über 340 Seite habe ich am Anfang der Amtel-Zeiten in den wichtigsten Punkten gelesen. Ebenso das Tutorium.

Nur vergisst man(n) das eine oder andere wieder, wenn es nicht aktuell ist. Für Interrupts werde ich das heute abend nachholen tun. ;)

Gruss R.

P.S.: Ach könnte ich mich doch nur noch mit meinem Roby beschäftigen, was wäre das schön. Wäre da nicht alles mit dem Laut "au", welche mir immer Sorgen machen.

Ritchie
17.05.2012, 10:58
Es scheint erstmal ein Problem des Embeeded Linux Kernel zu sein,
da dieser Fehler auch bei abgeschalteten Hauptprogramm auftritt.

Nach einer Erweiterung des Logfiles um die Zugriffsdaten auf den Kompass konnte ich änliche Effekt feststellen.
Ich werde also erstmal den Linux Kernel aktualisieren, wenn der Hersteller mir ein Problem im Kernel bestätigt,
bevor ich hier weiter schaue. ;)

Auch habe ich begonnen, nochmals die gesammte Verkabelung des I2C Bus jetzt mit Cat5e Kabel durchzuführen. Hier werde ich den Schirm jeweils auf beiden Seite auf GND legen. Auch werden hierbei alle Adern (verdrillt) verwendet, was eine Verstärkung der Drahtquerschnitts bedeutet. Ich hoffe hier Störungen durch Funk oder magnetischer Natur weiter zu drosseln, wenn vorhanden.

Übrigens, der Fehler tritt alle 2..3 tausend Kommunikation einmalig auf, ohne einen Fehlercode von der Funktion zurückzumelden.

Gruss R.