STM8 Halt Mode / Wake Up über externen Interrupt

OP #7780500
Lesenswert?

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:

1
void main(void)
2
{
3
  delay_ms(50);
4
  // clock konfigurieren
5
  CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1);
6
  CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO, CLK_SOURCE_HSE, DISABLE, CLK_CURRENTCLOCKSTATE_DISABLE);
7
  // LIN Transceiver in den Sleep Mode versetzen - EXTI_PORT_GPIOD sollt dann so lange floaten bis Aktivität erkannt wird
8
  GPIO_Init(SLIN_SLP_OUT_GPIO, (GPIO_Pin_TypeDef)SLIN_SLP_OUT_PIN, GPIO_MODE_OUT_PP_LOW_SLOW);
9
  GPIO_Init(MLIN_SLP_OUT_GPIO, (GPIO_Pin_TypeDef)MLIN_SLP_OUT_PIN, GPIO_MODE_OUT_PP_LOW_SLOW);
10
  // GPIO D6 als interruptfähigen Input mit PullUp konfigurieren
11
  GPIO_Init(GPIOD, (GPIO_Pin_TypeDef)GPIO_PIN_6, GPIO_MODE_IN_PU_IT);
12
  // Externen interrupt konfigurieren 
13
  EXTI_SetExtIntSensitivity(EXTI_PORT_GPIOD, EXTI_SENSITIVITY_FALL_ONLY);
14
  delay_ms(5);
15
  // prüfen, ob der Port schon LOW ist - falls Nein, Interrupts aktivieren und in den Halt Mode gehen
16
  if (GPIO_ReadInputPin(GPIOD, (GPIO_Pin_TypeDef)GPIO_PIN_6) == SET)
17
  {
18
    enableInterrupts();
19
    halt();
20
  }
21
  // ...
22
}

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

OP #7780597
Lesenswert?

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

1
if (GPIO_ReadInputPin(GPIOD, (GPIO_Pin_TypeDef)GPIO_PIN_6))

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?

OP #7780943
Lesenswert?

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.

OP #7781034
Lesenswert?

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?

#7781071
Lesenswert?

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:

1
      cli();
2
      if (some_condition)
3
      {
4
        sei();
5
        sleep_cpu();
6
      }
7
      sei();
OP #7781107
Lesenswert?

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?

OP #7786953
Lesenswert?

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
8
    halt();
9
  }

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren