From 1493583bf0c059399706c538d075aebade88e6ef Mon Sep 17 00:00:00 2001 From: Michael Droogleever Date: Sun, 29 Jul 2018 20:02:18 +0200 Subject: [PATCH] Small fixes, add full code display solution --- Cargo.toml | 1 + src/README.md | 5 +- src/SUMMARY.md | 2 +- src/display/03.02.MULT.md | 2 +- src/display/03.03.FULL.md | 10 +- src/display/Cargo.toml | 9 ++ src/display/src/main.rs | 209 ++++++++++++++++++++++++++++++++++++++ src/hello-world/.gdbinit | 9 +- 8 files changed, 239 insertions(+), 8 deletions(-) create mode 100644 src/display/Cargo.toml create mode 100644 src/display/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 6217771..cbd82a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ members = [ "src/getting-started", "src/hello-world", + "src/display", ] [profile.dev] diff --git a/src/README.md b/src/README.md index cf53b00..7b45cd9 100644 --- a/src/README.md +++ b/src/README.md @@ -58,8 +58,9 @@ What's out of scope for this book: ## Reporting problems The source of this book is in [this repository]. -If you encounter any typo or problem with the code report it on the [issue tracker], -or even submit a pull request. +If you encounter any typo or problem please report it on the [issue tracker], +or even submit a [pull request]. [this repository]: https://github.com/droogmic/microrust [issue tracker]: https://github.com/droogmic/microrust/issues +[pull request]: https://github.com/droogmic/microrust/pulls diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 8ae5862..c4d63be 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -37,7 +37,7 @@ - [Solution](display/03.00.SOLUTION.md) - [Layout](display/03.01.LAYOUT.md) - [Multiplexing](display/03.02.MULT.md) - - [Full](display/03.03.FULL.md) + - [Full Solution](display/03.03.FULL.md) - [WIP - Sensors and I²C](sensors/00.00.README.md) diff --git a/src/display/03.02.MULT.md b/src/display/03.02.MULT.md index 4c11c9f..ca5275b 100644 --- a/src/display/03.02.MULT.md +++ b/src/display/03.02.MULT.md @@ -29,7 +29,7 @@ pub fn display_pre(&mut self, delay: &mut Delay, led_matrix: [[u8; 9]; 3], durat delay.delay_ms(delay_ms); // It is not worth the logic to check which pins need resetting, // so set all the pins back high. - for col_line in cols.iter_mut() { + for col_line in &mut cols { col_line.set_high(); } // Set the row back low. diff --git a/src/display/03.03.FULL.md b/src/display/03.03.FULL.md index b3de009..6054b00 100644 --- a/src/display/03.03.FULL.md +++ b/src/display/03.03.FULL.md @@ -1,4 +1,8 @@ -# Full +# Full Solution -Full solution still to come. -Please look at the implementation in the [micro:bit crate](https://github.com/therealprof/microbit]). \ No newline at end of file +For the most modern implementations, +please look at the code in the [micro:bit crate](https://github.com/therealprof/microbit). + +``` rust +{{#include src/main.rs}} +``` \ No newline at end of file diff --git a/src/display/Cargo.toml b/src/display/Cargo.toml new file mode 100644 index 0000000..07000cc --- /dev/null +++ b/src/display/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "display" +version = "0.1.0" + +[dependencies] +cortex-m-rt="~0.5" +cortex-m-semihosting="~0.3" +panic-semihosting = "~0.3" +microbit="~0.5" diff --git a/src/display/src/main.rs b/src/display/src/main.rs new file mode 100644 index 0000000..3d49f85 --- /dev/null +++ b/src/display/src/main.rs @@ -0,0 +1,209 @@ +#![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; + +use microbit::hal::delay::Delay; +use microbit::hal::gpio::gpio::PIN; +use microbit::hal::gpio::gpio::{PIN4, PIN5, PIN6, PIN7, PIN8, PIN9, PIN10, PIN11, PIN12, PIN13, PIN14, PIN15}; +use microbit::hal::gpio::{Output, PushPull}; +use microbit::hal::serial; +use microbit::hal::serial::BAUD115200; +use microbit::hal::prelude::*; + +exception!(HardFault, hard_fault); + +fn hard_fault(ef: &ExceptionFrame) -> ! { + panic!("{:#?}", ef); +} + +exception!(*, default_handler); + +fn default_handler(irqn: i16) { + panic!("Unhandled exception (IRQn = {})", irqn); +} + +type LED = PIN>; + +const DEFAULT_DELAY_MS: u32 = 2; +const LED_LAYOUT: [[(usize, usize); 5]; 5] = [ + [(0, 0), (1, 3), (0, 1), (1, 4), (0, 2)], + [(2, 3), (2, 4), (2, 5), (2, 6), (2, 7)], + [(1, 1), (0, 8), (1, 2), (2, 8), (1, 0)], + [(0, 7), (0, 6), (0, 5), (0, 4), (0, 3)], + [(2, 2), (1, 6), (2, 0), (1, 5), (2, 1)], +]; + +/// Array of all the LEDs in the 5x5 display on the board +pub struct Display { + delay_ms: u32, + rows: [LED; 3], + cols: [LED; 9], +} + +impl Display { + /// Initializes all the user LEDs + pub fn new( + col1: PIN4>, + col2: PIN5>, + col3: PIN6>, + col4: PIN7>, + col5: PIN8>, + col6: PIN9>, + col7: PIN10>, + col8: PIN11>, + col9: PIN12>, + row1: PIN13>, + row2: PIN14>, + row3: PIN15>, + ) -> Self { + let mut retval = Display { + delay_ms: DEFAULT_DELAY_MS, + rows: [row1.downgrade(), row2.downgrade(), row3.downgrade()], + cols: [ + col1.downgrade(), col2.downgrade(), col3.downgrade(), + col4.downgrade(), col5.downgrade(), col6.downgrade(), + col7.downgrade(), col8.downgrade(), col9.downgrade() + ], + }; + // This is needed to reduce flickering on reset + retval.clear(); + retval + } + + /// Clear display + pub fn clear(&mut self) { + for row in &mut self.rows { + row.set_low(); + } + for col in &mut self.cols { + col.set_high(); + } + } + + /// Convert 5x5 display image to 3x9 matrix image + pub fn display2matrix(led_display: [[u8; 5]; 5]) -> [[u8; 9]; 3] { + let mut led_matrix: [[u8; 9]; 3] = [[0; 9]; 3]; + for (led_display_row, layout_row) in led_display.iter().zip(LED_LAYOUT.iter()) { + for (led_display_val, layout_loc) in led_display_row.iter().zip(layout_row) { + led_matrix[layout_loc.0][layout_loc.1] = *led_display_val; + } + } + led_matrix + } + + /// Display 5x5 display image for a given duration + pub fn display(&mut self, delay: &mut Delay, led_display: [[u8; 5]; 5], duration_ms: u32) { + let led_matrix = Display::display2matrix(led_display); + // Calculates how long to block for + // e.g. If the duration_ms is 500ms (half a second) + // and self.delay_ms is 2ms (about 2ms per scan row), + // each refresh takes 3rows×2ms, so we need 500ms / (3×2ms) loops. + let loops = duration_ms / (self.rows.len() as u32 * self.delay_ms); + for _ in 0..loops { + for (row_line, led_matrix_row) in self.rows.iter_mut().zip(led_matrix.iter()) { + row_line.set_high(); + for (col_line, led_matrix_val) in self.cols.iter_mut().zip(led_matrix_row.iter()) { + // We are keeping it simple, and not adding brightness + if *led_matrix_val > 0 { + col_line.set_low(); + } + } + delay.delay_ms(self.delay_ms); + for col_line in &mut self.cols { + col_line.set_high(); + } + row_line.set_low(); + } + } + } +} + +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, _) = serial::Serial::uart0(p.UART0, tx, rx, BAUD115200).split(); + write!(tx, "serial - start\r\n"); + + // Create delay provider + let mut delay = Delay::new(p.TIMER0); + + // Display pins + let row1 = gpio.pin13.into_push_pull_output(); + let row2 = gpio.pin14.into_push_pull_output(); + let row3 = gpio.pin15.into_push_pull_output(); + let col1 = gpio.pin4.into_push_pull_output(); + let col2 = gpio.pin5.into_push_pull_output(); + let col3 = gpio.pin6.into_push_pull_output(); + let col4 = gpio.pin7.into_push_pull_output(); + let col5 = gpio.pin8.into_push_pull_output(); + let col6 = gpio.pin9.into_push_pull_output(); + let col7 = gpio.pin10.into_push_pull_output(); + let col8 = gpio.pin11.into_push_pull_output(); + let col9 = gpio.pin12.into_push_pull_output(); + let mut leds = Display::new( + col1, col2, col3, + col4, col5, col6, + col7, col8, col9, + row1, row2, row3, + ); + + #[allow(non_snake_case)] + let letter_I = [ + [0, 1, 1, 1, 0], + [0, 0, 1, 0, 0], + [0, 0, 1, 0, 0], + [0, 0, 1, 0, 0], + [0, 1, 1, 1, 0], + ]; + + let heart = [ + [0, 1, 0, 1, 0], + [1, 0, 1, 0, 1], + [1, 0, 0, 0, 1], + [0, 1, 0, 1, 0], + [0, 0, 1, 0, 0], + ]; + + #[allow(non_snake_case)] + let letter_U = [ + [0, 1, 0, 1, 0], + [0, 1, 0, 1, 0], + [0, 1, 0, 1, 0], + [0, 1, 0, 1, 0], + [0, 1, 1, 1, 0], + ]; + + let _ = write!(tx, "\n\rStarting!\n\r"); + + loop { + let _ = write!(tx, "I <3 Rust on the micro:bit!\n\r"); + leds.display(&mut delay, letter_I, 1000); + leds.display(&mut delay, heart, 1000); + leds.display(&mut delay, letter_U, 1000); + leds.clear(); + delay.delay_ms(250_u32); + } + + } + panic!("End"); + +} diff --git a/src/hello-world/.gdbinit b/src/hello-world/.gdbinit index 2107960..b640d83 100644 --- a/src/hello-world/.gdbinit +++ b/src/hello-world/.gdbinit @@ -1,5 +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 hello::main -continue +# Continue with execution +continue \ No newline at end of file