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
Mach die Negation !(PORTA & ( 1<<VCSEL ) ) mal anderes, z.B. durch Vergleich. Ich glaube, die Stelle macht nicht das, was du willst.
Welchen Datentyp haben die Variablen und werden sie ausserhalb der Interrupt-Routine verwendet oder modifiziert?
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.
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.
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.
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
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 :)
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
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).
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.