Development progress
- many small fixes - add hello-world - fix source code
This commit is contained in:
9
.cargo/config
Normal file
9
.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"
|
||||
6
.gdbinit
Normal file
6
.gdbinit
Normal file
@@ -0,0 +1,6 @@
|
||||
target remote :3333
|
||||
monitor arm semihosting enable
|
||||
set print asm-demangle on
|
||||
load
|
||||
#break main
|
||||
continue
|
||||
@@ -1,6 +1,7 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"src/getting-started",
|
||||
"src/hello-world",
|
||||
]
|
||||
|
||||
[profile.dev]
|
||||
|
||||
@@ -13,14 +13,17 @@
|
||||
- [Verify the installation](setup/VERIFY.md)
|
||||
|
||||
- [Getting started](getting-started/00.00.README.md)
|
||||
- [Building](getting-started/01.00.PROJECT.md)
|
||||
- [Building](getting-started/01.00.BUILD.md)
|
||||
- [Flashing](getting-started/02.00.FLASH.md)
|
||||
- [LED](getting-started/03.00.LED.md)
|
||||
- [Debugging](getting-started/04.00.DEBUG.md)
|
||||
- [Debugging](getting-started/03.00.DEBUG.md)
|
||||
|
||||
- [WIP - Hello world](hello-world/00.00.README.md)
|
||||
- [Semi-hosting](hello-world/01.00.SEMIHOSTING.md)
|
||||
- [UART](hello-world/02.00.UART.md)
|
||||
- [Hello world](hello-world/00.00.README.md)
|
||||
- [Semihosting](hello-world/01.00.SEMIHOSTING.md)
|
||||
- [Serial communication](hello-world/02.00.UART.md)
|
||||
- [*nix](hello-world/02.01.NIX.md)
|
||||
- [Windows](hello-world/02.02.WINDOWS.md)
|
||||
- [LED](hello-world/03.00.LED.md)
|
||||
- [Solution](hello-world/03.01.SOLUTION.md)
|
||||
|
||||
- [Choose your own adventure](choice/00.00.README.md)
|
||||
|
||||
@@ -46,6 +49,8 @@
|
||||
|
||||
[Explore](appendix/explore.md)
|
||||
|
||||
[GDB cheatsheet](appendix/gdb.md)
|
||||
|
||||
[General troubleshooting](appendix/troubleshooting.md)
|
||||
|
||||
<!-- - [LED roulette](05-led-roulette/README.md)
|
||||
|
||||
15
src/appendix/gdb.md
Normal file
15
src/appendix/gdb.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# GDB cheatsheet
|
||||
|
||||
| Short | Command | Action |
|
||||
|:---------- |:---------------- |:------------------------------------------- |
|
||||
| b? | break | Add breakpoint |
|
||||
| | continue | |
|
||||
| | step | |
|
||||
| | stepi | |
|
||||
| | print | |
|
||||
| | info locals | |
|
||||
| | layout src | |
|
||||
| | tui disable | |
|
||||
| | layout asm | |
|
||||
| | dissasmble /m | |
|
||||
| | monitor reset halt| |
|
||||
@@ -166,3 +166,17 @@ $ rustup update nightly
|
||||
|
||||
$ rustup target add thumbv7em-none-eabihf
|
||||
```
|
||||
|
||||
## Build problems
|
||||
|
||||
### `error: language item required, but not found: \`eh_personality\``
|
||||
|
||||
#### Cause
|
||||
|
||||
The `eh_personality` language item is used to implement stack unwinding in case a panic occurs.
|
||||
|
||||
#### Fix
|
||||
|
||||
You need to use the correct target
|
||||
by using `--target thumbv6m-none-eabi`
|
||||
or modifying `.cargo/config`
|
||||
|
||||
@@ -86,7 +86,9 @@ error: cannot find macro `println!` in this scope
|
||||
```
|
||||
|
||||
`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:
|
||||
We don't need it at the moment, so we can remove it and try to build again.
|
||||
|
||||
## Build 3
|
||||
|
||||
```
|
||||
error: language item required, but not found: `panic_impl`
|
||||
@@ -124,7 +126,7 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
## Build 3
|
||||
## Build 4
|
||||
|
||||
``` console
|
||||
$ cargo build --target thumbv6m-none-eabi
|
||||
@@ -237,7 +239,7 @@ It will allow us to debug the code running on our micro:bit, from your computer.
|
||||
Now, all you need to do is run `$ cargo build`,
|
||||
and cargo will automatically add `--target thumbv6m-none-eabi`.
|
||||
|
||||
## Build 4
|
||||
## Build 5
|
||||
|
||||
`Cargo.toml`
|
||||
```
|
||||
@@ -296,7 +298,7 @@ fn main() -> ! {
|
||||
}
|
||||
```
|
||||
|
||||
## Build 5
|
||||
## Build 6
|
||||
|
||||
```
|
||||
error: linking with `arm-none-eabi-gcc` failed: exit code: 1
|
||||
@@ -313,12 +315,12 @@ 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
|
||||
cp ../../memory.x ./
|
||||
```
|
||||
|
||||
> Often a board support crate will already include this, so this step will not be necessary.
|
||||
|
||||
## Build 6
|
||||
## Build 7
|
||||
|
||||
```
|
||||
error: linking with `arm-none-eabi-gcc` failed: exit code: 1
|
||||
@@ -356,22 +358,22 @@ 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);
|
||||
}
|
||||
|
||||
entry!(main);
|
||||
fn main() -> ! {
|
||||
loop {}
|
||||
}
|
||||
```
|
||||
|
||||
It is all a bit ugly, but fortunately it only needs to be done once.
|
||||
@@ -4,21 +4,14 @@ Flashing is the process of moving our program into the microcontroller's (persis
|
||||
|
||||
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.
|
||||
|
||||
> OS: operating system
|
||||
<dl>
|
||||
<dt>OS</dt>
|
||||
<dd>operating system</dd>
|
||||
<dt>Daemon</dt>
|
||||
<dd>program running in the background</dd>
|
||||
</dl>
|
||||
|
||||
> Daemon: program running in the background
|
||||
|
||||
<!-- 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%
|
||||
``` -->
|
||||
Connect the micro:bit to your computer and run the following commands on a new terminal.
|
||||
|
||||
We need to give OCD the name of the interfaces we are using:
|
||||
|
||||
@@ -156,45 +149,16 @@ This will automate the last few steps so we don't need to repeatedly do the same
|
||||
|
||||
`.gdbinit`
|
||||
```
|
||||
# Connects GDB to OpenOCD server port
|
||||
target remote :3333
|
||||
# (optional) Unmangle function names when debugging
|
||||
set print asm-demangle on
|
||||
# Load your program, breaks at entry
|
||||
load
|
||||
# (optional) Add breakpoint at function
|
||||
break rustled::main
|
||||
# Continue with execution
|
||||
continue
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
The 5x5 array of LEDs are actually wired up as a 3x9 array (3 rows by 9 columns), 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.
|
||||
Now we can learn how to debug code on the micro:bit.
|
||||
@@ -1,5 +1,22 @@
|
||||
# Debugging
|
||||
|
||||
## Setup
|
||||
|
||||
Before we start, let's add some code to debug:
|
||||
|
||||
``` rust
|
||||
// -- snip --
|
||||
entry!(main);
|
||||
fn main() -> ! {
|
||||
let _y;
|
||||
let x = 42;
|
||||
_y = x;
|
||||
loop {}
|
||||
}
|
||||
```
|
||||
|
||||
## GDB session
|
||||
|
||||
We are already inside a debugging session so let's debug our program.
|
||||
|
||||
After the `load` command, our program is stopped at its *entry point*. This is indicated by the
|
||||
@@ -76,13 +93,15 @@ $3 = 134219052
|
||||
$4 = (i32 *) 0x10001fd8
|
||||
```
|
||||
|
||||
As expected, `x` contains the value `42`. `y`, however, contains the value `134219052` (?). Because
|
||||
`_y` has not been initialized yet, it contains some garbage value.
|
||||
As expected, `x` contains the value `42`.
|
||||
`_y` however, contains the value `134219052` (?).
|
||||
Because `_y` has not been initialized yet, it contains some garbage value.
|
||||
|
||||
The command `print &x` prints the address of the variable `x`. The interesting bit here is that GDB
|
||||
output shows the type of the reference: `i32*`, a pointer to an `i32` value. Another interesting
|
||||
thing is that the addresses of `x` and `_y` are very close to each other: their addresses are just
|
||||
`4` bytes apart.
|
||||
The command `print &x` prints the address of the variable `x`.
|
||||
The interesting bit here is that GDB output shows the type of the reference:
|
||||
`i32*`, a pointer to an `i32` value.
|
||||
Another interesting thing is that the addresses of `x` and `_y` are very close to each other:
|
||||
their addresses are just `4` bytes apart.
|
||||
|
||||
Instead of printing the local variables one by one, you can also use the `info locals` command:
|
||||
|
||||
@@ -116,8 +135,8 @@ command and advance one instruction at a time using `stepi`.
|
||||
(gdb) layout asm
|
||||
```
|
||||
|
||||
If you are not using the TUI mode, you can use the `disassemble /m` command to disassemble the
|
||||
program around the line you are currently at.
|
||||
If you are not using the TUI mode,
|
||||
you can use the `disassemble /m` command to disassemble the program around the line you are currently at.
|
||||
|
||||
```
|
||||
(gdb) disassemble /m
|
||||
@@ -145,8 +164,8 @@ End of assembler dump.
|
||||
|
||||
See the fat arrow `=>` on the left side? It shows the instruction the processor will execute next.
|
||||
|
||||
If not inside the TUI mode on each `stepi` command GDB will print the statement, the line number
|
||||
*and* the address of the instruction the processor will execute next.
|
||||
If not inside the TUI mode on each `stepi` command GDB will print the statement,
|
||||
the line number *and* the address of the instruction the processor will execute next.
|
||||
|
||||
```
|
||||
(gdb) stepi
|
||||
@@ -156,7 +175,8 @@ If not inside the TUI mode on each `stepi` command GDB will print the statement,
|
||||
0x08000194 17 loop {}
|
||||
```
|
||||
|
||||
One last trick before we move to something more interesting. Enter the following commands into GDB:
|
||||
One last trick before we move to something more interesting.
|
||||
Enter the following commands into GDB:
|
||||
|
||||
```
|
||||
(gdb) monitor reset halt
|
||||
@@ -176,17 +196,16 @@ Breakpoint 1, led_roulette::main () at src/main.rs:8
|
||||
We are now back at the beginning of `main`!
|
||||
|
||||
`monitor reset halt` will reset the microcontroller and stop it right at the program entry point.
|
||||
The following `continue` command will let the program run freely until it reaches the `main`
|
||||
function that has a breakpoint on it.
|
||||
The following `continue` command will let the program run freely until it reaches the `main` function that has a breakpoint on it.
|
||||
|
||||
This combo is handy when you, by mistake, skipped over a part of the program that you were
|
||||
interested in inspecting. You can easily roll back the state of your program back to its very
|
||||
beginning.
|
||||
This combo is handy when you, by mistake,
|
||||
skipped over a part of the program that you were interested in inspecting.
|
||||
You can easily roll back the state of your program back to its very beginning.
|
||||
|
||||
> **The fine print**: This `reset` command doesn't clear or touch RAM. That memory will retain its
|
||||
> values from the previous run. That shouldn't be a problem though, unless your program behavior
|
||||
> depends of the value of *uninitialized* variables but that's the definition of Undefined Behavior
|
||||
> (UB).
|
||||
> **The fine print**: This `reset` command doesn't clear or touch RAM.
|
||||
> That memory will retain its values from the previous run.
|
||||
> That shouldn't be a problem though, unless your program behavior depends of the value of *uninitialized* variables,
|
||||
> but that's the definition of *undefined behavior* (UB).
|
||||
|
||||
We are done with this debug session. You can end it with the `quit` command.
|
||||
|
||||
@@ -209,3 +228,9 @@ Ending remote debugging.
|
||||
|
||||
Don't close OpenOCD though! We'll use it again and again later on. It's better
|
||||
just to leave it running.
|
||||
|
||||
## What next?
|
||||
|
||||
In the next chapter we will learn
|
||||
how to send messages from the micro:bit to your computer,
|
||||
as well as howt to control the HAL GPIO.
|
||||
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "rustled"
|
||||
name = "start"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -1,29 +1,30 @@
|
||||
#![deny(unsafe_code)]
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate aux5;
|
||||
extern crate panic_abort;
|
||||
extern crate cortex_m_rt as rt;
|
||||
|
||||
use aux5::prelude::*;
|
||||
use aux5::{Delay, Leds};
|
||||
#[macro_use(entry, exception)]
|
||||
extern crate microbit;
|
||||
|
||||
fn main() {
|
||||
let (mut delay, mut leds): (Delay, Leds) = aux5::init();
|
||||
use rt::ExceptionFrame;
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
exception!(HardFault, hard_fault);
|
||||
|
||||
fn hard_fault(ef: &ExceptionFrame) -> ! {
|
||||
panic!("{:#?}", ef);
|
||||
}
|
||||
|
||||
exception!(*, default_handler);
|
||||
|
||||
fn default_handler(irqn: i16) {
|
||||
panic!("Unhandled exception (IRQn = {})", irqn);
|
||||
}
|
||||
|
||||
entry!(main);
|
||||
fn main() -> ! {
|
||||
let _y;
|
||||
let x = 42;
|
||||
_y = x;
|
||||
loop {}
|
||||
}
|
||||
|
||||
5
src/hello-world/.gdbinit
Normal file
5
src/hello-world/.gdbinit
Normal file
@@ -0,0 +1,5 @@
|
||||
target remote :3333
|
||||
monitor arm semihosting enable
|
||||
load
|
||||
break hello::main
|
||||
continue
|
||||
1
src/hello-world/00.00.README.md
Normal file
1
src/hello-world/00.00.README.md
Normal file
@@ -0,0 +1 @@
|
||||
# Hello world
|
||||
77
src/hello-world/01.00.SEMIHOSTING.md
Normal file
77
src/hello-world/01.00.SEMIHOSTING.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# Semihosting
|
||||
|
||||
Semihosting is a feature which allows targets without I/O support to use the I/O of the host.
|
||||
When the special `BKPT` instruction is reached, the host reads the characters directly from the micro:bit's memory.
|
||||
|
||||
## Semihosting is slow
|
||||
|
||||
The most important thing to remember about semihosting is that it is slow.
|
||||
The processor halts entirely for each operation, making each operation take 107 milliseconds.
|
||||
This means that if you are doing any time sensitive work, you should not use it for logging.
|
||||
[Check out this blog post for more information.](http://blog.japaric.io/itm/)
|
||||
|
||||
## GDB
|
||||
|
||||
The first thing to do is to enable semihosting in GDB.
|
||||
As before, we will add this to `.gdbinit` to avoid typing it every time.
|
||||
|
||||
`.gdbinit`
|
||||
|
||||
``` gdb
|
||||
target remote :3333
|
||||
monitor arm semihosting enable
|
||||
load
|
||||
```
|
||||
|
||||
## OpenOCD
|
||||
|
||||
You may have incorrectly assumed at this point that the outpust would appear in GDB.
|
||||
Remember that GDB simply connects to OpenOCD to interface with the micro:bit.
|
||||
OpenOCD is very loud currently,
|
||||
so it will be quite hard to see the output of our micro:bit in the noise.
|
||||
Fix this by stopping and restarting it with logging dumped to a file.
|
||||
|
||||
``` console
|
||||
openocd -f interface/cmsis-dap.cfg -f target/nrf51.cfg -l /tmp/openocd.log
|
||||
```
|
||||
|
||||
## Panic
|
||||
|
||||
The easiest way to use semihosting is to use it for the `panic!` macro.
|
||||
|
||||
`Cargo.toml`
|
||||
|
||||
``` toml
|
||||
panic-semihosting = ""
|
||||
```
|
||||
|
||||
You can then see what happens if you add a `panic!` to your code:
|
||||
|
||||
``` rust
|
||||
fn main() -> ! {
|
||||
panic!("test-panic");
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
Open On-Chip Debugger 0.10.0
|
||||
Licensed under GNU GPL v2
|
||||
For bug reports, read
|
||||
http://openocd.org/doc/doxygen/bugs.html
|
||||
panicked at 'test-panic', src/hello-world/src/main.rs:27:5
|
||||
```
|
||||
|
||||
## stdout
|
||||
|
||||
Finally, this is how to write to stdout, although writing to stderr is just as easy.
|
||||
|
||||
``` rust
|
||||
extern crate cortex_m_semihosting as sh;
|
||||
use core::fmt::Write;
|
||||
use sh::hio;
|
||||
// -- snip --
|
||||
let mut stdout = hio::hstdout().unwrap();
|
||||
stdout.write_str("semitest\n\r").unwrap();
|
||||
```
|
||||
|
||||
And that's it!
|
||||
38
src/hello-world/02.00.UART.md
Normal file
38
src/hello-world/02.00.UART.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# Serial communication
|
||||
|
||||
The micro:bit has a perihperal called UART,
|
||||
a Universal Asynchronous Receiver/Transmitter.
|
||||
This is a form of serial communication, data is transferred serially,
|
||||
i.e. one bit at a a time.
|
||||
It is asynchronous communication, and there is no clock signal to dictate the bitrate,
|
||||
intead this is agreed upon beforehand.
|
||||
The protocol has frames consisting of a start bit, data bits, parity bits, and stop bits.
|
||||
We will be using 8 bits per frame: 1 start, 6 data and 1 stop.
|
||||
The data rate is called the _baud rate_, and we will use 115200bps.
|
||||
|
||||
## USB
|
||||
|
||||
The micro:bit allows us to transmit and receive this serial communication over USB
|
||||
with no additional hardware,
|
||||
along with our flashing and debugging activities.
|
||||
|
||||
## Tooling
|
||||
|
||||
To read and write to the serial bus from your computer, you will need to configure your tooling:
|
||||
|
||||
- [*nix](hello-world/02.01.NIX.html)
|
||||
- [Windows](hello-world/02.02.WINDOWS.html)
|
||||
|
||||
## Code
|
||||
|
||||
``` rust
|
||||
// -- snip --
|
||||
if let Some(p) = microbit::Peripherals::take() {
|
||||
let mut gpio = p.GPIO.split();
|
||||
// Configure RX and TX pins accordingly
|
||||
let tx = gpio.pin24.into_push_pull_output().downgrade();
|
||||
let rx = gpio.pin25.into_floating_input().downgrade();
|
||||
let (mut tx, _) = serial::Serial::uart0(p.UART0, tx, rx, BAUD115200).split();
|
||||
let _ = write!(tx, "\n\rStarting!\n\r");
|
||||
}
|
||||
```
|
||||
83
src/hello-world/02.01.NIX.md
Normal file
83
src/hello-world/02.01.NIX.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# *nix tooling
|
||||
|
||||
Connect the serial module to your laptop and let's find out what name the OS assigned to it.
|
||||
|
||||
``` console
|
||||
$ dmesg | grep -i tty
|
||||
(..)
|
||||
[ +0.000155] usb 3-2: FTDI USB Serial Device converter now attached to ttyUSB0
|
||||
```
|
||||
|
||||
> **NOTE** On macs, the USB device will named like this: `cu.usbserial-*`. Adjust the following
|
||||
> commands accordingly!
|
||||
|
||||
But what's this `ttyUSB0` thing? It's a file of course! Everything is a file in *nix:
|
||||
|
||||
``` console
|
||||
$ ls -l /dev/ttyUSB0
|
||||
crw-rw---- 1 root uucp 188, 0 Oct 27 00:00 /dev/ttyUSB0
|
||||
```
|
||||
|
||||
You can send out data by simply writing to this file:
|
||||
|
||||
``` console
|
||||
$ echo 'Hello, world!' > /dev/ttyUSB0
|
||||
```
|
||||
|
||||
You should see the TX (red) LED on the serial module blink, just once and very fast!
|
||||
|
||||
## minicom
|
||||
|
||||
Dealing with serial devices using `echo` is far from ergonomic. So, we'll use the program `minicom`
|
||||
to interact with the serial device using the keyboard.
|
||||
|
||||
We must configure `minicom` before we use it. There are quite a few ways to do that but we'll use a
|
||||
`.minirc.dfl` file in the home directory. Create a file in `~/.minirc.dfl` with the following
|
||||
contents:
|
||||
|
||||
``` console
|
||||
$ cat ~/.minirc.dfl
|
||||
pu baudrate 115200
|
||||
pu bits 8
|
||||
pu parity N
|
||||
pu stopbits 1
|
||||
pu rtscts No
|
||||
pu xonxoff No
|
||||
```
|
||||
|
||||
> **NOTE** Make sure this file ends in a newline! Otherwise, `minicom` will fail to read it.
|
||||
|
||||
That file should be straightforward to read (except for the last two lines), but nonetheless let's
|
||||
go over it line by line:
|
||||
|
||||
- `pu baudrate 115200`. Sets baud rate to 115200 bps.
|
||||
- `pu bits 8`. 8 bits per frame.
|
||||
- `pu parity N`. No parity check.
|
||||
- `pu stopbits 1`. 1 stop bit.
|
||||
- `pu rtscts No`. No hardware control flow.
|
||||
- `pu xonxoff No`. No software control flow.
|
||||
|
||||
Once that's in place. We can launch `minicom`
|
||||
|
||||
``` console
|
||||
$ minicom -D /dev/ttyUSB0 -b 115200
|
||||
```
|
||||
|
||||
This tells `minicom` to open the serial device at `/dev/ttyUSB0` and set its baud rate to 115200.
|
||||
A text-based user interface (TUI) will pop out.
|
||||
|
||||
You can now send data using the keyboard! Go ahead and type something.
|
||||
Note that the TUI *won't* echo back what you type (nothing will happen when you type)
|
||||
but you'll see TX (red) LED on the serial module blink with each keystroke.
|
||||
|
||||
## `minicom` commands
|
||||
|
||||
`minicom` exposes commands via keyboard shortcuts. On Linux, the shortcuts start with `Ctrl+A`. On
|
||||
mac, the shortcuts start with the `Meta` key. Some useful commands below:
|
||||
|
||||
- `Ctrl+A` + `Z`. Minicom Command Summary
|
||||
- `Ctrl+A` + `C`. Clear the screen
|
||||
- `Ctrl+A` + `X`. Exit and reset
|
||||
- `Ctrl+A` + `Q`. Quit with no reset
|
||||
|
||||
> **NOTE** mac users: In the above commands, replace `Ctrl+A` with `Meta`.
|
||||
43
src/hello-world/02.02.WINDOWS.md
Normal file
43
src/hello-world/02.02.WINDOWS.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# Windows tooling
|
||||
|
||||
Before plugging the Serial module, run the following command on the terminal:
|
||||
|
||||
``` console
|
||||
$ mode
|
||||
```
|
||||
|
||||
It will print a list of devices that are connected to your laptop. The ones that start with `COM` in
|
||||
their names are serial devices. This is the kind of device we'll be working with. Take note of all
|
||||
the `COM` *ports* `mode` outputs *before* plugging the serial module.
|
||||
|
||||
Now, plug the Serial module and run the `mode` command again. You should see a new `COM` port appear
|
||||
on the list. That's the COM port assigned to the serial module.
|
||||
|
||||
Now launch `putty`. A GUI will pop out.
|
||||
|
||||
<p align="center">
|
||||
<img title="PuTTY settings" src="assets/putty-settings.png">
|
||||
</p>
|
||||
|
||||
On the starter screen, which should have the "Session" category open, pick "Serial" as the
|
||||
"Connection type". On the "Serial line" field enter the `COM` device you got on the previous step,
|
||||
for example `COM3`.
|
||||
|
||||
Next, pick the "Connection/Serial" category from the menu on the left. On this new view, make sure
|
||||
that the serial port is configured as follows:
|
||||
|
||||
- "Speed (baud)": 115200
|
||||
- "Data bits": 8
|
||||
- "Stop bits": 1
|
||||
- "Parity": None
|
||||
- "Flow control": None
|
||||
|
||||
Finally, click the Open button. A console will show up now:
|
||||
|
||||
<p align="center">
|
||||
<img title="PuTTY console" src="assets/putty-console.png">
|
||||
</p>
|
||||
|
||||
If you type on this console, the TX (red) LED on the Serial module should blink. Each key stroke
|
||||
should make the LED blink once. Note that the console won't echo back what you type so the screen
|
||||
will remain blank.
|
||||
37
src/hello-world/03.00.LED.md
Normal file
37
src/hello-world/03.00.LED.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# 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.
|
||||
|
||||
The 5x5 array of LEDs are actually wired up as a 3x9 array (3 rows by 9 columns), 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.
|
||||
@@ -1,4 +1,4 @@
|
||||
# LED
|
||||
# Solution
|
||||
|
||||
This is my solution:
|
||||
|
||||
@@ -41,5 +41,7 @@ fn default_handler(irqn: i16) {
|
||||
|
||||
It is worth noting that pin4 starts low, so does not need to be explicitly set low.
|
||||
|
||||
You now know enough to start playing around with the LED display,
|
||||
but before you do, you should know to debug your Rust code on the micro:bit.
|
||||
You now know enough to start playing around with the LED display and the GPIO in general.
|
||||
Before you do,
|
||||
you should know that the microbit crate already includes an abstraction for the LED display,
|
||||
and how this is implemented is demonstrated in the [LED display chapter](display/00.00.README.html)
|
||||
9
src/hello-world/Cargo.toml
Normal file
9
src/hello-world/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "hello"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
cortex-m-rt="~0.5"
|
||||
cortex-m-semihosting=""
|
||||
panic-semihosting = "~0.3"
|
||||
microbit="~0.5"
|
||||
42
src/hello-world/src/main.rs
Normal file
42
src/hello-world/src/main.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate panic_semihosting;
|
||||
extern crate cortex_m_rt as rt;
|
||||
extern crate cortex_m_semihosting as sh;
|
||||
|
||||
#[macro_use(entry, exception)]
|
||||
extern crate microbit;
|
||||
|
||||
use core::fmt::Write;
|
||||
use rt::ExceptionFrame;
|
||||
use sh::hio;
|
||||
|
||||
exception!(HardFault, hard_fault);
|
||||
|
||||
fn hard_fault(ef: &ExceptionFrame) -> ! {
|
||||
panic!("{:#?}", ef);
|
||||
}
|
||||
|
||||
exception!(*, default_handler);
|
||||
|
||||
fn default_handler(irqn: i16) {
|
||||
panic!("Unhandled exception (IRQn = {})", irqn);
|
||||
}
|
||||
|
||||
entry!(main);
|
||||
fn main() -> ! {
|
||||
let mut stdout = hio::hstdout().unwrap();
|
||||
stdout.write_str("semihosting test\n\r").unwrap();
|
||||
|
||||
if let Some(p) = microbit::Peripherals::take() {
|
||||
let mut gpio = p.GPIO.split();
|
||||
// Configure RX and TX pins accordingly
|
||||
let tx = gpio.pin24.into_push_pull_output().downgrade();
|
||||
let rx = gpio.pin25.into_floating_input().downgrade();
|
||||
let (mut tx, _) = serial::Serial::uart0(p.UART0, tx, rx, BAUD115200).split();
|
||||
let _ = write!(tx, "\n\rserial test\n\r");
|
||||
}
|
||||
|
||||
panic!("test-panic");
|
||||
}
|
||||
Reference in New Issue
Block a user