@@ -3,6 +3,8 @@ members = [
|
|||||||
"src/getting-started",
|
"src/getting-started",
|
||||||
"src/hello-world",
|
"src/hello-world",
|
||||||
"src/display",
|
"src/display",
|
||||||
|
"src/microbit",
|
||||||
|
"src/serial",
|
||||||
]
|
]
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
> Learn embedded software in Rust on the micro:bit
|
> Learn embedded software in Rust on the micro:bit
|
||||||
|
|
||||||
|
[**droogmic.github.io/microrust**](https://droogmic.github.io/microrust/)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
The documentation is licensed under
|
The documentation is licensed under
|
||||||
@@ -23,6 +25,7 @@ at your option.
|
|||||||
|
|
||||||
### Contribution
|
### Contribution
|
||||||
|
|
||||||
|
Contribution is welcome by either submitting an issue or a pull request.
|
||||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||||
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
|
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
|
||||||
licensed as above, without any additional terms or conditions.
|
licensed as above, without any additional terms or conditions.
|
||||||
|
|||||||
@@ -5,14 +5,34 @@
|
|||||||
[Rust]: https://www.rust-lang.org/
|
[Rust]: https://www.rust-lang.org/
|
||||||
[microbit]: https://microbit.org/
|
[microbit]: https://microbit.org/
|
||||||
|
|
||||||
This book is an introductory course on microcontroller-based embedded systems that uses Rust as the
|
This book is an introductory course on microcontroller-based embedded systems
|
||||||
teaching language (rather than the usual C/C++), and the micro:bit as target system.
|
that uses Rust as the teaching language (rather than the usual C/C++),
|
||||||
|
and the micro:bit as the target system.
|
||||||
|
|
||||||
|
## Approach
|
||||||
|
|
||||||
|
- Beginner friendly.
|
||||||
|
No previous experience with microcontrollers or embedded systems is required.
|
||||||
|
|
||||||
|
- Hands on.
|
||||||
|
*You* will be doing most of the work here.
|
||||||
|
When possible, pages will end on a problem for you to solve, with the solution on the next page.
|
||||||
|
There are plenty of exercises to put the theory into practice.
|
||||||
|
|
||||||
|
- Standard.
|
||||||
|
We'll make plenty use of standard tooling and processes to ease development
|
||||||
|
so you can apply the skills learnt to any Rust embedded project.
|
||||||
|
Fixing compiler errors, debugging with GDB, and logging will be introduced early on.
|
||||||
|
Using LEDs as a debugging mechanism has no place here.
|
||||||
|
|
||||||
## Scope
|
## Scope
|
||||||
|
|
||||||
The following topics will be covered:
|
The following topics are covered in the core chapters:
|
||||||
|
|
||||||
- How to write, build, flash and debug an embedded program.
|
- How to write, build, flash and debug an embedded program.
|
||||||
|
- Basic operation of a GPIO, ubiquitous in microcontrollers.
|
||||||
|
|
||||||
|
The rest of the chapters are independent, only requiring the core knowledge:
|
||||||
|
|
||||||
- Functionality ("peripherals") commonly found in microcontrollers:
|
- Functionality ("peripherals") commonly found in microcontrollers:
|
||||||
- Digital input and output, including buttons and LEDs
|
- Digital input and output, including buttons and LEDs
|
||||||
@@ -26,20 +46,6 @@ The following topics will be covered:
|
|||||||
<!-- - Control systems concepts: sensors, calibration, digital filters, actuators, open loop control,
|
<!-- - Control systems concepts: sensors, calibration, digital filters, actuators, open loop control,
|
||||||
closed loop control, etc. -->
|
closed loop control, etc. -->
|
||||||
|
|
||||||
## Approach
|
|
||||||
|
|
||||||
- Beginner friendly.
|
|
||||||
No previous experience with microcontrollers or embedded systems is required.
|
|
||||||
|
|
||||||
- Hands on.
|
|
||||||
*You* will be doing most of the work here.
|
|
||||||
When possible, pages will end on a problem for you to solve, with the solution on the next page.
|
|
||||||
Plenty of exercises to put the theory into practice.
|
|
||||||
|
|
||||||
- Standard. We'll make plenty use of standard tooling and processes to ease development.
|
|
||||||
Fixing compiler errors, debugging with GDB, and logging will be introduced early on.
|
|
||||||
Using LEDs as a debugging mechanism has no place here.
|
|
||||||
|
|
||||||
## Non-goals
|
## Non-goals
|
||||||
|
|
||||||
What's out of scope for this book:
|
What's out of scope for this book:
|
||||||
|
|||||||
@@ -23,10 +23,29 @@
|
|||||||
- [Serial communication](hello-world/02.00.UART.md)
|
- [Serial communication](hello-world/02.00.UART.md)
|
||||||
- [*nix](hello-world/02.01.NIX.md)
|
- [*nix](hello-world/02.01.NIX.md)
|
||||||
- [Windows](hello-world/02.02.WINDOWS.md)
|
- [Windows](hello-world/02.02.WINDOWS.md)
|
||||||
- [LED](hello-world/03.00.LED.md)
|
- [GPIO and LEDs](hello-world/03.00.LED.md)
|
||||||
- [Solution](hello-world/03.01.SOLUTION.md)
|
- [Solution](hello-world/03.01.SOLUTION.md)
|
||||||
|
|
||||||
- [Choose your own adventure](choice/00.00.README.md)
|
- [Choose Your Own Adventure](choice/00.00.README.md)
|
||||||
|
|
||||||
|
- [WIP - micro:bit HAL basics](microbit/00.00.README.md)
|
||||||
|
- [WIP - Buttons](microbit/01.00.BUTTONS.md)
|
||||||
|
- [WIP - Delays](microbit/02.00.DELAY.md)
|
||||||
|
- [WIP - Display](microbit/03.00.DISPLAY.md)
|
||||||
|
|
||||||
|
- [WIP - Serial UART](serial/00.00.README.md)
|
||||||
|
- [Echo Server](serial/01.00.ECHO.md)
|
||||||
|
- [Theory](serial/01.01.THEORY.md)
|
||||||
|
- [Solution](serial/01.02.ECHO.md)
|
||||||
|
- [Exercises](serial/02.00.md)
|
||||||
|
- [Reverse echo](serial/02.01.md)
|
||||||
|
- [Solution](serial/02.01.SOLUTION.md)
|
||||||
|
- [Countdown](serial/02.02.md)
|
||||||
|
- [Solution](serial/02.02.SOLUTION.md)
|
||||||
|
- [Display echo](serial/02.03.md)
|
||||||
|
- [Solution](serial/02.03.SOLUTION.md)
|
||||||
|
- [Quiz](serial/02.04.md)
|
||||||
|
- [Solution](serial/02.04.SOLUTION.md)
|
||||||
|
|
||||||
- [LED display](display/00.00.README.md)
|
- [LED display](display/00.00.README.md)
|
||||||
- [Theory](display/01.00.THEORY.md)
|
- [Theory](display/01.00.THEORY.md)
|
||||||
@@ -43,7 +62,7 @@
|
|||||||
|
|
||||||
- [WIP - Real time](rtfm/00.00.README.md)
|
- [WIP - Real time](rtfm/00.00.README.md)
|
||||||
|
|
||||||
- [WIP - HAL](hal/00.00.README.md)
|
- [WIP - Creating a HAL](hal/00.00.README.md)
|
||||||
<!-- - [Registers](hal/01.00.REGISTERS.md)
|
<!-- - [Registers](hal/01.00.REGISTERS.md)
|
||||||
- [Peripherals](hal/01.00.PERIPHERALS.md)
|
- [Peripherals](hal/01.00.PERIPHERALS.md)
|
||||||
- [Clocks and timers](hal/02.00.DELAY.md) -->
|
- [Clocks and timers](hal/02.00.DELAY.md) -->
|
||||||
@@ -53,14 +72,3 @@
|
|||||||
[GDB cheatsheet](appendix/gdb.md)
|
[GDB cheatsheet](appendix/gdb.md)
|
||||||
|
|
||||||
[General troubleshooting](appendix/troubleshooting.md)
|
[General troubleshooting](appendix/troubleshooting.md)
|
||||||
|
|
||||||
<!-- - [LED roulette](05-led-roulette/README.md)
|
|
||||||
- [Build it](05-led-roulette/build-it.md)
|
|
||||||
- [Flash it](05-led-roulette/flash-it.md)
|
|
||||||
- [Debug it](05-led-roulette/debug-it.md)
|
|
||||||
- [The `led` and `delay` abstractions](05-led-roulette/the-led-and-delay-abstractions.md)
|
|
||||||
- [The challenge](05-led-roulette/the-challenge.md)
|
|
||||||
- [My solution](05-led-roulette/my-solution.md) -->
|
|
||||||
|
|
||||||
<!-- - [Hello, world!](06-hello-world/README.md)
|
|
||||||
- [`panic!`](06-hello-world/panic.md) -->
|
|
||||||
|
|||||||
@@ -3,11 +3,9 @@
|
|||||||
At this point of the book,
|
At this point of the book,
|
||||||
you know the basics to get started with embedded development with Rust.
|
you know the basics to get started with embedded development with Rust.
|
||||||
|
|
||||||
The following chapters are all independent of each other.
|
The following chapters are more independent of each other, and can be done in any order;
|
||||||
This means they can be done in any order,
|
the only required knowledge is found in the chapters before this.
|
||||||
and the only required knowledge is found in the chapters before this.
|
If attempting the exerises, it is best to follow the book in order to pace the difficulty.
|
||||||
|
|
||||||
Feel free to either follow the rest of the book in order, or choose a chapter which interests you.
|
> A large portion of this book is still unfinished and I would love your support.
|
||||||
|
|
||||||
> A large portion of this book is still unfinished and we would love your support.
|
|
||||||
> Please submit an issue to request a new section, and a pull request to add a section.
|
> Please submit an issue to request a new section, and a pull request to add a section.
|
||||||
@@ -17,7 +17,7 @@ We need to give OCD the name of the interfaces we are using:
|
|||||||
|
|
||||||
``` console
|
``` console
|
||||||
$ # All
|
$ # All
|
||||||
$ # Windows: remember that you need an extra `-s %PATH_TO_OPENOCD%\share\scripts`
|
$ # Windows: remember that you need an extra `-s %PATH_TO_OPENOCD%\<version>\scripts`
|
||||||
$ openocd -f interface/cmsis-dap.cfg -f target/nrf51.cfg
|
$ openocd -f interface/cmsis-dap.cfg -f target/nrf51.cfg
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ Reading symbols from target/thumbv6m-none-eabi/debug/rustled...done.
|
|||||||
This only opens a GDB shell. To actually connect to the OpenOCD GDB server, use the following
|
This only opens a GDB shell. To actually connect to the OpenOCD GDB server, use the following
|
||||||
command within the GDB shell:
|
command within the GDB shell:
|
||||||
|
|
||||||
```
|
``` gdb
|
||||||
(gdb) target remote :3333
|
(gdb) target remote :3333
|
||||||
Remote debugging using :3333
|
Remote debugging using :3333
|
||||||
0x00000000 in ?? ()
|
0x00000000 in ?? ()
|
||||||
@@ -98,7 +98,7 @@ After entering this command, you'll see new output in the OpenOCD terminal:
|
|||||||
|
|
||||||
Almost there. To flash the device, we'll use the `load` command inside the GDB shell:
|
Almost there. To flash the device, we'll use the `load` command inside the GDB shell:
|
||||||
|
|
||||||
```
|
``` gdb
|
||||||
(gdb) load
|
(gdb) load
|
||||||
Loading section .vector_table, size 0x188 lma 0x8000000
|
Loading section .vector_table, size 0x188 lma 0x8000000
|
||||||
Loading section .text, size 0x38a lma 0x8000188
|
Loading section .text, size 0x38a lma 0x8000188
|
||||||
@@ -133,7 +133,7 @@ And that's it. You'll also see new output in the OpenOCD terminal.
|
|||||||
|
|
||||||
Our program is loaded, we can now run it!
|
Our program is loaded, we can now run it!
|
||||||
|
|
||||||
```
|
``` gdb
|
||||||
(gdb) continue
|
(gdb) continue
|
||||||
Continuing.
|
Continuing.
|
||||||
```
|
```
|
||||||
@@ -148,7 +148,8 @@ 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:
|
This will automate the last few steps so we don't need to repeatedly do the same actions in gdb:
|
||||||
|
|
||||||
`.gdbinit`
|
`.gdbinit`
|
||||||
```
|
|
||||||
|
``` gdbinit
|
||||||
# Connects GDB to OpenOCD server port
|
# Connects GDB to OpenOCD server port
|
||||||
target remote :3333
|
target remote :3333
|
||||||
# (optional) Unmangle function names when debugging
|
# (optional) Unmangle function names when debugging
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
# Hello world
|
# Hello world
|
||||||
|
|
||||||
In this chapter, we will discuss the basic I/O of embedded development in rust.
|
In this chapter, we will discuss the basic I/O of embedded development in rust.
|
||||||
|
|
||||||
|
After this chapter,you should have all the neccesary basic knowledge to do embedded development in Rust,
|
||||||
|
with anything remaining being solution specific.
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ panicked at 'test-panic', src/hello-world/src/main.rs:27:5
|
|||||||
|
|
||||||
## stdout
|
## stdout
|
||||||
|
|
||||||
Finally, this is how to write to stdout, although writing to stderr is just as easy.
|
Finally, this is how to write to stdout.
|
||||||
|
|
||||||
``` rust
|
``` rust
|
||||||
extern crate cortex_m_semihosting as sh;
|
extern crate cortex_m_semihosting as sh;
|
||||||
@@ -76,4 +76,4 @@ use sh::hio;
|
|||||||
writeln!(hio::hstdout().unwrap(), "Init").unwrap();
|
writeln!(hio::hstdout().unwrap(), "Init").unwrap();
|
||||||
```
|
```
|
||||||
|
|
||||||
And that's it!
|
Writing to stderr is just as easy.
|
||||||
|
|||||||
@@ -12,9 +12,7 @@ The data rate is called the _baud rate_, and we will use 115200bps.
|
|||||||
|
|
||||||
## USB
|
## USB
|
||||||
|
|
||||||
The micro:bit allows us to transmit and receive this serial communication over USB
|
The micro:bit allows us to transmit and receive this serial communication over USB with no additional hardware.
|
||||||
with no additional hardware,
|
|
||||||
along with our flashing and debugging activities.
|
|
||||||
|
|
||||||
## Tooling
|
## Tooling
|
||||||
|
|
||||||
@@ -35,19 +33,19 @@ if let Some(p) = microbit::Peripherals::take() {
|
|||||||
// Configure RX and TX pins accordingly
|
// Configure RX and TX pins accordingly
|
||||||
let tx = gpio.pin24.into_push_pull_output().downgrade();
|
let tx = gpio.pin24.into_push_pull_output().downgrade();
|
||||||
let rx = gpio.pin25.into_floating_input().downgrade();
|
let rx = gpio.pin25.into_floating_input().downgrade();
|
||||||
|
// Configure serial communication
|
||||||
let (mut tx, _) = serial::Serial::uart0(p.UART0, tx, rx, BAUD115200).split();
|
let (mut tx, _) = serial::Serial::uart0(p.UART0, tx, rx, BAUD115200).split();
|
||||||
|
// Write string with newline and carriage return
|
||||||
|
// This could also be a format string
|
||||||
let _ = write!(tx, "serial test\n\r");
|
let _ = write!(tx, "serial test\n\r");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
In minicom/PuTTY you should see:
|
||||||
|
|
||||||
```
|
```
|
||||||
Welcome to minicom 2.7.1
|
|
||||||
|
|
||||||
OPTIONS: I18n
|
|
||||||
Compiled on Jun 5 2018, 10:54:41.
|
|
||||||
Port /dev/ttyACM0, 19:50:57
|
|
||||||
|
|
||||||
Press CTRL-A Z for help on special keys
|
|
||||||
|
|
||||||
serial test
|
serial test
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This is a very simple introduction to using the UART as one way serial logging.
|
||||||
|
The chapter on UART serial communication goes into much more detail.
|
||||||
|
|||||||
@@ -66,6 +66,17 @@ $ minicom -D /dev/ttyUSB0 -b 115200
|
|||||||
This tells `minicom` to open the serial device at `/dev/ttyUSB0` and set its baud rate to 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.
|
A text-based user interface (TUI) will pop out.
|
||||||
|
|
||||||
|
```
|
||||||
|
Welcome to minicom 2.7.1
|
||||||
|
|
||||||
|
OPTIONS: I18n
|
||||||
|
Compiled on Jun 5 2018, 10:54:41.
|
||||||
|
Port /dev/ttyACM0, 19:50:57
|
||||||
|
|
||||||
|
Press CTRL-A Z for help on special keys
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
You can now send data using the keyboard! Go ahead and type something.
|
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)
|
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.
|
but you'll see TX (red) LED on the serial module blink with each keystroke.
|
||||||
|
|||||||
@@ -1,17 +1,39 @@
|
|||||||
# LED
|
# GPIO and LEDs
|
||||||
|
|
||||||
|
## GPIO
|
||||||
|
|
||||||
|
> GPIO: General purpose input-output
|
||||||
|
|
||||||
|
The GPIO is a block of pins found on nearly all microcontrollers.
|
||||||
|
As the name implies, they are general-purpose, configureable, analog or digital, input or output, electrical pins.
|
||||||
|
Exactly what features each pin has on a given microcontroller will require looking at a datasheet.
|
||||||
|
|
||||||
|
> Analog vs Digital: Analog signals carry data in their amplitude as they continuously vary over time,
|
||||||
|
> whereas digital signals have a fixed rate and fixed amplitudes. Digital signals are usually just 0 or 1, i.e. 0V or 3.3V
|
||||||
|
|
||||||
|
## LED
|
||||||
|
|
||||||
|
> LED: Light emitting diode
|
||||||
|
|
||||||
Let us now turn on an LED! But how?
|
Let us now turn on an LED! But how?
|
||||||
|
|
||||||
Well, first we should look at the [documentation of our crate][microbit],
|
Many integrated periperals like LEDs and buttons are already connected to certain GPIO pins,
|
||||||
|
so lighting up an LED can be as simple as configuring a GPIO pin to be a digital output.
|
||||||
|
|
||||||
|
First we should look at the [documentation of our crate][microbit],
|
||||||
and you should be able to figure out how to get access to the gpio,
|
and you should be able to figure out how to get access to the gpio,
|
||||||
and set individual pins high and low:
|
and set individual pins high and low:
|
||||||
|
|
||||||
[microbit]: https://docs.rs/microbit/0.5.1/microbit/
|
[microbit]: https://docs.rs/microbit/0.5.1/microbit/
|
||||||
|
|
||||||
``` rust
|
``` rust
|
||||||
|
// This takes singleton ownership of the micro:bit's peripherals
|
||||||
if let Some(p) = microbit::Peripherals::take() {
|
if let Some(p) = microbit::Peripherals::take() {
|
||||||
|
// Take the micro:bit's GPIO
|
||||||
let mut gpio = p.GPIO.split();
|
let mut gpio = p.GPIO.split();
|
||||||
|
// Take pin 1 of the GPIO, and configure it as a digital output
|
||||||
let mut pin1 = gpio.pin1.into_push_pull_output();
|
let mut pin1 = gpio.pin1.into_push_pull_output();
|
||||||
|
// Set pin 1 high
|
||||||
pin1.set_high();
|
pin1.set_high();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -22,12 +44,15 @@ On the first sheet you should find a diagram with a grid of numbered LEDs.
|
|||||||
|
|
||||||
> If you do not know much about electronics:
|
> If you do not know much about electronics:
|
||||||
> Each row and column (labelled ROW and COL) represent a GPIO output pin.
|
> Each row and column (labelled ROW and COL) represent a GPIO output pin.
|
||||||
> The components labelled LED are light emitting diodes.
|
> The components labelled are LEDs.
|
||||||
> LEDs only let current flow one way, and only emit light when current is flowing.
|
> 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,
|
> 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;
|
> the LED at the point that they cross will have a potential difference across it;
|
||||||
> current will flow and it will light up.
|
> current will flow and it will light up.
|
||||||
|
|
||||||
|
As you can see, the micro:bit's display LEDs are a bit more complicated than being connected to a single pin.
|
||||||
|
Each LED is connected to 2 pins, where one needs to be high, and the other low for the LED to light up.
|
||||||
|
|
||||||
The 5x5 array of LEDs are actually wired up as a 3x9 array (3 rows by 9 columns), with 2 missing.
|
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.
|
This is usually done to make the circuit design easier.
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ fn main() -> ! {
|
|||||||
let _ = gpio.pin4.into_push_pull_output();
|
let _ = gpio.pin4.into_push_pull_output();
|
||||||
// Set row high (column starts low)
|
// Set row high (column starts low)
|
||||||
led.set_high();
|
led.set_high();
|
||||||
|
// Write string with newline and carriage return
|
||||||
write!(tx, "serial - LED on\r\n");
|
write!(tx, "serial - LED on\r\n");
|
||||||
}
|
}
|
||||||
panic!("End");
|
panic!("End");
|
||||||
|
|||||||
12
src/serial/.gdbinit
Normal file
12
src/serial/.gdbinit
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Connects GDB to OpenOCD server port
|
||||||
|
target remote :3333
|
||||||
|
# (optional) Unmangle function names when debugging
|
||||||
|
set print asm-demangle on
|
||||||
|
# Enable semihosting
|
||||||
|
monitor arm semihosting enable
|
||||||
|
# Load your program, breaks at entry
|
||||||
|
load
|
||||||
|
# (optional) Add breakpoint at function
|
||||||
|
break serial::main
|
||||||
|
# Continue with execution
|
||||||
|
continue
|
||||||
9
src/serial/00.00.README.md
Normal file
9
src/serial/00.00.README.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# WIP - UART serial server
|
||||||
|
|
||||||
|
In the first section of this book we saw how to do a simple debug print using serial.
|
||||||
|
This is useful for logging and debugging, but does not cover the full potential of the UART peripheral.
|
||||||
|
|
||||||
|
## Input and output
|
||||||
|
|
||||||
|
The simultaneous input and output capabilities of the UART allow for both your computer and the micro:bit to act as a server.
|
||||||
|
They can receive a transmission, process it, perform some action, and send a response.
|
||||||
19
src/serial/01.00.ECHO.md
Normal file
19
src/serial/01.00.ECHO.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Echo Server
|
||||||
|
|
||||||
|
An echo server, is probably the simplest server we could make.
|
||||||
|
It should receive a message, and echo it back to the sender.
|
||||||
|
|
||||||
|
We earlier said that minicom/PuTTY would transmit any keystrokes we send,
|
||||||
|
and display and data received,
|
||||||
|
so the end result should be the familiar experience of typing and seeing the letters typed appear as expected.
|
||||||
|
|
||||||
|
## Flow
|
||||||
|
|
||||||
|
1. The character `a` is typed
|
||||||
|
2. minicom/PuTTY encodes the `a` character's unicode code point (`097` in decimal) as a word in a serial frame
|
||||||
|
3. The frame is transmitted to the micro:bit over USB
|
||||||
|
4. The micro:bit software decodes the frame to get the word
|
||||||
|
5. The microbit software re-encodes the word to get a (new but identical) frame
|
||||||
|
6. The frame is transmitted to the computer over USB
|
||||||
|
7. minicom/PuTTY decodes the frame's word as a unicode code point
|
||||||
|
8. The letter `a` is displayed
|
||||||
78
src/serial/01.01.THEORY.md
Normal file
78
src/serial/01.01.THEORY.md
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
# Serial Theory
|
||||||
|
|
||||||
|
The micro:bit core crate implements the embedded_hal::serial::Write and embedded_hal::serial::Read traits for the tx and rx pins respectively.
|
||||||
|
|
||||||
|
## `writeln!` and Carriage Return
|
||||||
|
|
||||||
|
In the introduction page on serial communication, I brushed over this:
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
// Write string with newline and carriage return
|
||||||
|
let _ = write!(tx, "serial test\r\n");
|
||||||
|
```
|
||||||
|
|
||||||
|
A naïve assumption would be to try the seemingly more correct `writeln!` macro:
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
// Write string with newline and carriage return
|
||||||
|
let _ = writeln!(tx, "serial test");
|
||||||
|
```
|
||||||
|
|
||||||
|
This will usually fail to do what is intended,
|
||||||
|
as multiple writes will only print one line in PuTTY,
|
||||||
|
and produce the following in minicom:
|
||||||
|
|
||||||
|
```
|
||||||
|
serial test
|
||||||
|
serial test
|
||||||
|
serial test
|
||||||
|
serial test
|
||||||
|
```
|
||||||
|
|
||||||
|
Your choices are to either configure minicom and PuTTY appropriately or use `write!` with `\r\n`.
|
||||||
|
|
||||||
|
### Control Characters
|
||||||
|
|
||||||
|
The control characters operate based on a print head, as used in [teleprinters][tty].
|
||||||
|
|
||||||
|
`\r` - Carriage Return - The print head is moves left to the start of the line.
|
||||||
|
`\n` - Line Feed - The print head moves down once to a new line.
|
||||||
|
|
||||||
|
[tty]: https://en.wikipedia.org/wiki/Teleprinter
|
||||||
|
|
||||||
|
### `writeln!` macro
|
||||||
|
|
||||||
|
The `writeln!` macro should append a new line,
|
||||||
|
but he [documentation for core::writeln][doc] says:
|
||||||
|
|
||||||
|
> On all platforms, the newline is the LINE FEED character (\n/U+000A) alone (no additional CARRIAGE RETURN (\r/U+000D).
|
||||||
|
|
||||||
|
[doc]: https://doc.rust-lang.org/core/macro.writeln.html
|
||||||
|
|
||||||
|
### minicom
|
||||||
|
|
||||||
|
CTRL-A + Z will tell you that CTRL-A + U will add a carriage return.
|
||||||
|
This will add a carriage return to a received `\n`
|
||||||
|
|
||||||
|
### PuTTY
|
||||||
|
|
||||||
|
In PuTTY, you can enable enable `Implicit LF in every CR` under Terminal options.
|
||||||
|
|
||||||
|
## Blocking
|
||||||
|
|
||||||
|
Behind the scenes, `embedded_hal::serial` uses the nb crate to allow for blocking and non-blocking operation.
|
||||||
|
This is implemented in embedded_hal crates by returning nb::Error::WouldBlock
|
||||||
|
when a read or write action cannot be performed immediately.
|
||||||
|
In this chapter, we will only be using read and write as simple blocking calls.
|
||||||
|
|
||||||
|
### `block!`
|
||||||
|
|
||||||
|
The `block!` macro provided by the crate continuously calls the expression
|
||||||
|
contained until it no longer returns Error::WouldBlock.
|
||||||
|
|
||||||
|
## Tx - `embedded_hal::serial::Write` or `core::fmt::Write`
|
||||||
|
|
||||||
|
The `write!` and `writeln!` macros call `write_str` of the `core::fmt::Write` trait which is implemented for Tx.
|
||||||
|
`write_str` is implemented as a blocking call to `write` of the `embedded_hal::serial::Write` trait.
|
||||||
|
|
||||||
|
This means `write!(tx, "a")` is equivalent to `block!(tx.write(b'a'))`.
|
||||||
3
src/serial/01.02.ECHO.md
Normal file
3
src/serial/01.02.ECHO.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Echo Solution
|
||||||
|
|
||||||
|
{{#include src/main.rs}}
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
# Windows
|
# Windows
|
||||||
|
|
||||||
> UNTESTED: please submit an issue if you can confirm this works.
|
|
||||||
|
|
||||||
## `arm-none-eabi-*`
|
## `arm-none-eabi-*`
|
||||||
|
|
||||||
ARM provides `.exe` installers for Windows. Grab one from [here][gcc], and follow the instructions.
|
ARM provides `.exe` installers for Windows. Grab one from [here][gcc], and follow the instructions.
|
||||||
|
|||||||
Reference in New Issue
Block a user