Skip to content

Commit 21ea6fd

Browse files
committed
completed hello-world chapter
1 parent 9025968 commit 21ea6fd

File tree

15 files changed

+197
-17
lines changed

15 files changed

+197
-17
lines changed

mdbook/src/05-hello-world/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ version = "0.1.0"
77
[dependencies]
88
cortex-m-rt = "0.7.3"
99
embedded-hal = "1.0.0"
10+
microbit-v2 = "0.15.0"
1011
nrf52833-hal = "0.18.0"
1112
panic-halt = "0.2.0"
1213

mdbook/src/05-hello-world/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,19 +39,19 @@ But what do we do in software to cause this to occur? We will work at the level
3939
particular microcontroller easier to work with. As can be seen from the name, we have one for the
4040
microcontroller on the MB2. It happens to contain everything needed to turn our target LED on.
4141

42-
Take a look at `src/bin/light-up.rs` in this chapter's directory, and then try running it.
42+
Take a look at `examples/light-up.rs` in this chapter's directory, and then try running it.
4343
You could use something fancy like before, but we have it set up so that
4444

4545
```
46-
cargo run --bin light-up
46+
cargo run --example light-up
4747
```
4848

4949
will load and run your program. That one LED should now be brightly lit!
5050

5151
``` rust
52-
{{#include src/bin/light-up.rs}}
52+
{{#include examples/light-up.rs}}
5353
```
5454

