Break out UI code to a separate module
This commit is contained in:
		
							
								
								
									
										99
									
								
								src/hello_screen.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								src/hello_screen.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,99 @@ | |||||||
|  | use arrayvec::ArrayString; | ||||||
|  |  | ||||||
|  | use embedded_graphics::{ | ||||||
|  |     draw_target::DrawTarget, | ||||||
|  |     mono_font::{ascii::FONT_6X9, MonoTextStyle, MonoTextStyleBuilder}, | ||||||
|  |     pixelcolor::BinaryColor, | ||||||
|  |     prelude::*, | ||||||
|  |     primitives::{Circle, PrimitiveStyle}, | ||||||
|  |     text::{Baseline, Text}, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | use core::fmt::Write; | ||||||
|  |  | ||||||
|  | #[derive(Copy, Clone)] | ||||||
|  | pub enum HelloEvent { | ||||||
|  |     Tick, | ||||||
|  |     Knob(i32), | ||||||
|  |     Button, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub struct HelloDisplay<const W: i32, const H: i32> { | ||||||
|  |     circle_pos: (i32, i32), | ||||||
|  |     counter: i32, | ||||||
|  |     textbuf: ArrayString<15>, | ||||||
|  |     text_style: MonoTextStyle<'static, BinaryColor>, | ||||||
|  |     title: Text<'static, MonoTextStyle<'static, BinaryColor>>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<const W: i32, const H: i32> HelloDisplay<W, H> { | ||||||
|  |     const C_RADIUS: i32 = 8; | ||||||
|  |  | ||||||
|  |     /// Construct a new HelloDisplay | ||||||
|  |     pub fn new() -> Self { | ||||||
|  |         let text_style = MonoTextStyleBuilder::new() | ||||||
|  |             .font(&FONT_6X9) | ||||||
|  |             .text_color(BinaryColor::On) | ||||||
|  |             .build(); | ||||||
|  |         let title = | ||||||
|  |             Text::with_baseline("Hello Rust!", Point::new(20, 16), text_style, Baseline::Top); | ||||||
|  |         Self { | ||||||
|  |             circle_pos: (0, 0), | ||||||
|  |             counter: 0, | ||||||
|  |             textbuf: ArrayString::new(), | ||||||
|  |             text_style, | ||||||
|  |             title, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Draw the current state of HelloDisplay onto the DrawTarget | ||||||
|  |     pub fn draw<D>(&mut self, target: &mut D) -> Result<(), D::Error> | ||||||
|  |     where | ||||||
|  |         D: DrawTarget<Color = BinaryColor>, | ||||||
|  |     { | ||||||
|  |         let (cx, cy) = self.circle_pos; | ||||||
|  |         let circle = Circle::new(Point::new(cx, cy), Self::C_RADIUS as u32) | ||||||
|  |             .into_styled(PrimitiveStyle::with_fill(BinaryColor::On)); | ||||||
|  |  | ||||||
|  |         self.textbuf.clear(); | ||||||
|  |         write!(&mut self.textbuf, "count: {}", self.counter).unwrap(); | ||||||
|  |         let count = Text::with_baseline( | ||||||
|  |             &self.textbuf, | ||||||
|  |             Point::new(20, 36), | ||||||
|  |             self.text_style, | ||||||
|  |             Baseline::Top, | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         target.clear(BinaryColor::Off)?; | ||||||
|  |  | ||||||
|  |         self.title.draw(target)?; | ||||||
|  |         circle.draw(target)?; | ||||||
|  |         count.draw(target)?; | ||||||
|  |  | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Update the current state of HelloDisplay based on an event | ||||||
|  |     pub fn event(&mut self, event: HelloEvent) { | ||||||
|  |         match event { | ||||||
|  |             HelloEvent::Tick => { | ||||||
|  |                 let (_, cy) = self.circle_pos; | ||||||
|  |                 let cx_next = self | ||||||
|  |                     .counter | ||||||
|  |                     .max(Self::C_RADIUS / 2) | ||||||
|  |                     .min(W - Self::C_RADIUS / 2); | ||||||
|  |                 let mut cy_next = cy + 1; | ||||||
|  |                 if cy_next > (H + Self::C_RADIUS) { | ||||||
|  |                     cy_next = -Self::C_RADIUS | ||||||
|  |                 }; | ||||||
|  |  | ||||||
|  |                 self.circle_pos = (cx_next, cy_next); | ||||||
|  |             } | ||||||
|  |             HelloEvent::Knob(pos_delta) => { | ||||||
|  |                 self.counter = self.counter + pos_delta; | ||||||
|  |             } | ||||||
|  |             HelloEvent::Button => {} | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										70
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										70
									
								
								src/main.rs
									
									
									
									
									
								
							| @@ -1,8 +1,6 @@ | |||||||
| #![no_std] | #![no_std] | ||||||
| #![cfg_attr(not(doc), no_main)] | #![cfg_attr(not(doc), no_main)] | ||||||
|  |  | ||||||
| use core::{cell::RefCell, fmt::Write}; |  | ||||||
|  |  | ||||||
| use panic_rtt_target as _; | use panic_rtt_target as _; | ||||||
| use rtt_target::{rprintln, rtt_init_print}; | use rtt_target::{rprintln, rtt_init_print}; | ||||||
|  |  | ||||||
| @@ -27,19 +25,14 @@ use switch_hal::{InputSwitch, IntoSwitch, OutputSwitch, ToggleableOutputSwitch}; | |||||||
|  |  | ||||||
| use ssd1306::{prelude::*, I2CDisplayInterface, Ssd1306}; | use ssd1306::{prelude::*, I2CDisplayInterface, Ssd1306}; | ||||||
|  |  | ||||||
| use embedded_graphics::{ | use core::cell::RefCell; | ||||||
|     mono_font::{ascii::FONT_6X9, MonoTextStyleBuilder}, |  | ||||||
|     pixelcolor::BinaryColor, |  | ||||||
|     prelude::*, |  | ||||||
|     primitives::{Circle, PrimitiveStyle}, |  | ||||||
|     text::{Baseline, Text}, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| use arrayvec::ArrayString; |  | ||||||
|  |  | ||||||
| mod rotary; | mod rotary; | ||||||
| use rotary::{Direction, Rotary}; | use rotary::{Direction, Rotary}; | ||||||
|  |  | ||||||
|  | mod hello_screen; | ||||||
|  | use hello_screen::{HelloEvent, HelloDisplay}; | ||||||
|  |  | ||||||
| type ClkPin = PA8<Input<Floating>>; | type ClkPin = PA8<Input<Floating>>; | ||||||
| type DtPin = PA9<Input<Floating>>; | type DtPin = PA9<Input<Floating>>; | ||||||
|  |  | ||||||
| @@ -47,6 +40,9 @@ static CLK_PIN: Mutex<RefCell<Option<ClkPin>>> = Mutex::new(RefCell::new(None)); | |||||||
| static DT_PIN: Mutex<RefCell<Option<DtPin>>> = Mutex::new(RefCell::new(None)); | static DT_PIN: Mutex<RefCell<Option<DtPin>>> = Mutex::new(RefCell::new(None)); | ||||||
| static COUNT: Mutex<RefCell<i32>> = Mutex::new(RefCell::new(0)); | static COUNT: Mutex<RefCell<i32>> = Mutex::new(RefCell::new(0)); | ||||||
|  |  | ||||||
|  | const DISPLAY_W: i32 = 128; | ||||||
|  | const DISPLAY_H: i32 = 64; | ||||||
|  |  | ||||||
| #[entry] | #[entry] | ||||||
| fn main() -> ! { | fn main() -> ! { | ||||||
|     // Init buffers for debug printing |     // Init buffers for debug printing | ||||||
| @@ -141,58 +137,36 @@ fn main() -> ! { | |||||||
|     } |     } | ||||||
|     NVIC::unpend(Interrupt::EXTI9_5); |     NVIC::unpend(Interrupt::EXTI9_5); | ||||||
|  |  | ||||||
|     // We will draw a fixed message on the screen and also a moving circle |     rprintln!("Finished init, starting main loop"); | ||||||
|  |  | ||||||
|     const C_RADIUS: i32 = 8; |     // Create the hello UI | ||||||
|     const DISPLAY_W: i32 = 128; |     let mut hello: HelloDisplay<DISPLAY_W, DISPLAY_H> = HelloDisplay::new(); | ||||||
|     const DISPLAY_H: i32 = 64; |  | ||||||
|  |  | ||||||
|     let mut cx = 20; |  | ||||||
|     let mut cy = 20; |  | ||||||
|  |  | ||||||
|     let text_style = MonoTextStyleBuilder::new() |  | ||||||
|         .font(&FONT_6X9) |  | ||||||
|         .text_color(BinaryColor::On) |  | ||||||
|         .build(); |  | ||||||
|     let t = Text::with_baseline("Hello Rust!", Point::new(20, 16), text_style, Baseline::Top); |  | ||||||
|  |  | ||||||
|     // Turn the LED on via the OutputPin trait |     // Turn the LED on via the OutputPin trait | ||||||
|     led.on().unwrap(); |     led.on().unwrap(); | ||||||
|  |  | ||||||
|     rprintln!("Finished init, starting main loop"); |  | ||||||
|  |  | ||||||
|     let mut button_last = false; |     let mut button_last = false; | ||||||
|  |     let mut counter_last = 0; | ||||||
|     // Microcontroller programs never exit main, so we must loop! |     // Microcontroller programs never exit main, so we must loop! | ||||||
|     loop { |     loop { | ||||||
|  |         hello.draw(&mut display).unwrap(); | ||||||
|  |         display.flush().unwrap(); | ||||||
|  |  | ||||||
|  |         hello.event(HelloEvent::Tick); | ||||||
|  |  | ||||||
|         // Check our inputs |         // Check our inputs | ||||||
|         let button = sw.is_active().unwrap(); |         let button = sw.is_active().unwrap(); | ||||||
|         let counter = cortex_m::interrupt::free(|cs| *COUNT.borrow(cs).borrow()); |  | ||||||
|         if button_last && !button { |         if button_last && !button { | ||||||
|             led.toggle().unwrap(); |             led.toggle().unwrap(); | ||||||
|  |             hello.event(HelloEvent::Button); | ||||||
|         } |         } | ||||||
|         button_last = button; |         button_last = button; | ||||||
|  |  | ||||||
|         let c = Circle::new(Point::new(cx, cy), C_RADIUS as u32) |         let counter = cortex_m::interrupt::free(|cs| *COUNT.borrow(cs).borrow()); | ||||||
|             .into_styled(PrimitiveStyle::with_fill(BinaryColor::On)); |         if counter_last != counter { | ||||||
|  |             hello.event(HelloEvent::Knob(counter - counter_last)); | ||||||
|         // Show the position of the knob and thus the ball |         } | ||||||
|         let mut textbuf = ArrayString::<15>::new(); |         counter_last = counter; | ||||||
|         write!(&mut textbuf, "count: {}", counter).unwrap(); |  | ||||||
|         let count = Text::with_baseline(&textbuf, Point::new(20, 36), text_style, Baseline::Top); |  | ||||||
|  |  | ||||||
|         display.clear(); |  | ||||||
|         c.draw(&mut display).unwrap(); |  | ||||||
|         t.draw(&mut display).unwrap(); |  | ||||||
|         count.draw(&mut display).unwrap(); |  | ||||||
|         display.flush().unwrap(); |  | ||||||
|  |  | ||||||
|         // Control the horizontal position with the knob |  | ||||||
|         cx = counter.max(C_RADIUS / 2).min(DISPLAY_W - C_RADIUS / 2); |  | ||||||
|         // Wrap the ball back to the top when it falls off the bottom |  | ||||||
|         cy += 1; |  | ||||||
|         if cy > (DISPLAY_H + C_RADIUS) { |  | ||||||
|             cy = -C_RADIUS |  | ||||||
|         }; |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user