From 871ec3584767c86d59134ab50b35ba7cca820998 Mon Sep 17 00:00:00 2001 From: JP Stringham Date: Mon, 29 Dec 2025 10:45:23 -0500 Subject: [PATCH] Working with sh1106 module to display temp and some averaging --- Cargo.lock | 15 ++++++ Cargo.toml | 1 + src/main.rs | 148 ++++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 148 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e5bc94c..3e3ac4d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -339,6 +339,7 @@ dependencies = [ "panic-probe", "rp2040-boot2", "rp2040-hal", + "sh1106-pico-rs", ] [[package]] @@ -516,6 +517,20 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "sh1106-pico-rs" +version = "0.1.0" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "defmt", + "defmt-rtt", + "embedded-hal 1.0.0", + "panic-probe", + "rp2040-boot2", + "rp2040-hal", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" diff --git a/Cargo.toml b/Cargo.toml index 7d9b714..26282a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ embedded_hal_0_2 = {package = "embedded-hal", version = "0.2.5", features = ["un panic-probe = { version = "1.0.0", features = [ "print-rtt" ]} rp2040-boot2 = "0.3.0" rp2040-hal = "0.11.0" +sh1106-pico-rs = { path = "../sh1106-pico-rs" } [features] logging = [] \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 792e46b..5f61544 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,11 +21,13 @@ use rp2040_hal as hal; // A shorter alias for the Peripheral Access Crate, which provides low-level // register access use hal::pac; +use hal::fugit::RateExtU32; + +use sh1106_pico_rs::{sh1106::SH1106Dev, graphics::GraphicsBuf}; // Some traits we need -use embedded_hal::delay::DelayNs; use embedded_hal::digital::OutputPin; -use embedded_hal_0_2::adc::OneShot; +use embedded_hal_0_2::{adc::OneShot, digital::v2::ToggleableOutputPin}; /// The linker will place this boot block at the start of our program image. We /// need this to help the ROM bootloader get our code up and running. @@ -57,6 +59,7 @@ const SIXTY_C: u16 = 3200; fn main() -> ! { // Grab our singleton objects let mut pac = pac::Peripherals::take().unwrap(); + let core = pac::CorePeripherals::take().unwrap(); // Set up the watchdog driver - needed by the clock setup code let mut watchdog = hal::Watchdog::new(pac.WATCHDOG); @@ -73,8 +76,7 @@ fn main() -> ! { ) .unwrap(); - let mut timer = rp2040_hal::Timer::new(pac.TIMER, &mut pac.RESETS, &clocks); - + let mut delay = cortex_m::delay::Delay::new(core.SYST, 133_000_000u32); // The single-cycle I/O block controls our GPIO pins let sio = hal::Sio::new(pac.SIO); @@ -88,22 +90,82 @@ fn main() -> ! { // Configure GPIO25 as an output let mut led_pin = pins.gpio25.into_push_pull_output(); + led_pin.set_high().unwrap(); let mut adc = hal::Adc::new(pac.ADC, &mut pac.RESETS); - let mut adc_pin = hal::adc::AdcPin::new(pins.gpio26).unwrap(); + let mut adc_pin = hal::adc::AdcPin::new(pins.gpio26.into_pull_down_input()).unwrap(); - adc.free_running(&adc_pin); + let mut adc_fifo = adc.build_fifo().clock_divider(40000, 0).set_channel(&mut adc_pin).start(); + // adc.free_running(&adc_pin); + + let mut adc_samps = [0u16;30]; + let mut adc_samp_i = 0usize; let mut test_val = 0u16; + // Create the I²C drive + let mut i2c = hal::I2C::i2c0( + pac.I2C0, + pins.gpio8.reconfigure(), + pins.gpio9.reconfigure(), + 400.kHz(), + &mut pac.RESETS, + &clocks.system_clock, + ); + + let sh1106_dev = SH1106Dev::new(&mut delay, &mut i2c); + let mut gfx_buf = GraphicsBuf::new(); + + println!("Graphics ready"); + led_pin.set_low().unwrap(); + loop { - led_pin.set_high().unwrap(); - timer.delay_ms(300); - led_pin.set_low().unwrap(); - timer.delay_ms(300); - let adc_val = adc.read(&mut adc_pin).unwrap(); - println!("ADC {=u16}, samp {=u16}", adc_val, test_val); + gfx_buf.clear(); + + println!("{} samples", adc_fifo.len()); + for _ in 0..adc_fifo.len() { + adc_samps[adc_samp_i] = adc_fifo.read(); + adc_samp_i += 1; + if adc_samp_i >= 30 { + adc_samp_i = 0; + } + } + + let mut adc_hi = 0u16; + let mut adc_lo = 0xFFFFu16; + + adc_samps.iter().for_each(|v| { + if adc_hi < *v { + adc_hi = *v; + } + if adc_lo > *v { + adc_lo = *v; + } + }); + + let mut accepted_samps = 0u32; + let mut adc_avg = 0u32; + + adc_samps.iter().for_each(|v| { + if *v >= adc_hi { + return; + } + if *v <= adc_lo { + return; + } + accepted_samps += 1; + adc_avg += *v as u32; + }); + + if accepted_samps < 10 { + println!("Not enough accepted samples, skipping"); + continue; + } + + let adc_val = (adc_avg / accepted_samps) as u16; + + println!("ADC {=u16}, samps {}", adc_val, accepted_samps); let deg = match adc_val { ..ZERO_DEG_C => 0f32, @@ -118,12 +180,66 @@ fn main() -> ! { }; let deg = (deg * 10.) as u32; - let deg = (deg as f32) / 10.; + let mut itoa_result = itoa(deg); - println!("Deg? {=f32}", deg); - test_val += 1; + let deg_buf = &mut itoa_result.buf; + for i in 1..10usize { + deg_buf[i-1] = deg_buf[i]; + } + + deg_buf[9] = b'.'; + itoa_result.len += 1; + + let deg_str = itoa_result.as_str(); + + println!("tenths of a deg {}, deg {}", deg, deg_str); + gfx_buf.draw_textfield(10, 10, 100, 60, deg_str); + + gfx_buf.sprites[0].y = 40 + ((test_val as i32) / 4) % 2; + + gfx_buf.redraw(); + sh1106_dev.blit_framebuffer(&mut i2c, &mut gfx_buf); + + test_val = test_val.wrapping_add(1); + delay.delay_ms(30); } } -// End of file \ No newline at end of file + +// Can't return str without a ref to a lifetime somewhere else, +// so instead return a buffer of the maximum i32 length +pub struct ItoaResult { + pub buf: [u8; 11], + pub len: usize, +} + +impl ItoaResult { + pub fn new() -> Self { + Self { buf: [0u8; 11], len: 0 } + } + + fn push(&mut self, c: u8) { + self.len += 1; + self.buf[11usize.checked_sub(self.len).unwrap()] = c; + } + + pub fn as_str(&self) -> &str { + core::str::from_utf8(&self.buf[11usize.checked_sub(self.len).unwrap()..]).unwrap() + } +} + +pub fn itoa(mut value: u32) -> ItoaResult { + let mut result = ItoaResult::new(); + + loop { + result.push(b'0' + u8::try_from(value % 10).unwrap()); + value /= 10; + // check at end of loop, so 0 prints to "0" + if value == 0 { + break; + } + } + + result +}