From b12fb9f827cdf0501c19add25268e2152ddf2517 Mon Sep 17 00:00:00 2001 From: Michael Droogleever Date: Sat, 18 Aug 2018 23:12:14 +0200 Subject: [PATCH] Add serial examples --- src/SUMMARY.md | 6 +- src/microbit/examples/display_blocking.rs | 2 +- src/serial/.gdbinit | 4 +- src/serial/02.00.md | 6 + src/serial/02.01.SOLUTION.md | 8 + src/serial/02.01.md | 19 +++ src/serial/02.02.SOLUTION.md | 5 + src/serial/02.02.md | 18 +++ src/serial/02.03.SOLUTION.md | 5 + src/serial/02.03.md | 23 +++ src/serial/Cargo.toml | 1 + src/serial/examples/countdown.rs | 86 +++++++++++ src/serial/examples/display_echo.rs.disabled | 69 +++++++++ src/serial/examples/quiz.rs | 146 +++++++++++++++++++ src/serial/examples/{main.rs => reverse.rs} | 32 +++- 15 files changed, 421 insertions(+), 9 deletions(-) create mode 100644 src/serial/02.00.md create mode 100644 src/serial/02.01.SOLUTION.md create mode 100644 src/serial/02.01.md create mode 100644 src/serial/02.02.SOLUTION.md create mode 100644 src/serial/02.02.md create mode 100644 src/serial/02.03.SOLUTION.md create mode 100644 src/serial/02.03.md create mode 100644 src/serial/examples/countdown.rs create mode 100644 src/serial/examples/display_echo.rs.disabled create mode 100644 src/serial/examples/quiz.rs rename src/serial/examples/{main.rs => reverse.rs} (56%) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index d704195..0556fc1 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -33,7 +33,7 @@ - [Delays](microbit/02.00.DELAY.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) - [Theory](serial/01.01.THEORY.md) - [Solution](serial/01.02.ECHO.md) @@ -42,8 +42,6 @@ - [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) @@ -58,6 +56,8 @@ - [Multiplexing](display/03.02.MULT.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 - Real time](rtfm/00.00.README.md) diff --git a/src/microbit/examples/display_blocking.rs b/src/microbit/examples/display_blocking.rs index f6ad6e7..a41a3df 100644 --- a/src/microbit/examples/display_blocking.rs +++ b/src/microbit/examples/display_blocking.rs @@ -36,7 +36,7 @@ fn main() -> ! { let mut gpio = p.GPIO.split(); let mut delay = Delay::new(p.TIMER0); - // Display + // 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(); diff --git a/src/serial/.gdbinit b/src/serial/.gdbinit index b70f137..2e67e86 100644 --- a/src/serial/.gdbinit +++ b/src/serial/.gdbinit @@ -7,6 +7,6 @@ monitor arm semihosting enable # Load your program, breaks at entry load # (optional) Add breakpoint at function -break serial::main +#break serial::main # Continue with execution -continue \ No newline at end of file +#continue \ No newline at end of file diff --git a/src/serial/02.00.md b/src/serial/02.00.md new file mode 100644 index 0000000..ff7d356 --- /dev/null +++ b/src/serial/02.00.md @@ -0,0 +1,6 @@ +# Exercises + +- Reverse echo a line of input +- Numerical countdown +- Display echo +- Quiz game diff --git a/src/serial/02.01.SOLUTION.md b/src/serial/02.01.SOLUTION.md new file mode 100644 index 0000000..2fac642 --- /dev/null +++ b/src/serial/02.01.SOLUTION.md @@ -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. diff --git a/src/serial/02.01.md b/src/serial/02.01.md new file mode 100644 index 0000000..1924f6f --- /dev/null +++ b/src/serial/02.01.md @@ -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) diff --git a/src/serial/02.02.SOLUTION.md b/src/serial/02.02.SOLUTION.md new file mode 100644 index 0000000..e896e5b --- /dev/null +++ b/src/serial/02.02.SOLUTION.md @@ -0,0 +1,5 @@ +# Solution + +``` rust +{{#include examples/countdown.rs}} +``` \ No newline at end of file diff --git a/src/serial/02.02.md b/src/serial/02.02.md new file mode 100644 index 0000000..58df9bf --- /dev/null +++ b/src/serial/02.02.md @@ -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) diff --git a/src/serial/02.03.SOLUTION.md b/src/serial/02.03.SOLUTION.md new file mode 100644 index 0000000..ffdffeb --- /dev/null +++ b/src/serial/02.03.SOLUTION.md @@ -0,0 +1,5 @@ +# Solution + +``` rust +{{#include examples/quiz.rs}} +``` \ No newline at end of file diff --git a/src/serial/02.03.md b/src/serial/02.03.md new file mode 100644 index 0000000..397ae3a --- /dev/null +++ b/src/serial/02.03.md @@ -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. diff --git a/src/serial/Cargo.toml b/src/serial/Cargo.toml index 3c26173..6d80b8d 100644 --- a/src/serial/Cargo.toml +++ b/src/serial/Cargo.toml @@ -3,6 +3,7 @@ name = "serial" version = "0.1.0" [dependencies] +heapless="~0.3" cortex-m-rt="~0.5" cortex-m-semihosting="" panic-semihosting = "~0.3" diff --git a/src/serial/examples/countdown.rs b/src/serial/examples/countdown.rs new file mode 100644 index 0000000..cd2aff2 --- /dev/null +++ b/src/serial/examples/countdown.rs @@ -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 = 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"); +} diff --git a/src/serial/examples/display_echo.rs.disabled b/src/serial/examples/display_echo.rs.disabled new file mode 100644 index 0000000..e96f0fd --- /dev/null +++ b/src/serial/examples/display_echo.rs.disabled @@ -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"); +} diff --git a/src/serial/examples/quiz.rs b/src/serial/examples/quiz.rs new file mode 100644 index 0000000..b4ddb32 --- /dev/null +++ b/src/serial/examples/quiz.rs @@ -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"); +} diff --git a/src/serial/examples/main.rs b/src/serial/examples/reverse.rs similarity index 56% rename from src/serial/examples/main.rs rename to src/serial/examples/reverse.rs index 7bae758..a835d3f 100644 --- a/src/serial/examples/main.rs +++ b/src/serial/examples/reverse.rs @@ -4,6 +4,7 @@ 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; @@ -11,6 +12,7 @@ extern crate microbit; use core::fmt::Write; use rt::ExceptionFrame; use sh::hio; +use heapless::{consts, Vec}; use microbit::hal::prelude::*; use microbit::hal::delay::Delay; @@ -43,10 +45,34 @@ fn main() -> ! { 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(); - write!(tx, "Start\r\n"); + // A buffer with 32 bytes of capacity + let mut buffer: Vec = Vec::new(); + writeln!(tx, "Start"); loop { - let val = block!(rx.read()).unwrap(); - block!(tx.write(val)); + 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; + } + } + // 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");