Skip to content

STM8 eForth Interrupts

Thomas edited this page Mar 1, 2019 · 7 revisions

Low-level Interrupts in Forth

Working with certain STM8S peripherals requires using low-level interrupts.

Interrupt service routines (ISR) can be coded in Forth with the help of the STM8 eForth lightweight-context-switch feature: almost all aspects are under user control as long as a certain usage pattern is observed. By default, the data stack is 8 cells deep, which should be more than sufficient for most applications. Low-level interrupts run with the highest "software priority". ISRs have a higher priority than the background task but one ISR can't preempt another.

Note that STM8 eForth also has a Background Task feature for executing input-processing-output code with a constant rate, and an Idle Task, e.g. for implementing protocols with string evaluation. These use cases don't require interrupt code.

Low level interrupt code depends on the following words:

  • SAVEC: save the context
  • IRET: restore the context and return from the interrupt

The very nature if interrupt code (real time, context change) makes interactive debugging difficult but it's possible to test ISRs using the interactive console. A good way to do that is to test words, e.g. buffer or protocol handlers, interactively before using them in a interrupt routine. The MODBUS and nRF24L01 libraries on the STM8 eForth Example Code were developed using mixed interactive and "pin-debugging" methods.

Architecture differences in STM8 Families

Unfortunately there are significant architecture differences between STM8S and STM8L which often require different implementations of ISRs. For STM8 family independent functionality in libraries it's necessary to separate low level interrupt configuration and peripheral handlers from higher level control.

As an example, the STM8 Families STM8S and STM8L have strikingly different architectures, and trade-offs for external interrupt configuration:

STM8S uses "configuration per port" for PA, PB, PC, PD and PE:

  • STM8S assigns one interrupt vector and one edge/level configuration to a port (EXTI_CR1 for ports A through D, EXTI_CR2 for port E)
  • interrupts are enabled or disabled per GPIO (Px_CR2)
  • interrupts remain active as long as the interrupt condition is present
  • the ports PF, PG, PH and PH can't produce external interrupts
  • one TLI (top level interrupt) is assigned to either PC3 or PD7

STM8L provides more versatile, but also more complex features (see STM8L reference manual 12.6))

  • configuration per port (PB/PG, PD/PH, PE/PF) or per GPIO index Px0 ... Px7 (PA, PB, PC, PD, PE) and PF0
  • for both modes interrupts are enabled or disabled per GPIO (Px_CR2)
  • interrupts remain active unless EXTI_SRx is cleared by writing a 1 to the corresponding bit

Other peripherals often have less obvious or minor differences, however, sometimes key features, like STM8L USART DMA, make family dependent code rather attractive.

Wake-Up Timer Interrupt Example

As a simple example, the following code implements an AWU (Auto Wake Up) handler.

The AWU is used to provide an internal wake-up time base that can be used to return from the MCU "Active-Halt" power saving mode. This time base can be clocked by the low speed internal (LSI) RC oscillator clock or by the HSE crystal oscillator clock (both divided by a prescaler). Usage of the register names commencing with "AWU_" below are explained in the STM8S Reference Manual, Chapter 12.

#require :NVM

\res MCU: STM8S103


:NVM              \ interrupt handler, "headerless" code
   AWU_CSR1 C@
;NVM ( xt ) INT_AWU !

: initawu  ( -- ) \ AWU period about 1s
  31 AWU_APR C!   12 AWU_TBR C!   16 AWU_CSR1 C!

: HALT ( -- )    \ encodes the STM8 `HALT` instruction
  [ $8E C, ]

\\ Example
initawu HALT \ test - will return after the AWU period

The "nameless" interrupt handler is initiated with :NVM (put start address on stack, switch to NVM mode, start compiler mode). It first does a Forth VM context switch with SAVEC. Reading AWU_CSR1 then clears the AWU interrupt flag, and IRET restores the Forth VM context, and returns to with the STM8 IRET instruction. The stack can be left unbalanced (DROP isn't required). ;NVM switches back to interpreter mode, and the start address of the handler (the execution token xt which is still on the stack) is then written to the AWU interrupt vector.

initawu initializes the Auto Wakeup Timer with about 1s delay, and the word HALT encodes the STM8 HALT instruction that shuts down the CPU clock until an interrupt occurs.

When initawu followed by HALT is executed, the CPU stops, and returns through the execution of the Auto Wake-Up Timer interrupt.

Guidelines for STM8 eForth Interrupt Code

When writing interrupt handlers in Forth, the following practice is recommended:

  • interrupts should only be used for low-level code, e.g. for using µC peripherals that require interrupts to work
  • care should be taken regarding data- and return stack use (8 levels should be more than sufficient, the return stack must be balanced)
  • only the data processing that's absolutely required should be performed in interrupt routines. The rest should be done in a low-priority task (e.g. background)
  • the code should be fast - if in doubt use pin-debugging with a scope or a simple logic analyzer for testing the timing
  • high-level words, e.g, output string formatting, should not be used unless a proper context switch has been done (example: background task code in forth.asm)
  • when using character I/O potential side effects should be carefully assessed
You can’t perform that action at this time.