|
| 1 | +# Interrupts |
| 2 | + |
| 3 | +In the last exercise, we learnt how to read to read a GPIO pin state. |
| 4 | +However, it's not always desirable to poll the pin state in software. |
| 5 | +This is where interrupts come it. We can ask hardware to notify us whenever the pin state changes. |
| 6 | + |
| 7 | +## Background |
| 8 | + |
| 9 | +Because interrupts can happen at any time during the execution of our program, we can think of them |
| 10 | +as if they're running on a separate thread. As such, when working with interrupts, we apply the same |
| 11 | +principles as when working in a multi-threaded environment. |
| 12 | + |
| 13 | +Additionally, because the interrupt routine is called by hardware, there is no way to pass parameters or |
| 14 | +return values from the interrupt handler. |
| 15 | + |
| 16 | +One common way to pass data between the application and interrupt contexts is using global state. |
| 17 | +This is mostly straightforward in C, as you can just read and write any global variable at any time. |
| 18 | +Whether you like it or not, this "feature" opens up questions about what happens when the variable is modified |
| 19 | +while some other code is reading it? |
| 20 | + |
| 21 | +Consider this code: |
| 22 | + |
| 23 | +```c |
| 24 | +volatile int counter = 0; |
| 25 | + |
| 26 | +int main() { |
| 27 | + while (true) { |
| 28 | + app_task(); |
| 29 | + ++counter; // Same as: counter = counter + 1 |
| 30 | + } |
| 31 | +} |
| 32 | + |
| 33 | +void timer_interrupt() { |
| 34 | + interrupt_task(counter); |
| 35 | + counter = 0; |
| 36 | +} |
| 37 | +``` |
| 38 | + |
| 39 | +In Rust, however, using a mutable global state is explicitly `unsafe`. While it's possible to bypass |
| 40 | +the compiler rules, the language strongly encourages developers to write correct code. |
| 41 | + |
| 42 | +## Atomics & Mutexes |
| 43 | + |
| 44 | +When working with primitive types we can use one of the types provided in [`core::sync::atomic`][1]. |
| 45 | +Atomic types are the most fundamental way to ensure exclusive access -- these opperations are provided by the CPU. |
| 46 | + |
| 47 | +For more complex types, we have to resort to higher level synchronization primitives, such as [Mutexes][2] and |
| 48 | +other types of locks. Note that `Mutex` is only available in the `std` library, as is requires operating system support. |
| 49 | + |
| 50 | +No worries though, for embedded systems we can use Mutex provided by the [`critical_section`][3] crate. |
| 51 | + |
| 52 | +[1]: https://doc.rust-lang.org/nightly/std/sync/atomic/index.html |
| 53 | +[2]: https://doc.rust-lang.org/nightly/std/sync/struct.Mutex.html |
| 54 | +[3]: https://docs.rs/critical-section/latest/critical_section/ |
0 commit comments