Forum: Mikrocontroller und Digitale Elektronik AVR: Funktion wird ab und zu nicht ausgeführt


von Stefan H. (shaun)


Lesenswert?

Hallo zusammen,
da ich an diesem Programm gerade verzweifle, wollte ich es hier noch mal 
schnell zur Diskussion stellen. Folgende Routine auf einem ATmega64:

if( ( pulse_timer >=5 ) && ( ch_pulse > 0 ) && ( PORTA & ( 1<<VCSEL ) ) 
) {
  PORTA &= ~( 1<<VCSEL );
  pulse_timer = 0;
}

if( ( pulse_timer >=5 ) && ( ch_pulse > 0 ) && !(PORTA & ( 1<<VCSEL ) ) 
) {
  PORTA |= ( 1<<VCSEL );
  pulse_timer = 0;
  ch_pulse--;
}

if( pulse_timer < 15 ) pulse_timer++;

wird innerhalb einer INT-Routine alle 10ms aufgerufen und soll folgendes
bewirken:
ch_pulse wird mit einer Zahl geladen, die die Anzahl der an einer 
PORTA-Leitung auszugebenden low-Impulse angibt.
Ausgehend davon, dass pulse_timer bereits 15 und die Portleitung 1, wird 
im ersten Teil die Leitung auf 0 gesetzt und der Timer zurückgesetzt. 5 
Aufrufe später ist der Timer dann 5, die Portleitung sollte 0 sein und 
daher im zweiten Teil die Leitung wieder auf 1 gesetzt, der Timer 
abermals neu gestartet und zudem der Pulszähler dekrementiert werden.
Soweit die Theorie - in der Praxis klappt es 20,30 mal, aber dann wird 
mal ein 0-Impuls doppelt so lang (d.h., eigentlich fehlt das high 
zwischendurch einfach) oder ganz selten bleibt die Portleitung auch 
einfach mal 0.
Bin ich gerade betriebsblind?
Am Port-Pin hängt ein kleiner MOSFET (BSN20, also ganz klein) - nicht 
dass der Umladestrom des Gate irgendwas stört...
Falls jemand eine Idee hat - immer her damit, ich bin drauf und dran, 
das Ganze nochmal neu (und anders) zu programmieren...

Gruß
Stefan

von ozo (Gast)


Lesenswert?

Mach die Negation !(PORTA & ( 1<<VCSEL ) ) mal anderes, z.B. durch 
Vergleich. Ich glaube, die Stelle macht nicht das, was du willst.

von Andreas K. (a-k)


Lesenswert?

Welchen Datentyp haben die Variablen und werden sie ausserhalb der 
Interrupt-Routine verwendet oder modifiziert?

von Stefan H. (shaun)


Lesenswert?

1. Die Negation mit ! ist ja keine logische Negation (~), sollte also 
funktionieren. Oder mal so gesagt: bisher hat sie immer getan was sie 
sollte, und wenn sie falsch wäre, sollte sie ja nicht 29 von 30 Malen 
funktionieren.
Oder...?

2. Die Variablen die Du hier siehst sind unsigned char 
(volatile-deklariert, avr-gcc). Nur ch_pulse wird anderweitig benutzt, 
und zwar nur wenn sie 0 ist auf die gewünschte Anzahl von Impulsen.

Ich hab schon drei Mal nachgesehen, ob nicht irgendwo aus Versehen PA0 
(das ist die Leitung namens VCSEL) beeinflusst wird - Fehlanzeige.

von Johannes M. (johnny-m)


Lesenswert?

Ich vermute mal, der Fehler liegt woanders. In dem Codeschnipsel gibt es 
keine Anhaltspunkte für Unregelmäßigkeiten. Zeig doch bitte mal mehr 
Code. So hat das keinen Zweck.

von Stefan H. (shaun)


Lesenswert?

