Hallo Zusammen,
ich habe hier eine Hardware mit STM8AF und einem TJA1022 Dual LIN
Transceiver.
Wenn einer der LIN Busse einschläft führt das System einen Reset durch,
beim Neustart soll die Software die LIN Transceiver schlafen schicken
und so lange in den Halt Mode gehen bis der LIN Transceiver einen
externen Wakeup (sprich Busaktivität) erkennt.
Aussehen tut das so:
Das Problem ist nur es funktioniert nicht - halt() wird auch bei einem
Reboot entweder nie aufgerufen oder das System kommt mehr oder weniger
sofort zurück.
Hat irgendjemand eine Idee was ich hier falsch mache?
Danke und Gruß,
Markus
OMG - ich hatte den Post sogar gesehen...
Habe mir den Quelltext von GPIO_ReadInputPin angesehen - das wird es
gewesen sein.
Vielen Dank!
Habe es jetzt auf
geändert und werde es testen.
Mich stört aber noch, dass da eine Race Condition auftritt:
Was passiert wenn der Pegel GPIO D6 zwischen GPIO_ReadInputPin() und
halt() auf Low geht? Dann kommt kein Interrupt (oder war schon da) und
aus halt() wird nie zurückgekehrt - oder?
Eventuell hilft gegen die Race Condition wenn man in der ISR für den Pin
ein Flag setzt und halt() nicht aufruft wenn das Flag gesetzt ist?
Vermutlich müsste man dann aber auch den Ablauf bis zu halt() umstellen
damit es passt.
Das ändert aber nix - die Race condition geht nicht weg. Egal was man
tut - wenn der Interrupt unmittelbar vor dem Aufruf von halt() kommt ist
das Problem da. So kann man nur das Zeitfenster minimieren.
Muss man denn enableInterrupts() explizit vor halt() aufrufen?
Laut PM0044 ("STM8 CPU programming manual") für HALT:
"The interrupt mask is reset, allowing interrupts to be fetched."
Danke - das probiere ich aus.
Da aber das prüfen des Input Pins und der Aufruf von halt() keine
atomare Operation sind, kann da dennoch ein Interrupt
dazwischengrätschen.
Anders wäre es, wenn Pegelwechsel nach Low nach Konfiguration des
Interrupts "zwischengespeichert" würden und erst bei Reset der Interrupt
Mask feuern würden.
Das weiß ich aber nicht.
Der Code läuft unmittelbar nach dem Reboot - da sind die Interrupts
sowieso noch disabled. Aber bedeutet das konkret, dass es ggf. so
ablaufen würde:
1) GPIO Pin geht von High nach Low
2) Aufruf halt() -> implizites enablen der Interrupts
3) Interrupt feuert, halt() kehrt zurück.
Das bedeutet, obwohl der GPIO Pin bei Aufruf von halt() schon Low ist
feuert der Interrupt?
"EXTI_SENSITIVITY_FALL_LOW" ("Interrupt on Falling edge and Low level")
anstelle "EXTI_SENSITIVITY_FALL_ONLY" ("Interrupt on Falling edge only")
geht nicht?
Markus schrieb:> Mich stört aber noch, dass da eine Race Condition auftritt:
Die µC-Entwickler machen sich schon Gedanken, daß Race Conditions nicht
auftreten, bzw. programmtechnisch ausgeschlossen werden können.
Der STM8 macht es dadurch einfach, daß HALT und Interrupt enable atomar
erfolgen.
Bei den AVRs ist das deutlich umständlicher gelöst. Da kann man
unendlich schlafen, wenn man nicht aufpaßt. Man muß direkt vor dem SLEEP
die Interrupts enablen (SEI). Da der SEI-Befehl erst nach dem nächsten
Befehl greift, ist sichergestellt, daß vor dem SLEEP kein Interrupt
angesprungen werden kann:
Ich hatte auch das Gefühl, dass es nicht sein kann, dass die Race
Condition nicht zu vermeiden sein soll.
Habe aber einfach zu wenig Ahnung vom STM8.
Vielen Dank für die tolle Unterstützung!
P.S: Wie kriege ich den STM auf den kleinsten Stromverbrauch?
Aktuell: Alle Peripheral Clocks abschalten, alle GPIO auf
GPIO_MODE_OUT_PP_LOW_SLOW, Halt.
Vorher wird noch die Clock auf 16 MHz Extern gesetzt, aber das sollte ja
nichts ausmachen, da die Clock im Halt Mode ja ohnehin angehalten ist.
Das Ding braucht dennoch 2-4 mA (der "Hardware Guy" misst noch).
Kann natürlich an externen Komponneten liegen, auf die Hardware habe ich
keinen direkten Einfluss.
Kann man da seitens der MCU noch was machen?
Ich kenne das Dokument. Hab's gerade nochmals durchgelesen.
Wenn ich es richtig sehe, kann ich mir das Abschalten der Peripheral
clocks sparen, im Halt Mode sind sie sowieso aus - oder?
Ansonsten gab es keine Erleuchtungen.
EXTI_SENSITIVITY_FALL_LOW hat leider nicht wie erwartet funktioniert -
ich mutmaße weil ich den Interrupt in der ISR nicht richtig zurücksetze.
Was wäre da der richtige Weg?
OK - nach langem hin- und herprobieren habe ich jetzt die scheinbar
einzig funktionierende Variante gefunden. Ich schreibe das vor allem
weil vielleicht ja jemand irgendwann über den Thread stolpert.
1
// wait for external interrupt
2
GPIO_Init(GPIOD,GPIO_PIN_6,GPIO_MODE_IN_PU_IT);
3
EXTI_SetExtIntSensitivity(EXTI_PORT_GPIOD,EXTI_SENSITIVITY_FALL_ONLY);// EXTI_SENSITIVITY_FALL_LOW seems not to work
4
delay_ms(1);
5
if(GPIO_ReadInputPin(GPIOD,GPIO_PIN_6))// don't compare with SET as GPIO_ReadInputPin will never return SET
6
{
7
enableInterrupts();// halt() seems not to enable interrupts