5555
Note that we access the Peripheral Access Crate (PAC) for this chip through our HAL crate. There's a
5656
complicated dance needed to get access to our pins. Finally, since we can just initialize the pins
57-
to the right levels, we don't need to set them. That's a topic for the next section.
57+
to the right levels, we don't need to set them. Wiggling the pins is a topic for the next section.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Board support crate
2+
3+
Working directly with the PAC and HAL is pretty neat. Most ARM MCUs and many other MCUs that Rust
4+
can compile for have a PAC crate. If you are working with one that does not, writing a PAC crate can
5+
be tedious but is pretty straightforward. Many MCUs that have a PAC crate also have a HAL crate —
6+
again, it's mostly just tedious work to build one if it is absent. Code written at the PAC and HAL
7+
level gives access to the fine details of the MCU.
8+
9+
As we have seen, though, it becomes pretty annoying to keep track of just what is going on at the
10+
interface between our nRF52833 and the rest of our MB2. We have had to read schematics and whatnot
11+
to see how to use our off-board hardware.
12+
13+
A "board support crate" — known in the non-Rust embedded community as a Board Support Package (BSP)
14+
— is a crate built on top of the HAL and PAC for a board to abstract away the details and provide
15+
conveniences. The board support crate we have been working with is the `microbit-v2` crate.
16+
17+
Let's use `microbit-v2` to get a final, cleaned up blinky (`src/main.rs`).
18+
19+
```rust
20+
{{#include src/main.rs}}
21+
```
22+
23+
In this case, we haven't changed much. Our board support crate has hidden the PAC (for now). More
24+
importantly, it has done so by letting us just use reasonable names for the row and column GPIO pins
25+
for the LED.
26+
27+
The `microbit-v2` crate provides even fancier support for those "display" LEDs. We will see this
28+
support used soon to do things more fun than blinky.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#![no_main]
2+
#![no_std]
3+
4+
use cortex_m_rt::entry;
5+
use embedded_hal::{delay::DelayNs, digital::OutputPin};
6+
use nrf52833_hal::{gpio, pac, timer};
7+
use panic_halt as _;
8+
9+
#[entry]
10+
fn main() -> ! {
11+
let peripherals = pac::Peripherals::take().unwrap();
12+
13+
let p0 = gpio::p0::Parts::new(peripherals.P0);
14+
let mut row1 = p0.p0_21.into_push_pull_output(gpio::Level::High);
15+
let _col1 = p0.p0_28.into_push_pull_output(gpio::Level::Low);
16+
17+
let mut timer0 = timer::Timer::new(peripherals.TIMER0);
18+
19+
loop {
20+
timer0.delay_ms(500);
21+
row1.set_high().unwrap();
22+
timer0.delay_ms(500);
23+
row1.set_low().unwrap();
24+
}
25+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#![no_std]
2+
#![no_main]
3+
4+
use panic_halt as _;
5+
use riscv_rt::entry;
6+
use gd32vf103xx_hal::{pac, prelude::*, delay::McycleDelay};
7+
use embedded_hal::{blocking::delay::DelayMs, digital::v2::OutputPin};
8+
9+
#[entry]
10+
fn main() -> ! {
11+
let dp = pac::Peripherals::take().unwrap();
12+
13+
let mut rcu = dp.RCU.configure().ext_hf_clock(8.mhz()).sysclk(108.mhz()).freeze();
14+
15+
let gpioc = dp.GPIOC.split(&mut rcu);
16+
let mut led = gpioc.pc13.into_push_pull_output();
17+
let mut delay = McycleDelay::new(&rcu.clocks);
18+
19+
loop {
20+
delay.delay_ms(500);
21+
led.set_high().unwrap();
22+
delay.delay_ms(500);
23+
led.set_low().unwrap();
24+
}
25+
}

mdbook/src/05-hello-world/nop.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ You might wonder what that `nop()` call is doing in the `wait()` loop in `src/bi
44

55
The answer is that it literally does nothing. The `nop()` function causes the compiler to put a
66
`NOP` ARM machine instruction at that point in the program. `NOP` is a special instruction that
7-
causes the CPU to skip it. To ignore it. To literally do No OPeration with it, hence the name.
7+
causes the CPU to skip it. To ignore it. To literally do No OPeration with it (hence the name).
88

99
So get rid of that line and recompile the program. Don't forget `--release` mode. Then run it.
1010

1111
We're back to a slightly darker solid LED again. With no loop body, the compiler's optimizer decided
12-
that `wait()` wasn't doing anything. So it just removed it for you at compile time. Thanks
12+
that `wait()` function wasn't doing anything. So it just removed it for you at compile time. Thanks
1313
optimizer. You have made my wait loop infinitely fast.
1414

1515
How does `nop()` do its job? Well, if you look at the implementation of `nop()` you will find
@@ -28,4 +28,4 @@ embedded programming. Sometime a CPU will have instructions the compiler doesn't
2828
that you still need in order to use the CPU effectively. Rust's `asm!()` directive gives you a way
2929
to do that.
3030

31-
But our spin-wait is still terrible. Let's talk about doing better.
31+
Our spin-wait is still terrible. Let's talk about doing better.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Portability
2+
3+
(This section is optional. Feel free to skip to the [next section], where we clean our code up a bit
4+
and call it a day.)
5+
6+
[next section]: board-support.html
7+
8+
You may wonder whether all this fancy ecosystem is worth its weight. The setup for our blinky is
9+
pretty fancy, and uses a lot of Rust crates and features for such a simple job.
10+
11+
One cool advantage, though, is that our code becomes really portable. On a different board, the
12+
setup may be different, but the actual blinky loop is identical!
13+
14+
Let's take a look at a blinky for the Sipeed Longan Nano. This is a little $5 board that, like the
15+
MB2, is an embedded board with an MCU. Otherwise, it is completely different: different processor
16+
(the GD32VF103, with a RISC-V instruction set entirely unlike the ARM instruction set we're using),
17+
different peripherals, different board. But it has an LED attached to a GPIO pin, so we can blinky
18+
it.
19+
20+
```rust
21+
{{#include nanoblinky.rs}}
22+
```
23+
24+
The differences in setup here are partly because different hardware, and partly because this code
25+
uses an older HAL crate that hasn't yet been updated for `embedded-hal` 1.0. Yet the main loop is
26+
identical as advertised, and the rest of the code is pretty recognizable. Because of the portability
27+
provided by Rust's easy cross-compilation and the embedded Rust ecosystem, blinky is just blinky.
28+
29+
You can find a complete working [nanoblinky] example on GitHub, if you want to see all the
30+
details or even get your own board and try it yourself.
31+
32+
[nanoblinky]: https://github.com/pdx-cs-rust/nanoblinky

0 commit comments

Comments
 (0)