diff --git a/stm32-lvgl/Sources/Application/Interrupts.swift b/stm32-lvgl/Sources/Application/Interrupts.swift index b5a01d2..148b39b 100644 --- a/stm32-lvgl/Sources/Application/Interrupts.swift +++ b/stm32-lvgl/Sources/Application/Interrupts.swift @@ -69,13 +69,23 @@ func SystickTimerISR() { uptimeInMs += 1 } +// The following interrupt set up is trickier that it looks on the surface. The +// ISR Swift code must be "trivial" directly and transitively, and namely it +// must avoid destroying any heap objects (because we could be inside malloc +// when the interrupt hits). For that, the expectation is that +// lcdInterruptVerticalSyncHandler is only ever set once, and it not changing +// after boot. The code inside the lcdInterruptVerticalSyncHandler closure is +// expected to only perform trivial operations. lcdInterruptVerticalSyncEnabled +// is allowed to change. + var lcdInterruptVerticalSyncHandler: (() -> Void)? = nil +var lcdInterruptVerticalSyncEnabled: Bool = false @_cdecl("LtdcIntHandlerISR") func LtdcIntHandlerISR() { let sr = ltdc.isr.read() ltdc.icr.write { $0.storage = sr.storage } if sr.raw.rrif != 0 { - lcdInterruptVerticalSyncHandler?() + if lcdInterruptVerticalSyncEnabled { lcdInterruptVerticalSyncHandler?() } } } diff --git a/stm32-lvgl/Sources/Application/Main.swift b/stm32-lvgl/Sources/Application/Main.swift index d2722dc..ddc4166 100644 --- a/stm32-lvgl/Sources/Application/Main.swift +++ b/stm32-lvgl/Sources/Application/Main.swift @@ -120,14 +120,16 @@ struct Main { // interrupt handler. Once we get our VBI, we can tell // LVGL that we're good to go to switch again. Lcd.setFrameBuffer(bufferToShow!) - lcdInterruptVerticalSyncHandler = { - lv_display_flush_ready(disp) - lcdInterruptVerticalSyncHandler = nil - } + lcdInterruptVerticalSyncEnabled = true Lcd.reloadConfiguration() // the lv_display_flush_ready() will happen in the LCD frame interrupt. }) + lcdInterruptVerticalSyncHandler = { + lv_display_flush_ready(disp) + lcdInterruptVerticalSyncEnabled = false + } + let touch = lv_indev_create() lv_indev_set_type(touch, LV_INDEV_TYPE_POINTER) lv_indev_set_read_cb( @@ -152,7 +154,7 @@ struct Main { while true { // If we're pending a render, wait. - while lcdInterruptVerticalSyncHandler != nil { /* busy wait */ nop() } + while lcdInterruptVerticalSyncEnabled { /* busy wait */ nop() } lv_timer_handler() diff --git a/stm32-lvgl/toolset.json b/stm32-lvgl/toolset.json index ba5ab79..ad9da11 100644 --- a/stm32-lvgl/toolset.json +++ b/stm32-lvgl/toolset.json @@ -38,7 +38,8 @@ "-llvgl", "-llvgl_demos", "-static", "-e", "_start_elf", - "--orphan-handling=error" + "--orphan-handling=error", + "-Map", ".build/armv7em-none-none-eabi/release/Application.map" ] } }