Add serial examples

This commit is contained in:
Michael Droogleever
2018-08-18 23:12:14 +02:00
parent ecdbd8d730
commit b12fb9f827
15 changed files with 421 additions and 9 deletions

View File

@@ -33,7 +33,7 @@
- [Delays](microbit/02.00.DELAY.md) - [Delays](microbit/02.00.DELAY.md)
- [Display](microbit/03.00.DISPLAY.md) - [Display](microbit/03.00.DISPLAY.md)
- [WIP - Serial UART](serial/00.00.README.md) - [Serial UART - Blocking](serial/00.00.README.md)
- [Echo Server](serial/01.00.ECHO.md) - [Echo Server](serial/01.00.ECHO.md)
- [Theory](serial/01.01.THEORY.md) - [Theory](serial/01.01.THEORY.md)
- [Solution](serial/01.02.ECHO.md) - [Solution](serial/01.02.ECHO.md)
@@ -42,8 +42,6 @@
- [Solution](serial/02.01.SOLUTION.md) - [Solution](serial/02.01.SOLUTION.md)
- [Countdown](serial/02.02.md) - [Countdown](serial/02.02.md)
- [Solution](serial/02.02.SOLUTION.md) - [Solution](serial/02.02.SOLUTION.md)
- [Display echo](serial/02.03.md)
- [Solution](serial/02.03.SOLUTION.md)
- [Quiz](serial/02.04.md) - [Quiz](serial/02.04.md)
- [Solution](serial/02.04.SOLUTION.md) - [Solution](serial/02.04.SOLUTION.md)
@@ -58,6 +56,8 @@
- [Multiplexing](display/03.02.MULT.md) - [Multiplexing](display/03.02.MULT.md)
- [Full Solution](display/03.03.FULL.md) - [Full Solution](display/03.03.FULL.md)
- [WIP - Non-blocking](nb/00.00.README.md)
- [WIP - Sensors and I²C](sensors/00.00.README.md) - [WIP - Sensors and I²C](sensors/00.00.README.md)
- [WIP - Real time](rtfm/00.00.README.md) - [WIP - Real time](rtfm/00.00.README.md)

View File

