Initial commit
Much of the content here is a direct port from https://github.com/japaric/discovery
This commit is contained in:
9
src/getting-started/.cargo/config
Normal file
9
src/getting-started/.cargo/config
Normal file
@@ -0,0 +1,9 @@
|
||||
[target.thumbv6m-none-eabi]
|
||||
runner = "arm-none-eabi-gdb"
|
||||
rustflags = [
|
||||
"-C", "link-arg=-Wl,-Tlink.x",
|
||||
"-C", "link-arg=-nostartfiles",
|
||||
]
|
||||
|
||||
[build]
|
||||
target = "thumbv6m-none-eabi"
|
||||
4
src/getting-started/.gdbinit
Normal file
4
src/getting-started/.gdbinit
Normal file
@@ -0,0 +1,4 @@
|
||||
target remote :3333
|
||||
load
|
||||
break main
|
||||
continue
|
||||
13
src/getting-started/00.00.README.md
Normal file
13
src/getting-started/00.00.README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Getting started
|
||||
|
||||
Alright, let's start as you usually would with Rust.
|
||||
|
||||
``` console
|
||||
$ rustup update
|
||||
```
|
||||
|
||||
It is always good to keep your toolchain up to date.
|
||||
|
||||
Now let's make a new binary project.
|
||||
You might not do this often, so it is understandeable to forget.
|
||||
If you run `$ cargo`, you will be given a hint.
|
||||
388
src/getting-started/01.00.PROJECT.md
Normal file
388
src/getting-started/01.00.PROJECT.md
Normal file
@@ -0,0 +1,388 @@
|
||||
# New Project
|
||||
|
||||
``` console
|
||||
$ cargo new rustled
|
||||
Created binary (application) `rustled` project
|
||||
$ cd rustled
|
||||
Cargo.toml src
|
||||
```
|
||||
|
||||
This has created a binary crate.
|
||||
|
||||
Now we could `$ cargo build` this, and even `$ cargo run` it,
|
||||
but everything is being compiled for, and run on, your computer.
|
||||
|
||||
## Targets
|
||||
|
||||
The micro:bit has a different architecture than your computer,
|
||||
so the first step will be to cross compile for the micro:bit's architecture.
|
||||
If you were to do an internet search, you would find a [platform support list for Rust][platforms].
|
||||
Looking into this page, you will find the micro:bit's nRF51822 Cortex-M0 microprocessor:
|
||||
|
||||
> `thumbv6m-none-eabi [*] [ ] [ ] Bare Cortex-M0, M0+, M1`
|
||||
|
||||
"thumbv6m-none-eabi" is known a a target triple. Note what the star represents:
|
||||
|
||||
> These are bare-metal microcontroller targets that only have access to the core library, not std.
|
||||
|
||||
To install this target:
|
||||
|
||||
``` console
|
||||
$ rustup target add thumbv6m-none-eabi
|
||||
```
|
||||
|
||||
## Build 1
|
||||
|
||||
Now how should we use this? Well, if you were to take a look at `$ cargo build -h`, you would try:
|
||||
|
||||
``` console
|
||||
$ cargo build --target thumbv6m-none-eabi
|
||||
```
|
||||
|
||||
```
|
||||
error[E0463]: can't find crate for `std`
|
||||
|
|
||||
= note: the `thumbv6m-none-eabi` target may not be installed
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0463`.
|
||||
error: Could not compile `rustled`.
|
||||
|
||||
To learn more, run the command again with --verbose.
|
||||
```
|
||||
|
||||
The help note is rather unhelpful because we just installed that target.
|
||||
We also just noted that the thumbv6m-none-eabi target does not include std,
|
||||
only the core crate, which is has a platform independent subset of the std features.
|
||||
Why is it still looking for the std crate when we build?
|
||||
|
||||
### `no_std`
|
||||
|
||||
It turns out, rust will always look for the std crate unless explicitly disabled,
|
||||
so we will add the no_std attribute
|
||||
|
||||
`src/main.rs`
|
||||
``` rust
|
||||
#![no_std]
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
||||
```
|
||||
|
||||
## Build 2
|
||||
|
||||
``` console
|
||||
$ cargo build --target thumbv6m-none-eabi
|
||||
```
|
||||
|
||||
```
|
||||
error: cannot find macro `println!` in this scope
|
||||
--> src/main.rs:4:5
|
||||
|
|
||||
4 | println!("Hello, world!");
|
||||
| ^^^^^^^
|
||||
```
|
||||
|
||||
`println` is a macro found in the std crate.
|
||||
We don't need it at the moment, so we can remove it and try to compile again:
|
||||
|
||||
```
|
||||
error: language item required, but not found: `panic_impl`
|
||||
```
|
||||
|
||||
This error, is because the panic macro is unimplemented,
|
||||
when rustc needs it to have an implementation.
|
||||
|
||||
### `panic_impl`
|
||||
|
||||
We could try and implement the panic macro ourselves,
|
||||
but it's easier and more portable to use a crate that does it for us.
|
||||
|
||||
If we look on [crates.io for the panic-impl keyword][panic] we will find some examples.
|
||||
Let us pic the simplest one, and add it to our Cargo.toml.
|
||||
If you have forgotten how to do this, try looking at [the cargo book][cargo].
|
||||
|
||||
[platforms]: https://forge.rust-lang.org/platform-support.html
|
||||
[panic]: https://crates.io/keywords/panic-impl
|
||||
[cargo]: https://doc.rust-lang.org/stable/cargo/
|
||||
|
||||
`Cargo.toml`
|
||||
```
|
||||
[dependencies]
|
||||
panic-abort = "~0.2"
|
||||
```
|
||||
|
||||
`src/main.rs`
|
||||
```
|
||||
#![no_std]
|
||||
|
||||
extern crate panic_abort;
|
||||
|
||||
fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
## Build 3
|
||||
|
||||
``` console
|
||||
$ cargo build --target thumbv6m-none-eabi
|
||||
```
|
||||
|
||||
```
|
||||
error: requires `start` lang_item
|
||||
```
|
||||
|
||||
### `no_main`
|
||||
|
||||
In the normal command line rust binaries you would be used to making,
|
||||
executing the binary usually has the operating system start by executing the C runtime library (crt0).
|
||||
This in turn invokes the Rust runtime, as marked by the `start` language item,
|
||||
which in turn invokes the main function.
|
||||
|
||||
Having enabled `no_std`, as we are targeting on a microcontroller,
|
||||
neither the crt0 nor the rust runtime are available,
|
||||
so even implementing `start` would not help us.
|
||||
We need to replace the operating system entry point.
|
||||
|
||||
You could for example name a function after the default entry point, which for linux is `_main`,
|
||||
and start that way. Note, you would also need to disable [name mangling][nm]:
|
||||
|
||||
```
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn _start() -> ! {
|
||||
loop {}
|
||||
}
|
||||
```
|
||||
|
||||
[nm]: https://en.wikipedia.org/wiki/Name_mangling
|
||||
|
||||
This is the end of the road for trying to get this to work on our own.
|
||||
At this point we need the help of a board specific crate and a few cargo tweaks to get this working.
|
||||
|
||||
## microbit crate
|
||||
|
||||
Let us add a dependency on the board crate for the micro:bit.
|
||||
|
||||
```
|
||||
[dependencies]
|
||||
panic-abort = "~0.2"
|
||||
microbit="~0.5"
|
||||
```
|
||||
|
||||
The microbit crate has 2 notable dependencies:
|
||||
|
||||
### `embedded-hal`
|
||||
|
||||
This crate is a HAL implementation crate, where HAL stands for *hardware abstraction layer*.
|
||||
As rust becomes more and more popular in embedded development,
|
||||
it is desireable to have as little hardware specific implementation as possible.
|
||||
|
||||
For this reason, the `embedded-hal` crate contains a range of hardware abstraction traits which can
|
||||
be implemented by board specific crates.
|
||||
|
||||
### `cortex-m-rt`
|
||||
|
||||
This crate implements the minimal startup / runtime for Cortex-M microcontrollers.
|
||||
Among other things this crate provides:
|
||||
- the `entry!` macro, to define the entry point of the program.
|
||||
- the `exception!` macro, to set or override a processor core exception handler.
|
||||
|
||||
This crate requires:
|
||||
- a definition of the specific microcontroller's memory layout as a memory.x file.
|
||||
- a definition of the hard fault handler
|
||||
- a definition of the default exception handler
|
||||
|
||||
For more detailed information,
|
||||
you can use the helpful [cortex-m-quickstart crate][qs] and [its documentation][doc].
|
||||
|
||||
[qs]: https://docs.rs/crate/cortex-m-quickstart
|
||||
[doc]: https://docs.rs/cortex-m-quickstart
|
||||
|
||||
## cargo config
|
||||
|
||||
Before we go any further,
|
||||
we are going to tweak the cargo's configuration by editing `rustled/.cargo/config`.
|
||||
For more information, you can read [the documentation here][cargoconfig].
|
||||
|
||||
[cargoconfig]: https://doc.rust-lang.org/cargo/reference/config.html
|
||||
|
||||
``` toml
|
||||
# Configure builds for our target
|
||||
[target.thumbv6m-none-eabi]
|
||||
# Execute binary using gdb
|
||||
runner = "arm-none-eabi-gdb"
|
||||
# Tweak to the linking process required by the cortex-m-rt crate
|
||||
rustflags = [
|
||||
"-C", "link-arg=-Tlink.x",
|
||||
"-C", "link-arg=-nostartfiles",
|
||||
]
|
||||
|
||||
# Automatically select this target when running cargo for this project
|
||||
[build]
|
||||
target = "thumbv6m-none-eabi"
|
||||
```
|
||||
|
||||
### arm-none-eabi-gdb
|
||||
|
||||
This is a version of gdb (the GNU debugger) for the ARM EABI (embedded application binary interface).
|
||||
It will allow us to debug the code running on our micro:bit, from your computer.
|
||||
|
||||
### Build target
|
||||
|
||||
Now, all you need to do is run `$ cargo build`,
|
||||
and cargo will automatically add `--target thumbv6m-none-eabi`.
|
||||
|
||||
## Build 4
|
||||
|
||||
`Cargo.toml`
|
||||
```
|
||||
[dependencies]
|
||||
panic-abort = "~0.2"
|
||||
microbit="~0.5"
|
||||
```
|
||||
|
||||
`src/main.rs`
|
||||
```
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate panic_abort;
|
||||
|
||||
entry!(main);
|
||||
fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
``` console
|
||||
cargo build
|
||||
```
|
||||
|
||||
```
|
||||
error[E0308]: mismatched types
|
||||
--> src/main.rs:9:1
|
||||
|
|
||||
8 | entry!(main);
|
||||
| ^^^^^^^^^^^^^ expected !, found ()
|
||||
|
|
||||
= note: expected type `fn() -> !`
|
||||
found type `fn() {main}`
|
||||
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
|
||||
```
|
||||
|
||||
## `!` return type
|
||||
|
||||
A little known rust feature, so I will forgive you if you do not know what this means.
|
||||
A return type of `!` means that the function cannot return
|
||||
An easy way to implement this, is by using an infinite loop.
|
||||
|
||||
`src/main.rs`
|
||||
```
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate panic_abort;
|
||||
|
||||
#[macro_use(entry)]
|
||||
extern crate microbit;
|
||||
|
||||
entry!(main);
|
||||
fn main() -> ! {
|
||||
loop {}
|
||||
}
|
||||
```
|
||||
|
||||
## Build 5
|
||||
|
||||
```
|
||||
error: linking with `arm-none-eabi-gcc` failed: exit code: 1
|
||||
|
|
||||
= note: "arm-none-eabi-gcc" "-L" "/home/xxx/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/thumbv6m-none-eabi/lib" "/home/xxx/rust/rustled/target/thumbv6m-none-eabi/debug/deps/rustled-e6053d34b0422141.2yhvr0tmp69gb94x.rcgu.o" "-o"
|
||||
# SNIP
|
||||
"/home/xxx/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/thumbv6m-none-eabi/lib/libcore-fb37a4ea1db1e473.rlib" "-Wl,--end-group" "/home/xxx/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/thumbv6m-none-eabi/lib/libcompiler_builtins-f2357c0397dd7e0d.rlib" "-Wl,-Tlink.x" "-nostartfiles" "-Wl,-Bdynamic"
|
||||
= note: /usr/lib/gcc/arm-none-eabi/8.1.0/../../../../arm-none-eabi/bin/ld: cannot open linker script file memory.x: No such file or directory
|
||||
collect2: error: ld returned 1 exit status
|
||||
```
|
||||
|
||||
A scary error, but if you look closely you will see `cannot open linker script file memory.x: No such file or directory`.
|
||||
We mentioned something a little earlier about memory.x file.
|
||||
To save you the hassle of scouring the internet for one or creating your own, you can copy it over into your project:
|
||||
|
||||
``` console
|
||||
cp ../getting-started/memory.x
|
||||
```
|
||||
|
||||
> Often a board support crate will already include this, so this step will not be necessary.
|
||||
|
||||
## Build 6
|
||||
|
||||
```
|
||||
error: linking with `arm-none-eabi-gcc` failed: exit code: 1
|
||||
|
|
||||
= note: "arm-none-eabi-gcc" "-L" "/home/xxx/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/thumbv6m-none-eabi/lib" "/home/xxx/rust/rustled/target/thumbv6m-none-eabi/debug/deps/rustled-e6053d34b0422141.2yhvr0tmp69gb94x.rcgu.o" "-o"
|
||||
# SNIP
|
||||
"/home/xxx/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/thumbv6m-none-eabi/lib/libcompiler_builtins-f2357c0397dd7e0d.rlib" "-Wl,-Tlink.x" "-nostartfiles" "-Wl,-Bdynamic"
|
||||
= note: device.x:1: undefined symbol `DefaultHandler' referenced in expression
|
||||
collect2: error: ld returned 1 exit status
|
||||
|
||||
```
|
||||
|
||||
Notice `undefined symbol 'DefaultHandler' referenced in expression`.
|
||||
We said earlier, as with the memory,
|
||||
that the hard fault handler and default exception handler both need defining.
|
||||
|
||||
`Cargo.toml`
|
||||
```
|
||||
[dependencies]
|
||||
panic-abort = "~0.2"
|
||||
cortex-m-rt="~0.5"
|
||||
microbit="~0.5"
|
||||
```
|
||||
|
||||
`src/main.rs`
|
||||
```
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate panic_abort;
|
||||
extern crate cortex_m_rt as rt;
|
||||
|
||||
#[macro_use(entry, exception)]
|
||||
extern crate microbit;
|
||||
|
||||
use rt::ExceptionFrame;
|
||||
|
||||
entry!(main);
|
||||
fn main() -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
||||
exception!(HardFault, hard_fault);
|
||||
|
||||
fn hard_fault(ef: &ExceptionFrame) -> ! {
|
||||
panic!("{:#?}", ef);
|
||||
}
|
||||
|
||||
exception!(*, default_handler);
|
||||
|
||||
fn default_handler(irqn: i16) {
|
||||
panic!("Unhandled exception (IRQn = {})", irqn);
|
||||
}
|
||||
```
|
||||
|
||||
It is all a bit ugly, but fortunately it only needs to be done once.
|
||||
If you try building now, you should finally be greeted with `Finished`!
|
||||
|
||||
## Build Complete
|
||||
|
||||
As a sanity check, let's verify that the produced executable is actually an ARM binary:
|
||||
|
||||
``` console
|
||||
$ file target/thumbv6m-none-eabi/debug/rustled
|
||||
target/thumbv6m-none-eabi/debug/rustled: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, with debug_info, not stripped
|
||||
^^^ ^^^^
|
||||
```
|
||||
192
src/getting-started/02.00.FLASH.md
Normal file
192
src/getting-started/02.00.FLASH.md
Normal file
@@ -0,0 +1,192 @@
|
||||
# Flashing
|
||||
|
||||
Flashing is the process of moving our program into the microcontroller's (persistent) memory. Once flashed, the microcontroller will execute the flashed program every time it is powered on.
|
||||
|
||||
In this case, our `rustled` program will be the only program in the microcontroller memory. By this I mean that there's nothing else running on the microcontroller: no OS, no "daemon", nothing. `rustled` has full control over the device. This is what is meant by *bare-metal* programming.
|
||||
|
||||
Onto the actual flashing. First thing we need is to do is launch OpenOCD. We did that in the previous section but this time we'll run the command inside a temporary directory (/tmp on *nix; %TEMP% on Windows).
|
||||
|
||||
Connect the mirco:bit to your computer and run the following commands on a new terminal.
|
||||
|
||||
``` console
|
||||
$ # *nix
|
||||
$ cd /tmp
|
||||
|
||||
$ # Windows
|
||||
$ cd %TEMP%
|
||||
```
|
||||
|
||||
We need to give OCD the name of the interfaces we are using:
|
||||
|
||||
``` console
|
||||
$ # All
|
||||
$ # Windows: remember that you need an extra `-s %PATH_TO_OPENOCD%\share\scripts`
|
||||
$ openocd -f interface/cmsis-dap.cfg -f target/nrf51.cfg
|
||||
```
|
||||
|
||||
The program will block; leave that terminal open.
|
||||
|
||||
Now it's a good time to explain what this command is actually doing.
|
||||
|
||||
I mentioned that the micro:bit actually has two microcontrollers.
|
||||
One of them is used as a USB interface and programmer/debugger.
|
||||
This microcontroller is connected to the target microcontroller using a Serial Wire Debug (SWD) interface
|
||||
(this interface is an ARM standard so you'll run into it when dealing with other Cortex-M based microcontrollers).
|
||||
This SWD interface can be used to flash and debug a microcontroller.
|
||||
It uses the CMSIS-DAP protocol for host debugging of application programs.
|
||||
It will appear as a USB device when you connect the micro:bit to your laptop.
|
||||
|
||||
As for OpenOCD, it's software that provides some services like a *GDB server* on top of USB
|
||||
devices that expose a debugging protocol like SWD or JTAG.
|
||||
|
||||
Onto the actual command: those `.cfg` files we are using instruct OpenOCD to look for
|
||||
- a CMSIS-DAP USB interface device (`interface/cmsis-dap.cfg`)
|
||||
- a nRF51XXX microcontroller target (`target/nrf51.cfg`) to be connected to the USB interface.
|
||||
|
||||
The OpenOCD output looks like this:
|
||||
|
||||
``` console
|
||||
Open On-Chip Debugger 0.9.0 (2016-04-27-23:18)
|
||||
Licensed under GNU GPL v2
|
||||
For bug reports, read
|
||||
http://openocd.org/doc/doxygen/bugs.html
|
||||
Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
|
||||
adapter speed: 1000 kHz
|
||||
adapter_nsrst_delay: 100
|
||||
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
|
||||
none separate
|
||||
Info : Unable to match requested speed 1000 kHz, using 950 kHz
|
||||
Info : Unable to match requested speed 1000 kHz, using 950 kHz
|
||||
Info : clock speed 950 kHz
|
||||
Info : STLINK v2 JTAG v27 API v2 SWIM v15 VID 0x0483 PID 0x374B
|
||||
Info : using stlink api v2
|
||||
Info : Target voltage: 2.919073
|
||||
Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints
|
||||
```
|
||||
|
||||
The "6 breakpoints, 4 watchpoints" part indicates the debugging features the processor has
|
||||
available.
|
||||
|
||||
I mentioned that OpenOCD provides a GDB server so let's connect to that right now:
|
||||
|
||||
``` console
|
||||
$ arm-none-eabi-gdb -q target/thumbv7em-none-eabihf/debug/led-roulette
|
||||
Reading symbols from target/thumbv7em-none-eabihf/debug/led-roulette...done.
|
||||
(gdb)
|
||||
```
|
||||
|
||||
This only opens a GDB shell. To actually connect to the OpenOCD GDB server, use the following
|
||||
command within the GDB shell:
|
||||
|
||||
```
|
||||
(gdb) target remote :3333
|
||||
Remote debugging using :3333
|
||||
0x00000000 in ?? ()
|
||||
```
|
||||
|
||||
By default OpenOCD's GDB server listens on TCP port 3333 (localhost). This command is connecting to
|
||||
that port.
|
||||
|
||||
After entering this command, you'll see new output in the OpenOCD terminal:
|
||||
|
||||
``` diff
|
||||
Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints
|
||||
+Info : accepting 'gdb' connection on tcp/3333
|
||||
+Info : device id = 0x10036422
|
||||
+Info : flash size = 256kbytes
|
||||
```
|
||||
|
||||
Almost there. To flash the device, we'll use the `load` command inside the GDB shell:
|
||||
|
||||
```
|
||||
(gdb) load
|
||||
Loading section .vector_table, size 0x188 lma 0x8000000
|
||||
Loading section .text, size 0x38a lma 0x8000188
|
||||
Loading section .rodata, size 0x8 lma 0x8000514
|
||||
Start address 0x8000188, load size 1306
|
||||
Transfer rate: 6 KB/sec, 435 bytes/write.
|
||||
```
|
||||
|
||||
And that's it. You'll also see new output in the OpenOCD terminal.
|
||||
|
||||
``` diff
|
||||
Info : flash size = 256kbytes
|
||||
+Info : Unable to match requested speed 1000 kHz, using 950 kHz
|
||||
+Info : Unable to match requested speed 1000 kHz, using 950 kHz
|
||||
+adapter speed: 950 kHz
|
||||
+target state: halted
|
||||
+target halted due to debug-request, current mode: Thread
|
||||
+xPSR: 0x01000000 pc: 0x08000194 msp: 0x2000a000
|
||||
+Info : Unable to match requested speed 8000 kHz, using 4000 kHz
|
||||
+Info : Unable to match requested speed 8000 kHz, using 4000 kHz
|
||||
+adapter speed: 4000 kHz
|
||||
+target state: halted
|
||||
+target halted due to breakpoint, current mode: Thread
|
||||
+xPSR: 0x61000000 pc: 0x2000003a msp: 0x2000a000
|
||||
+Info : Unable to match requested speed 1000 kHz, using 950 kHz
|
||||
+Info : Unable to match requested speed 1000 kHz, using 950 kHz
|
||||
+adapter speed: 950 kHz
|
||||
+target state: halted
|
||||
+target halted due to debug-request, current mode: Thread
|
||||
+xPSR: 0x01000000 pc: 0x08000194 msp: 0x2000a000
|
||||
```
|
||||
|
||||
Our program is loaded, we can now run it!
|
||||
|
||||
```
|
||||
(gdb) continue
|
||||
Continuing.
|
||||
```
|
||||
|
||||
Continue runs the program until the next breakpoint.
|
||||
This time it blocks, nothing happens.
|
||||
This is because all we have in our code is a loop!
|
||||
|
||||
## `.gdbinit`
|
||||
|
||||
Before we move on though, we are going to add one more file to our project.
|
||||
This will automate the last few steps so we don't need to repeatedly do the same actions in gdb:
|
||||
|
||||
`.gdbinit`
|
||||
```
|
||||
target remote :3333
|
||||
load
|
||||
```
|
||||
|
||||
## LED
|
||||
|
||||
Let us now turn on an LED! But how?
|
||||
|
||||
Well, first we should look at the documentation of our crate,
|
||||
and you should be able to figure out how to get access to the gpio,
|
||||
and set individual pins high and low:
|
||||
|
||||
``` rust
|
||||
if let Some(p) = microbit::Peripherals::take() {
|
||||
let mut gpio = p.GPIO.split();
|
||||
let mut pin1 = gpio.pin1.into_push_pull_output();
|
||||
pin1.set_high();
|
||||
}
|
||||
```
|
||||
|
||||
Next we need to see how these pins are hooked up,
|
||||
for that we need [the micro:bit schematics][schematics] linked to at the bottom of [the hardware overview][hw].
|
||||
On the first sheet you should find a diagram with a grid of numbered LEDs.
|
||||
|
||||
> If you do not know much about electronics:
|
||||
> Each row and column (labelled ROW and COL) represent a GPIO output pin.
|
||||
> The components labelled LED are light emitting diodes;
|
||||
> LEDs only let current flow one way, and only emit light when current is flowing.
|
||||
> If a row is set high, high voltage, and a column is set low, low voltage,
|
||||
> the LED at the point that they cross will have a potential difference across it,
|
||||
> so current will flow and it will light up.
|
||||
|
||||
It is worth noting that the 5x5 array of LEDs is wired up as a 9x3 array, with 2 missing.
|
||||
This is usually done to make the circuit design easier.
|
||||
|
||||
The fifth sheet shows how each row and column correspond to each GPIO pin.
|
||||
|
||||
[hw]: http://tech.microbit.org/hardware/
|
||||
[schematics]: https://github.com/bbcmicrobit/hardware/blob/master/SCH_BBC-Microbit_V1.3B.pdf
|
||||
|
||||
You should now have enough information to try and turn on an LED.
|
||||
41
src/getting-started/03.00.LED.md
Normal file
41
src/getting-started/03.00.LED.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# LED
|
||||
|
||||
This is my solution:
|
||||
|
||||
``` rust
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate panic_abort;
|
||||
extern crate cortex_m_rt as rt;
|
||||
|
||||
#[macro_use(entry, exception)]
|
||||
extern crate microbit;
|
||||
|
||||
use rt::ExceptionFrame;
|
||||
use microbit::hal::prelude::*;
|
||||
|
||||
entry!(main);
|
||||
fn main() -> ! {
|
||||
if let Some(p) = microbit::Peripherals::take() {
|
||||
let mut gpio = p.GPIO.split();
|
||||
let mut led = gpio.pin13.into_push_pull_output();
|
||||
let _ = gpio.pin4.into_push_pull_output();
|
||||
led.set_high();
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
exception!(HardFault, hard_fault);
|
||||
|
||||
fn hard_fault(ef: &ExceptionFrame) -> ! {
|
||||
panic!("{:#?}", ef);
|
||||
}
|
||||
|
||||
exception!(*, default_handler);
|
||||
|
||||
fn default_handler(irqn: i16) {
|
||||
panic!("Unhandled exception (IRQn = {})", irqn);
|
||||
}
|
||||
```
|
||||
It is worth noting that pin4 starts low, so does not need to be explicitly set low.
|
||||
1
src/getting-started/04.00.DEBUGGING.md
Normal file
1
src/getting-started/04.00.DEBUGGING.md
Normal file
@@ -0,0 +1 @@
|
||||
# Debugging
|
||||
8
src/getting-started/Cargo.toml
Normal file
8
src/getting-started/Cargo.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "rustled"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
panic-abort = "~0.2"
|
||||
cortex-m-rt="~0.5"
|
||||
microbit="~0.5"
|
||||
11
src/getting-started/memory.x
Normal file
11
src/getting-started/memory.x
Normal file
@@ -0,0 +1,11 @@
|
||||
MEMORY
|
||||
{
|
||||
/* NOTE K = KiBi = 1024 bytes */
|
||||
FLASH : ORIGIN = 0x00000000, LENGTH = 256K
|
||||
RAM : ORIGIN = 0x20000000, LENGTH = 16K
|
||||
}
|
||||
|
||||
/* This is where the call stack will be allocated. */
|
||||
/* The stack is of the full descending type. */
|
||||
/* NOTE Do NOT modify `_stack_start` unless you know what you are doing */
|
||||
_stack_start = ORIGIN(RAM) + LENGTH(RAM);
|
||||
29
src/getting-started/src/main.rs
Normal file
29
src/getting-started/src/main.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
#![deny(unsafe_code)]
|
||||
#![no_std]
|
||||
|
||||
extern crate aux5;
|
||||
|
||||
use aux5::prelude::*;
|
||||
use aux5::{Delay, Leds};
|
||||
|
||||
fn main() {
|
||||
let (mut delay, mut leds): (Delay, Leds) = aux5::init();
|
||||
|
||||
let period = 50_u16;
|
||||
let mut step: usize = 0;
|
||||
loop {
|
||||
match step % 2 {
|
||||
0 => leds[step/2].on(),
|
||||
1 => {
|
||||
let wrap_step = ((step + 16 - 3) / 2) % 8;
|
||||
leds[wrap_step].off();
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
delay.delay_ms(period);
|
||||
match step {
|
||||
15 => step = 0,
|
||||
_ => step += 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user