Skip to content

Commit 05e86db

Browse files
committed
[stm32-lvgl] Fix an interrupt safety bug that caused occasional crashes
1 parent 89dda98 commit 05e86db

File tree

3 files changed

+20
-7
lines changed

3 files changed

+20
-7
lines changed

stm32-lvgl/Sources/Application/Interrupts.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,23 @@ func SystickTimerISR() {
6969
uptimeInMs += 1
7070
}
7171

72+
// The following interrupt set up is trickier that it looks on the surface. The
73+
// ISR Swift code must be "trivial" directly and transitively, and namely it
74+
// must avoid destroying any heap objects (because we could be inside malloc
75+
// when the interrupt hits). For that, the expectation is that
76+
// lcdInterruptVerticalSyncHandler is only ever set once, and it not changing
77+
// after boot. The code inside the lcdInterruptVerticalSyncHandler closure is
78+
// expected to only perform trivial operations. lcdInterruptVerticalSyncEnabled
79+
// is allowed to change.
80+
7281
var lcdInterruptVerticalSyncHandler: (() -> Void)? = nil
82+
var lcdInterruptVerticalSyncEnabled: Bool = false
7383

7484
@_cdecl("LtdcIntHandlerISR")
7585
func LtdcIntHandlerISR() {
7686
let sr = ltdc.isr.read()
7787
ltdc.icr.write { $0.storage = sr.storage }
7888
if sr.raw.rrif != 0 {
79-
lcdInterruptVerticalSyncHandler?()
89+
if lcdInterruptVerticalSyncEnabled { lcdInterruptVerticalSyncHandler?() }
8090
}
8191
}

stm32-lvgl/Sources/Application/Main.swift

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -120,14 +120,16 @@ struct Main {
120120
// interrupt handler. Once we get our VBI, we can tell
121121
// LVGL that we're good to go to switch again.
122122
Lcd.setFrameBuffer(bufferToShow!)
123-
lcdInterruptVerticalSyncHandler = {
124-
lv_display_flush_ready(disp)
125-
lcdInterruptVerticalSyncHandler = nil
126-
}
123+
lcdInterruptVerticalSyncEnabled = true
127124
Lcd.reloadConfiguration()
128125
// the lv_display_flush_ready() will happen in the LCD frame interrupt.
129126
})
130127

128+
lcdInterruptVerticalSyncHandler = {
129+
lv_display_flush_ready(disp)
130+
lcdInterruptVerticalSyncEnabled = false
131+
}
132+
131133
let touch = lv_indev_create()
132134
lv_indev_set_type(touch, LV_INDEV_TYPE_POINTER)
133135
lv_indev_set_read_cb(
@@ -152,7 +154,7 @@ struct Main {
152154

153155
while true {
154156
// If we're pending a render, wait.
155-
while lcdInterruptVerticalSyncHandler != nil { /* busy wait */ nop() }
157+
while lcdInterruptVerticalSyncEnabled { /* busy wait */ nop() }
156158

157159
lv_timer_handler()
158160

stm32-lvgl/toolset.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@
3838
"-llvgl", "-llvgl_demos",
3939
"-static",
4040
"-e", "_start_elf",
41-
"--orphan-handling=error"
41+
"--orphan-handling=error",
42+
"-Map", ".build/armv7em-none-none-eabi/release/Application.map"
4243
]
4344
}
4445
}

0 commit comments

Comments
 (0)