A personal rust firmware for the Badger 2040 W

image loading

+1 -1
.cargo/config.toml
··· 2 2 runner = "probe-rs run --chip RP2040" 3 3 4 4 [build] 5 - target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ 5 + target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ 6 6 7 7 [env] 8 8 DEFMT_LOG = "debug"
+2 -1
.gitignore
··· 1 1 /target 2 - .DS_Store 2 + .DS_Store 3 + .env
+10
Cargo.lock
··· 909 909 "smart-leds", 910 910 "st7789", 911 911 "static_cell", 912 + "tinybmp", 912 913 "trouble-host", 913 914 "uc8151", 914 915 "usbd-hid", ··· 2315 2316 checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" 2316 2317 dependencies = [ 2317 2318 "crunchy", 2319 + ] 2320 + 2321 + [[package]] 2322 + name = "tinybmp" 2323 + version = "0.5.0" 2324 + source = "registry+https://github.com/rust-lang/crates.io-index" 2325 + checksum = "197cc000e382175ff15abd9c54c694ef80ef20cb07e7f956c71e3ea97fc8dc60" 2326 + dependencies = [ 2327 + "embedded-graphics", 2318 2328 ] 2319 2329 2320 2330 [[package]]
+1
Cargo.toml
··· 103 103 "graphics", 104 104 ] } 105 105 embedded-text = "0.7.0" 106 + tinybmp = "0.5.0" 106 107 107 108 [profile.release] 108 109 debug = 2
images/ferris_w_a_knife.bmp

This is a binary file and will not be displayed.

images/repo.bmp

This is a binary file and will not be displayed.

