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.
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?
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.
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.
"EXTI_SENSITIVITY_FALL_LOW" ("Interrupt on Falling edge and Low level") anstelle "EXTI_SENSITIVITY_FALL_ONLY" ("Interrupt on Falling edge only") geht nicht?
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:
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.
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