Skip to content

Commit 24cb76f

Browse files
authored
Merge pull request #61 from Tomer-Eliahu/main
Improved description of interrupts
2 parents f38bf39 + 4dade69 commit 24cb76f

4 files changed

Lines changed: 17 additions & 14 deletions

File tree

mdbook/src/15-interrupts/README.md

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ The model of computation used by our NRF52833 is the one used by almost every mo
1414

1515
Everything about the computation the CPU is currently running is stored in the CPU registers. If the core is going to switch tasks, it must store the contents of the CPU registers somewhere so that the new task can use the registers as its own scratch-pad. When the new task is complete the CPU can then restore the register values and restart the old computation. Sure enough, that is exactly the first thing the core does in response to an interrupt request: it stops what it's doing immediately and stores the contents of the CPU registers on the stack.
1616

17-
The next step is actually jumping to the code that should be run in response to an interrupt. An Interrupt Service Routines (ISR), often referred to as an interrupt "handler", is a special function in your application code that gets called by the core in response to interrupts. An "interrupt table" in memory contains an "interrupt vector" for every possible interrupt: the interrupt vector indicates what ISR to call when a specific interrupt is received. We describe the details of ISR vectoring in the [NVIC and Interrupt Priority] section.
17+
The next step is actually jumping to the code that should be run in response to an interrupt. An Interrupt Service Routine (ISR), often referred to as an interrupt "handler", is a special function in your application code that gets called by the core in response to interrupts. An "interrupt table" in memory contains an "interrupt vector" for every possible interrupt: the interrupt vector indicates what ISR to call when a specific interrupt is received. We describe the details of ISR vectoring in the [NVIC and Interrupt Priority] section.
1818

1919
An ISR function "returns" using a special return-from-interrupt machine instruction that causes the CPU to restore the CPU registers and jump back to where it was before the ISR was called.
2020

@@ -31,21 +31,20 @@ The ISR handler function is "special". The name `GPIOTE` is required here, indic
3131
that this ISR should be stored at the entry for the `GPIOTE` interrupt in the interrupt table.
3232

3333
The `#[interrupt]` decoration is used at compile time to mark a function to be treated specially as
34-
an ISR. (This is a "proc macro", in case you feel like exploring that concept.)
34+
an ISR. (This is a "proc macro": you can read more about it in the [Rust book] if you wish.)
3535

36-
Marking a function with `#[interrupt]` implies several special things about the function:
36+
Essentially, a "proc macro" translates source code into other source code. If you are curious as to what any particular macro use translates into,
37+
you could expand that macro invocation. You can do this by using either the Tools in the [Rust Playground] or the "rust-analyzer: Expand macro" command in your IDE.
3738

38-
* The compiler will check that the function takes no arguments and returns no value. The CPU has no
39-
arguments to provide to an ISR, and no place to put a return value from the ISR.
39+
Marking a function with `#[interrupt]` implies several special things about the function:
4040

41-
* The compiler will place a vector to this function at the location in the interrupt table
42-
implied by the function's name.
41+
* The compiler will check that the function takes no arguments and returns no value (or never returns). The CPU has no
42+
arguments to provide to an ISR, and no place to put a return value from the ISR. This is because interrupt handlers have their own call stack (at least *conceptually* if not always in practice).
4343

44-
* The function will be compiled to finishing by using a return-from-interrupt instruction rather
45-
than the normal function return instruction.
44+
* A vector to this function (that is a function pointer) will be placed at the location in the interrupt table
45+
which corresponds to the function's name.
4646

47-
* Since the function finishes in a non-standard way, the compiler will understand not to allow
48-
directly calling the ISR from normal code.
47+
* The compiler will prevent directly calling the ISR from normal code.
4948

5049
There are two steps to configure the interrupt. First, the GPIOTE must be set up to generate an
5150
interrupt when the pin connected to Button A goes from high to low voltage. Second, the NVIC must be
@@ -72,3 +71,5 @@ Normally, once an ISR is complete the main program continues running just as it
7271

7372
[NVIC and Interrupt Priority]: nvic-and-interrupt-priority.html
7473
[Registers]: ../09-registers/index.html
74+
[Rust Playground]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024
75+
[Rust book]: https://doc.rust-lang.org/book/ch20-05-macros.html

mdbook/src/15-interrupts/nvic-and-interrupt-priority.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ Preemption allows processors to respond very quickly to critical events. For ex
3030

3131
If an equal-priority or lower-priority interrupt occurs during an ISR, it will be "pended": the NVIC will remember the new interrupt and run its ISR sometime after the current ISR completes. When an ISR function returns the NVIC looks to see if, while the ISR was running, other interrupts have happened that need to be handled. If so, the NVIC checks the interrupt table and calls the highest-priority ISR vectored there. Otherwise, the CPU returns to the running program.
3232

33+
Note that if interrupts are disabled entirely, all incoming interrupts will be pended. Pending interrupts will be handled once interrupts are enabled again.
34+
3335
In embedded Rust, we can program the NVIC using the [`cortex-m`] crate, which provides methods to
3436
enable and disable (called `unmask` and `mask`) interrupts, set interrupt priorities, and trigger
3537
interrupts from software. Frameworks such as [RTIC] can handle NVIC configuration for you, taking

mdbook/src/15-interrupts/sharing-data-with-globals.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
> *[Interrupts Is Threads]* by James Munns, which contains more discussion about this
55
> topic.
66
7-
As I mentioned in the last section, when an interrupt occurs we aren't passed any arguments and
8-
cannot return any result. This makes it hard for our program interact with peripherals and other
7+
As I mentioned previously, when an interrupt occurs we aren't passed any arguments and
8+
cannot return any result. This makes it hard for our program to interact with peripherals and other
99
main program state. Before worrying about this bare-metal embedded problem, it is likely worth
1010
thinking about threads in "std" Rust.
1111

mdbook/src/15-interrupts/the-challenge.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Let's make the MB2 into a siren! But not just any siren — an
44
interrupt-driven siren. That way we can turn the siren on
55
and the rest of our program can run on, ignoring it.
66

7-
Make your siren sweep the pitch from 220Hz to 440Hz and back
7+
Make your siren sweep the pitch from 440Hz to 660Hz and back
88
over a one-second period. The main program should start the
99
siren, then print a ten-second countdown from 10 to 1, then
1010
stop the siren and print "launch!". The main program should

0 commit comments

Comments
 (0)