+65
src/image_handler.rs
··· 1 + use core::sync::atomic::{AtomicBool, AtomicU8}; 2 + 3 + use embedded_graphics::prelude::Point; 4 + 5 + pub static CURRENT_IMAGE: AtomicU8 = AtomicU8::new(0); 6 + pub static CHANGE_IMAGE: AtomicBool = AtomicBool::new(true); 7 + 8 + static NUMBER_OF_IMAGES: u8 = 2; 9 + static FERRIS_IMG: &[u8; 15722] = include_bytes!("../images/ferris_w_a_knife.bmp"); 10 + static REPO_IMG: &[u8; 11262] = include_bytes!("../images/repo.bmp"); 11 + 12 + pub enum DisplayImage { 13 + Ferris = 0, 14 + Repo = 1, 15 + } 16 + 17 + pub fn get_current_image() -> DisplayImage { 18 + DisplayImage::from_u8(CURRENT_IMAGE.load(core::sync::atomic::Ordering::Relaxed)).unwrap() 19 + } 20 + 21 + impl DisplayImage { 22 + pub fn from_u8(value: u8) -> Option<Self> { 23 + match value { 24 + 0 => Some(Self::Ferris), 25 + 1 => Some(Self::Repo), 26 + _ => None, 27 + } 28 + } 29 + 30 + pub fn as_u8(&self) -> u8 { 31 + match self { 32 + Self::Ferris => 0, 33 + Self::Repo => 1, 34 + } 35 + } 36 + 37 + pub fn image(&self) -> &'static [u8] { 38 + match self { 39 + Self::Ferris => FERRIS_IMG, 40 + Self::Repo => REPO_IMG, 41 + } 42 + } 43 + 44 + pub fn next(&self) -> Self { 45 + let image_count = self.as_u8(); 46 + let next_image = (image_count + 1) % NUMBER_OF_IMAGES; 47 + DisplayImage::from_u8(next_image).unwrap() 48 + } 49 + 50 + pub fn previous(&self) -> Self { 51 + let image_count = self.as_u8(); 52 + if image_count == 0 { 53 + return DisplayImage::from_u8(NUMBER_OF_IMAGES - 1).unwrap(); 54 + } 55 + let previous_image = (image_count - 1) % NUMBER_OF_IMAGES; 56 + DisplayImage::from_u8(previous_image).unwrap() 57 + } 58 + 59 + pub fn image_location(&self) -> Point { 60 + match self { 61 + Self::Ferris => Point::new(150, 26), 62 + Self::Repo => Point::new(190, 26), 63 + } 64 + } 65 + }
+88 -53
src/main.rs
··· 5 5 #![no_std] 6 6 #![no_main] 7 7 use core::sync::atomic::AtomicU32; 8 - 9 - use cyw43_driver::setup_cyw43; 10 8 use defmt::info; 11 9 use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice; 12 10 use embassy_executor::Spawner; ··· 18 16 use embassy_sync::blocking_mutex::raw::NoopRawMutex; 19 17 use embassy_sync::mutex::Mutex; 20 18 use embassy_time::{Delay, Duration, Timer}; 21 - use embedded_graphics::primitives::PrimitiveStyleBuilder; 22 - use embedded_graphics::text::Text; 23 19 use embedded_graphics::{ 20 + image::Image, 24 21 mono_font::{ascii::*, MonoTextStyle}, 25 22 pixelcolor::BinaryColor, 26 23 prelude::*, ··· 33 30 }; 34 31 use gpio::{Level, Output, Pull}; 35 32 use heapless::String; 33 + use image_handler::{get_current_image, DisplayImage, CHANGE_IMAGE, CURRENT_IMAGE}; 36 34 use static_cell::StaticCell; 35 + use tinybmp::Bmp; 37 36 use uc8151::asynch::Uc8151; 38 37 use uc8151::LUT; 39 38 use uc8151::WIDTH; 40 39 use {defmt_rtt as _, panic_probe as _}; 40 + 41 41 mod cyw43_driver; 42 + mod image_handler; 42 43 43 44 type Spi0Bus = Mutex<NoopRawMutex, Spi<'static, SPI0, spi::Async>>; 45 + 44 46 static WIFI_COUNT: AtomicU32 = AtomicU32::new(0); 45 47 46 48 #[embassy_executor::main] 47 49 async fn main(spawner: Spawner) { 48 50 let p = embassy_rp::init(Default::default()); 49 - let (_net_device, mut control) = setup_cyw43( 50 - p.PIO0, p.PIN_23, p.PIN_24, p.PIN_25, p.PIN_29, p.DMA_CH0, spawner, 51 - ) 52 - .await; 51 + // let (_net_device, mut control) = setup_cyw43( 52 + // p.PIO0, p.PIN_23, p.PIN_24, p.PIN_25, p.PIN_29, p.DMA_CH0, spawner, 53 + // ) 54 + // .await; 53 55 54 56 // let input = gpio::Input::new(p.PIN_29, gpio::Pull::Up); 55 57 ··· 62 64 let reset = p.PIN_21; 63 65 let power = p.PIN_10; 64 66 65 - let btn_up = p.PIN_15; 66 - let btn_down = p.PIN_11; 67 - let btn_a = p.PIN_12; 68 - let btn_b = p.PIN_13; 69 - let btn_c = p.PIN_14; 70 - 71 67 let reset = Output::new(reset, Level::Low); 72 68 let _power = Output::new(power, Level::Low); 73 69 ··· 75 71 let cs = Output::new(cs, Level::High); 76 72 let busy = Input::new(busy, Pull::Up); 77 73 78 - let mut _btn_up = Input::new(btn_up, Pull::Up); 79 - let mut _btn_down = Input::new(btn_down, Pull::Up); 80 - let mut _btn_a = Input::new(btn_a, Pull::Up); 81 - let mut _btn_b = Input::new(btn_b, Pull::Up); 82 - let mut _btn_c = Input::new(btn_c, Pull::Up); 74 + let _btn_up = Input::new(p.PIN_15, Pull::Down); 75 + let _btn_down = Input::new(p.PIN_11, Pull::Down); 76 + let _btn_a = Input::new(p.PIN_12, Pull::Down); 77 + let _btn_b = Input::new(p.PIN_13, Pull::Down); 78 + let btn_c = Input::new(p.PIN_14, Pull::Down); 79 + 80 + // let mut btn_c: Debouncer<'_> = Debouncer::new(Input::new(btn_c, Pull::Up), Duration::from_millis(20)); 83 81 84 82 let spi = Spi::new( 85 83 p.SPI0, ··· 95 93 let spi_bus = SPI_BUS.init(Mutex::new(spi)); 96 94 97 95 info!("led on!"); 98 - control.gpio_set(0, true).await; 96 + // control.gpio_set(0, true).await; 99 97 spawner.must_spawn(run_the_display(spi_bus, cs, dc, busy, reset)); 98 + 99 + //Input loop 100 + loop { 101 + //Change Image Button 102 + if btn_c.is_high() { 103 + info!("Button C pressed"); 104 + let current_image = CURRENT_IMAGE.load(core::sync::atomic::Ordering::Relaxed); 105 + let new_image = DisplayImage::from_u8(current_image).unwrap().next(); 106 + CURRENT_IMAGE.store(new_image.as_u8(), core::sync::atomic::Ordering::Relaxed); 107 + CHANGE_IMAGE.store(true, core::sync::atomic::Ordering::Relaxed); 108 + Timer::after(Duration::from_millis(500)).await; 109 + continue; 110 + } 111 + Timer::after(Duration::from_millis(100)).await; 112 + } 100 113 } 101 114 102 115 #[embassy_executor::task] ··· 120 133 let character_style = MonoTextStyle::new(&FONT_9X18_BOLD, BinaryColor::Off); 121 134 let textbox_style = TextBoxStyleBuilder::new() 122 135 .height_mode(HeightMode::FitToText) 123 - .alignment(HorizontalAlignment::Center) 136 + .alignment(HorizontalAlignment::Left) 124 137 .paragraph_spacing(6) 125 138 .build(); 126 139 127 140 // Bounding box for our text. Fill it with the opposite color so we can read the text. 128 - let bounds = Rectangle::new(Point::new(0, 40), Size::new(WIDTH - 157, 0)); 129 - bounds 141 + let name_and_detail_bounds = Rectangle::new(Point::new(0, 40), Size::new(WIDTH - 75, 0)); 142 + name_and_detail_bounds 130 143 .into_styled(PrimitiveStyle::with_fill(BinaryColor::On)) 131 144 .draw(&mut display) 132 145 .unwrap(); 133 146 134 147 // Create the text box and apply styling options. 135 - let text = "Written In\nRust!!"; 136 - let text_box = TextBox::with_textbox_style(text, bounds, character_style, textbox_style); 148 + 149 + let text = "Bailey Townsend\nSoftware Dev"; 150 + // \nWritten in rust\nRunning on a pico w"; 151 + let name_and_detail_box = 152 + TextBox::with_textbox_style(text, name_and_detail_bounds, character_style, textbox_style); 137 153 138 154 // Draw the text box. 139 - text_box.draw(&mut display).unwrap(); 155 + name_and_detail_box.draw(&mut display).unwrap(); 140 156 141 157 let _ = display.update().await; 142 158 ··· 145 161 146 162 loop { 147 163 let count = WIFI_COUNT.load(core::sync::atomic::Ordering::Relaxed); 148 - let _ = core::fmt::write(&mut text, format_args!("Count: {}", count)); 149 - let count_bounds = Rectangle::new(Point::new(0, 0), Size::new(WIDTH, 24)); 150 - count_bounds 151 - .into_styled( 152 - PrimitiveStyleBuilder::default() 153 - .stroke_color(BinaryColor::Off) 154 - .fill_color(BinaryColor::On) 155 - .stroke_width(1) 156 - .build(), 157 - ) 158 - .draw(&mut display) 159 - .unwrap(); 164 + // let _ = core::fmt::write(&mut text, format_args!("Count: {}", count)); 165 + // let count_bounds = Rectangle::new(Point::new(0, 0), Size::new(WIDTH, 24)); 166 + // count_bounds 167 + // .into_styled( 168 + // PrimitiveStyleBuilder::default() 169 + // .stroke_color(BinaryColor::Off) 170 + // .fill_color(BinaryColor::On) 171 + // .stroke_width(1) 172 + // .build(), 173 + // ) 174 + // .draw(&mut display) 175 + // .unwrap(); 160 176 161 - Text::new(text.as_str(), Point::new(8, 16), character_style) 162 - .draw(&mut display) 163 - .unwrap(); 177 + // Text::new(text.as_str(), Point::new(8, 16), character_style) 178 + // .draw(&mut display) 179 + // .unwrap(); 164 180 165 - // // Draw the text box. 166 - let result = display 167 - .partial_update(count_bounds.try_into().unwrap()) 168 - .await; 169 - match result { 170 - Ok(_) => {} 171 - Err(_) => { 172 - info!("Error updating display"); 173 - } 174 - } 175 - text.clear(); 181 + // // // Draw the text box. 182 + // let result = display 183 + // .partial_update(count_bounds.try_into().unwrap()) 184 + // .await; 185 + // match result { 186 + // Ok(_) => {} 187 + // Err(_) => { 188 + // info!("Error updating display"); 189 + // } 190 + // } 191 + // text.clear(); 176 192 // let _ = display.clear(Rgb565::WHITE.into()); 177 193 // let _ = display.update().await; 178 194 WIFI_COUNT.store(count + 1, core::sync::atomic::Ordering::Relaxed); 179 195 180 - Timer::after(delay).await; 196 + if CHANGE_IMAGE.load(core::sync::atomic::Ordering::Relaxed) { 197 + let current_image = get_current_image(); 198 + let tga: Bmp<BinaryColor> = Bmp::from_slice(&current_image.image()).unwrap(); 199 + let image = Image::new(&tga, current_image.image_location()); 200 + //clear image location by writing a white rectangle over previous image location 201 + let clear_bounds = Rectangle::new( 202 + current_image.previous().image_location(), 203 + Size::new(157, 91), 204 + ); 205 + clear_bounds 206 + .into_styled(PrimitiveStyle::with_fill(BinaryColor::On)) 207 + .draw(&mut display) 208 + .unwrap(); 209 + 210 + let _ = image.draw(&mut display); 211 + let _ = display.update().await; 212 + CHANGE_IMAGE.store(false, core::sync::atomic::Ordering::Relaxed); 213 + } 214 + 215 + Timer::after(Duration::from_millis(500)).await; 181 216 } 182 217 }