@@ -36,7 +36,7 @@ fn main() -> ! {
let mut gpio = p.GPIO.split(); let mut gpio = p.GPIO.split();
let mut delay = Delay::new(p.TIMER0); let mut delay = Delay::new(p.TIMER0);
// Display // Configure display pins
let row1 = gpio.pin13.into_push_pull_output().downgrade(); let row1 = gpio.pin13.into_push_pull_output().downgrade();
let row2 = gpio.pin14.into_push_pull_output().downgrade(); let row2 = gpio.pin14.into_push_pull_output().downgrade();
let row3 = gpio.pin15.into_push_pull_output().downgrade(); let row3 = gpio.pin15.into_push_pull_output().downgrade();

View File

@@ -7,6 +7,6 @@ monitor arm semihosting enable
# Load your program, breaks at entry # Load your program, breaks at entry
load load
# (optional) Add breakpoint at function # (optional) Add breakpoint at function
break serial::main #break serial::main
# Continue with execution # Continue with execution
continue #continue

6
src/serial/02.00.md Normal file
View File

@@ -0,0 +1,6 @@
# Exercises
- Reverse echo a line of input
- Numerical countdown
- Display echo
- Quiz game

View File

@@ -0,0 +1,8 @@
# Solution
``` rust
{{#include examples/reverse.rs}}
```
I have used an implementation of a vector on the stack, provided by the heapless crate.
After 32 characters (a char is a u8 byte) the heapless vector is full, and an error is shown.

19
src/serial/02.01.md Normal file
View File

@@ -0,0 +1,19 @@
# Reverse Echo
The micro:bit should buffer characters it receives until `\n` or `\r` is received
(the enter key is pressed on the host computer).
The characters should then be printed back in reverse order to the host computer.
The characters may also be echoed like earlier to see what is being typed.
## Flow
1. Letter `a` is typed and transmitted to the micro:bit
2. (optional) The micro:bit retransmits the letter `a` (echo)
3. Letter `b` is typed and transmitted to the micro:bit
4. (optional) The micro:bit retransmits the letter `b` (echo)
5. Enter key is pressed and `\r` is transmitted to the micro:bit
6. Letters `ba` are transmitted from the micro:bit
## Useful crates
+ [Heapless](https://docs.rs/heapless)

View File

@@ -0,0 +1,5 @@
# Solution
``` rust
{{#include examples/countdown.rs}}
```

18
src/serial/02.02.md Normal file
View File

@@ -0,0 +1,18 @@
# Countdown
You should be able to type a number greater than 0, press enter,
and the micro:bit will return a countdown.
```
5
4
3
2
1
```
Feel free to add your own surprise at the end of the countdown
## Useful crates
+ [Heapless](https://docs.rs/heapless)

View File

@@ -0,0 +1,5 @@
# Solution
``` rust
{{#include examples/quiz.rs}}
```

23
src/serial/02.03.md Normal file
View File

@@ -0,0 +1,23 @@
# Quiz
Let us create a simple quiz system.
The quiz will have a quizmaster and 2 contestants;
the quizmaster will operate the host computer,
and the micro:bit's user buttons will be used as buzzers for the 2 contestants.
We have only learnt about blocking operations so far,
so the following cannot be done at the same time:
+ Scan the buttons for presses
+ Operate the LED display
+ Operate the serial UART
For the sake of consistency, let us say that the quiz should have 11 questions.
## Flow
1. Display question number and score.
2. Wait for a contestant to press a button.
3. Print the constestant who buzzed in to the quizmaster's computer.
4. (optional) Display the contestant who buzzed in to the contestants on the micro:bit for 1-2 seconds.
5. Prompt the quizmaster for whether the contestant answered correctly.
6. Record the updated score and repeat from 1, unless someone has won.

View File

@@ -3,6 +3,7 @@ name = "serial"
version = "0.1.0" version = "0.1.0"
[dependencies] [dependencies]
heapless="~0.3"
cortex-m-rt="~0.5" cortex-m-rt="~0.5"
cortex-m-semihosting="" cortex-m-semihosting=""
panic-semihosting = "~0.3" panic-semihosting = "~0.3"

View File

@@ -0,0 +1,86 @@
#![no_std]
#![no_main]
extern crate panic_semihosting;
extern crate cortex_m_rt as rt;
extern crate cortex_m_semihosting as sh;
extern crate heapless;
#[macro_use(entry, exception, block)]
extern crate microbit;
use core::fmt::Write;
use rt::ExceptionFrame;
use sh::hio;
use heapless::{consts, Vec, String};
use microbit::hal::prelude::*;
use microbit::hal::delay::Delay;
use microbit::hal::serial;
use microbit::hal::serial::BAUD115200;
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();
writeln!(stdout, "Start").unwrap();
if let Some(p) = microbit::Peripherals::take() {
// Split GPIO
let mut gpio = p.GPIO.split();
// Create delay provider
let mut delay = Delay::new(p.TIMER0);
// Configure RX and TX pins accordingly
let tx = gpio.pin24.into_push_pull_output().downgrade();
let rx = gpio.pin25.into_floating_input().downgrade();
// Configure serial communication
let (mut tx, mut rx) = serial::Serial::uart0(p.UART0, tx, rx, BAUD115200).split();
writeln!(tx, "Start");
loop {
// A buffer with 32 bytes of capacity
let mut buffer: Vec<u8, consts::U32> = Vec::new();
loop {
// Read
let byte = block!(rx.read()).unwrap();
// Echo
block!(tx.write(byte));
// Carriage return
if byte == b'\r' {
break;
}
// Push to buffer
if buffer.push(byte).is_err() {
// Buffer full
writeln!(tx, "\r\nWarning: buffer full, dumping buffer");
break;
}
}
// Buffer to string
let buf_str = String::from_utf8(buffer).unwrap();
writeln!(tx, "");
match buf_str.parse() {
// Transmit countdown
Ok(buf_int) => {
for i in (1..buf_int).rev() {
delay.delay_ms(1000_u32);
writeln!(tx, "{}", i);
}
// Add post countdown effects here
},
// Transmit parse error
Err(e) => writeln!(tx, "{:?}", e).unwrap(),
}
}
}
panic!("End");
}

View File

@@ -0,0 +1,69 @@
#![no_std]
#![no_main]
extern crate panic_semihosting;
extern crate cortex_m_rt as rt;
extern crate cortex_m_semihosting as sh;
extern crate heapless;
#[macro_use(entry, exception, block)]
extern crate microbit;
use core::fmt::Write;
use rt::ExceptionFrame;
use sh::hio;
use heapless::{consts, Vec, String};
use microbit::hal::prelude::*;
use microbit::hal::delay::Delay;
use microbit::hal::serial;
use microbit::hal::serial::BAUD115200;
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();
writeln!(stdout, "Start").unwrap();
if let Some(p) = microbit::Peripherals::take() {
// Split GPIO
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();
// Configure serial communication
let (mut tx, mut rx) = serial::Serial::uart0(p.UART0, tx, rx, BAUD115200).split();
// Configure display pins
let row1 = gpio.pin13.into_push_pull_output().downgrade();
let row2 = gpio.pin14.into_push_pull_output().downgrade();
let row3 = gpio.pin15.into_push_pull_output().downgrade();
let col1 = gpio.pin4.into_push_pull_output().downgrade();
let col2 = gpio.pin5.into_push_pull_output().downgrade();
let col3 = gpio.pin6.into_push_pull_output().downgrade();
let col4 = gpio.pin7.into_push_pull_output().downgrade();
let col5 = gpio.pin8.into_push_pull_output().downgrade();
let col6 = gpio.pin9.into_push_pull_output().downgrade();
let col7 = gpio.pin10.into_push_pull_output().downgrade();
let col8 = gpio.pin11.into_push_pull_output().downgrade();
let col9 = gpio.pin12.into_push_pull_output().downgrade();
// Configure display
let mut leds = led::Display::new(
row1, row2, row3, col1, col2, col3, col4, col5, col6, col7, col8, col9,
);
writeln!(tx, "Start");
loop {
let val = block!(rx.read()).unwrap();
}
}
panic!("End");
}

146
src/serial/examples/quiz.rs Normal file
View File

@@ -0,0 +1,146 @@
#![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, block)]
extern crate microbit;
use core::fmt::Write;
use rt::ExceptionFrame;
use sh::hio;
use microbit::hal::delay::Delay;
use microbit::hal::prelude::*;
use microbit::hal::serial;
use microbit::hal::serial::BAUD115200;
use microbit::led;
exception!(HardFault, hard_fault);
fn hard_fault(ef: &ExceptionFrame) -> ! {
panic!("{:#?}", ef);
}
exception!(*, default_handler);
fn default_handler(irqn: i16) {
panic!("Unhandled exception (IRQn = {})", irqn);
}
const WINNING_SCORE: u8 = 6;
const QUESTION_COUNT: u8 = 2*WINNING_SCORE - 1;
const LETTER_A: [[u8; 5]; 5] = [
[0, 0, 1, 0, 0],
[0, 1, 0, 1, 0],
[0, 1, 1, 1, 0],
[0, 1, 0, 1, 0],
[0, 1, 0, 1, 0],
];
const LETTER_B: [[u8; 5]; 5] = [
[0, 1, 1, 0, 0],
[0, 1, 0, 1, 0],
[0, 1, 1, 0, 0],
[0, 1, 0, 1, 0],
[0, 1, 1, 0, 0],
];
entry!(main);
fn main() -> ! {
let mut stdout = hio::hstdout().unwrap();
writeln!(stdout, "Start").unwrap();
if let Some(p) = microbit::Peripherals::take() {
// Split GPIO
let mut gpio = p.GPIO.split();
// Configure delay
let mut delay = Delay::new(p.TIMER0);
// Configure RX and TX pins accordingly
let tx = gpio.pin24.into_push_pull_output().downgrade();
let rx = gpio.pin25.into_floating_input().downgrade();
// Configure serial communication
let (mut tx, mut rx) = serial::Serial::uart0(p.UART0, tx, rx, BAUD115200).split();
// Configure display pins
let row1 = gpio.pin13.into_push_pull_output().downgrade();
let row2 = gpio.pin14.into_push_pull_output().downgrade();
let row3 = gpio.pin15.into_push_pull_output().downgrade();
let col1 = gpio.pin4.into_push_pull_output().downgrade();
let col2 = gpio.pin5.into_push_pull_output().downgrade();
let col3 = gpio.pin6.into_push_pull_output().downgrade();
let col4 = gpio.pin7.into_push_pull_output().downgrade();
let col5 = gpio.pin8.into_push_pull_output().downgrade();
let col6 = gpio.pin9.into_push_pull_output().downgrade();
let col7 = gpio.pin10.into_push_pull_output().downgrade();
let col8 = gpio.pin11.into_push_pull_output().downgrade();
let col9 = gpio.pin12.into_push_pull_output().downgrade();
// Configure display
let mut leds = led::Display::new(
row1, row2, row3, col1, col2, col3, col4, col5, col6, col7, col8, col9,
);
// Configure button GPIOs as inputs
let button_a = gpio.pin17.into_floating_input();
let button_b = gpio.pin26.into_floating_input();
writeln!(tx, "Start");
loop {
let mut score_a: u8 = 0;
let mut score_b: u8 = 0;
for n in 0..QUESTION_COUNT {
writeln!(tx, "Question {} - Score {}:{}", n, score_a, score_b);
let mut button_a_low;
let mut button_b_low;
loop {
// Get button states
button_a_low = button_a.is_low();
button_b_low = button_b.is_low();
if button_a_low || button_b_low {
break;
}
}
let letter = match (button_a_low, button_b_low) {
(true, false) => {
writeln!(tx, "A");
LETTER_A
},
(false, true) => {
writeln!(tx, "B");
LETTER_B
},
(true, true) => {
writeln!(tx, "Tie! Next question.");
continue;
},
_ => unreachable!(),
};
leds.display(&mut delay, letter, 1000);
loop {
// Keep asking until y or n is received
write!(tx, "Answer correct? [y/n] ");
let byte = block!(rx.read()).unwrap();
block!(tx.write(byte));
writeln!(tx);
match byte {
b'y' => {
if button_a_low {
score_a += 1;
}
if button_b_low {
score_b += 1;
}
break
},
b'n' => break,
_ => (),
}
}
if score_a >= WINNING_SCORE || score_b >= WINNING_SCORE {
break;
}
}
writeln!(tx, "Final Score {}:{}", score_a, score_b);
}
}
panic!("End");
}

View File

@@ -4,6 +4,7 @@
extern crate panic_semihosting; extern crate panic_semihosting;
extern crate cortex_m_rt as rt; extern crate cortex_m_rt as rt;
extern crate cortex_m_semihosting as sh; extern crate cortex_m_semihosting as sh;
extern crate heapless;
#[macro_use(entry, exception, block)] #[macro_use(entry, exception, block)]
extern crate microbit; extern crate microbit;
@@ -11,6 +12,7 @@ extern crate microbit;
use core::fmt::Write; use core::fmt::Write;
use rt::ExceptionFrame; use rt::ExceptionFrame;
use sh::hio; use sh::hio;
use heapless::{consts, Vec};
use microbit::hal::prelude::*; use microbit::hal::prelude::*;
use microbit::hal::delay::Delay; use microbit::hal::delay::Delay;
@@ -43,10 +45,34 @@ fn main() -> ! {
let rx = gpio.pin25.into_floating_input().downgrade(); let rx = gpio.pin25.into_floating_input().downgrade();
// Configure serial communication // Configure serial communication
let (mut tx, mut rx) = serial::Serial::uart0(p.UART0, tx, rx, BAUD115200).split(); let (mut tx, mut rx) = serial::Serial::uart0(p.UART0, tx, rx, BAUD115200).split();
write!(tx, "Start\r\n"); // A buffer with 32 bytes of capacity
let mut buffer: Vec<u8, consts::U32> = Vec::new();
writeln!(tx, "Start");
loop { loop {
let val = block!(rx.read()).unwrap(); loop {
block!(tx.write(val)); // Read
let byte = block!(rx.read()).unwrap();
// Echo
block!(tx.write(byte));
// Carriage return
if byte == b'\r' {
break;
}
// Push to buffer
if buffer.push(byte).is_err() {
// Buffer full
writeln!(tx, "\r\nWarning: buffer full, dumping buffer");
break;
}
}
// Uncomment to not overwrite input string
//writeln!(tx, "");
// Respond
for b in buffer.iter().rev() {
block!(tx.write(*b));
}
writeln!(tx, "");
buffer.clear();
} }
} }
panic!("End"); panic!("End");