= Solution
\b[A short <interrupt handler> should acknowledge the detector and count work; a timed <finite-state machine> should generate the flashes. A <Boolean> “someone arrived” variable cannot preserve several queued detections.] This is an <interrupt-driven pulse sequencer>. Initialise the light off, the pending-event counter to zero and the sequencer to `IDLE` before enabling <interrupts>.
In the following pseudocode, <interrupt> acknowledgement uses the source-specified operation of writing zero. The <atomic operation> and timer calls name abstract services, not undocumented <device registers> of a particular microcontroller.
``
eye_handler():
if EYE_DETECTED != 0:
EYE_DETECTED = 0
pending = pending + 1
try_take_event():
briefly mask the detector interrupt
if pending == 0: taken = false
else: pending = pending - 1; taken = true
restore the previous interrupt mask
return taken
service(now):
if state == ON and elapsed(now, started) >= one_second:
LIGHT = 0
state = GAP
started = now
if state == GAP and elapsed(now, started) >= one_second:
state = IDLE
if state == IDLE and try_take_event():
LIGHT = 1
state = ON
started = now
``
The main program repeatedly handles a new event or the next timer deadline and calls `service`. The `ON` state lasts one second; \b[every completed flash enters `GAP`, even if the queue is empty at that instant]. An arrival during that gap must wait for it to expire. Otherwise turning the light off and immediately treating the device as idle could allow a new flash too soon. Once a gap has expired, a queued event starts a new flash; if no work is pending the device remains idle. Removing a pending event only when a flash starts gives one completed flash per consumed event.
An <interrupt> can arrive during the main program's decrement. Without a <critical section>, its increment can be overwritten by a <race condition>. Merely declaring the counter `volatile`, as in <volatile-qualified objects in C>, does not make the decrement an <atomic operation>. Short <interrupt masking>, or an appropriate <atomic operation>, protects it; <interrupts> must remain enabled during the one-second intervals so that detections continue to be recorded.
A calibrated <busy-wait> loop is simple, but occupies the <CPU> and its duration depends on instruction timing, compiler transformations and time spent in <interrupts>. Polling a <hardware timer> avoids calibration dependence but still consumes <CPU> while polling. A <timer interrupt> or timer comparison deadline permits useful work or safe sleeping between transitions and is preferable for precise timing. If sleeping, the test for queued work and entry into sleep must avoid a <lost wake-up>. By the <modulo-counter elapsed-time identity>, a wrapping <digital counter> is usable if elapsed-time subtraction is unsigned and the real observation interval is less than its modulus; a wide enough counter or suitable bounded intervals make this explicit. Software dispatch adds bounded latency, so the specification is achieved to timer/interrupt-service resolution; stricter physical pulse-width requirements call for hardware output comparison or a suitably prioritised timer handler.
Finally, the <device register> is only a <single-bit event latch>. Two physical detections before acknowledgement may coalesce, so no software can recover their multiplicity from that <bit> alone. The <event counter> must also have enough capacity for the allowed backlog; indefinitely many arrivals faster than one flash every two seconds cannot be guaranteed with finite storage. These are explicit hardware/rate assumptions, not missing input files. Keeping the handler short minimises the rearming interval but cannot abolish those limitations.
Back to article page