Ich habe den gestrigen Beitrag mit dem elendig langen Quältext gelöscht, 
weil ich das Problem inzwischen lösen konnte.
Genaugenommen ist ein work around draus geworden, der IMHO aber sowieso 
Sinn macht. Ich habe testweise mal die am fraglichen Portpin hängende 
Schaltstufe entfernt, um Rückwirkungen auf die Versorgung als Ursache 
auszuschliessen - keine Änderung. An einem freien Pin von Port B 
hingegen gab es keine Probleme.
Da ausser der LCD-Routine nichts auf den Port A zugreift, habe ich diese 
kurzerhand lahmgelegt und siehe da: die Störungen sind weg. Warum das so 
ist, kann ich mir beim besten Willen nicht erklären.
Nun bin ich einen anderen Weg gegangen und habe die Aktualisierung des 
LCD-Inhalts nicht mehr willkürlich in der Hauptschleife erledigt, 
sondern kontrolliert durch ein Statusflag, welches am Ende der 
Interrupt-Serviceroutine gesetzt wird, dann in der Hauptschleife 
abgefragt wird und nur wenn es gesetzt ist - und demnach die 
zeitkritischen Dinge des INT-Handlers beendet sind - das LCD 
aktualisiern. Damit habe ich nun in mehreren Tests störungsfreien 
Betrieb erzielt. In diese Routine werde ich nun die Abfrage der 
A/D-Kanäle und die Mittelwertberechungen mit einbeziehen.

von Peter D. (peda)


Lesenswert?

Stefan Huebner wrote:

> Da ausser der LCD-Routine nichts auf den Port A zugreift, habe ich diese
> kurzerhand lahmgelegt und siehe da: die Störungen sind weg. Warum das so
> ist, kann ich mir beim besten Willen nicht erklären.

Das ist sehr einfach zu erklären:

In Deiner LCD-Routine sind warscheinlich Zugriffe wie "PORTA &= x;" und 
"PORTA |= x;".
Diese kann der AVR nicht atomar ausführen (der 8051 kanns).

Wenn nun ein Interrupt dazwischen auch nen PortA-Pin ändert, geht diese 
Änderung verloren (Das main hat ja im Register den alten Wert).

Abhilfe ist, alle derartigen Ausdrücke mit cli(); und sei(); zu 
klammern, damit kein Interrupt dazwischen funkt.


Peter

von Stefan H. (shaun)


Lesenswert?

Also doch Betriebsblindheit meinerseits ;)
Ich habe gerade letzte Woche für einen Kunden ein Testprogramm für ein 
Steuermodul mit 80C535 entwickelt, welches ihm ermöglichen soll, die 
gesamte Hardware im Modul mit einfachen Messgeräten auf Funktion zu 
prüfen, da war ich von den Adressierungsmöglichkeiten des MCS51 im Bezug 
auf die SFR noch komplett versaut.
Du meinst also, wenn ich mir die Mühe gemacht hätte, den Assemblercode 
des Compilers zu lesen, hätte ich so etwas wie
PORTA &= 0x55; -> register = PORTA; register &= 0x55; PORTA = register; 
(Pseudocode ;) gesehen?
Wenn da der Interrupt zwischenhaut, wird natürlich der Zustand der 
Port-Leseoperation geschrieben, egal, was der INT-Handler inzwischen mit 
dem Portpin anstellt. Danke für die Erleuchtung :)

von Philipp (Gast)


Lesenswert?

Mmmm, das interessiert mich jetzt aber auch. Dachte eigentlich das so 
etwas nicht passieren kann weil die Ports ja als volatile deklariert 
sind. Oder gilt das wirklich nur für die Optimierung des Compilers? Das 
er also nicht den Wert im Register hält wenn er ein paar Zeilen weiter 
wieder gebraut wird, sonder ihn dann neu einließt.

Philipp

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Philipp wrote:
> ..., sonder ihn dann neu einließt.

Klar liest er ihn neu wieder ein.  Aber es gibt halt ein Zeitfenster
zwischen dem Lesen des IO-Registers und dem Rückschreiben, in dem
der Interruptcode das IO-Register bereits neu beschrieben haben kann.
Diese Änderung wird dann durch das Rückschreiben aus dem unterbrochenen
Programm gewissermaßen rückgängig gemacht.

Was allerdings atomar ist sind SBI- und CBI-Befehle, die auf die
unteren 32 IO-Register anwendbar sind und dort ein einzelnes Bit
manipulieren können.  Also:
1
PORTB |= 1;
ist atomar, aber
1
PORTB |= 3;
ist es nicht (weil zwei Bits geändert werden).

von Philipp (Gast)


Lesenswert?

Ah, ok super. Vielen Dank, jetzt hab ichs verstanden :)

Gruß
Philipp

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.