Working with sh1106 module to display temp and some averaging

This commit is contained in:
JP Stringham
2025-12-29 10:45:23 -05:00
parent 5576b8ca55
commit 871ec35847
3 changed files with 148 additions and 16 deletions

15
Cargo.lock generated
View File

@@ -339,6 +339,7 @@ dependencies = [
"panic-probe", "panic-probe",
"rp2040-boot2", "rp2040-boot2",
"rp2040-hal", "rp2040-hal",
"sh1106-pico-rs",
] ]
[[package]] [[package]]
@@ -516,6 +517,20 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 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]] [[package]]
name = "stable_deref_trait" name = "stable_deref_trait"
version = "1.2.1" version = "1.2.1"

View File

@@ -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" ]} panic-probe = { version = "1.0.0", features = [ "print-rtt" ]}
rp2040-boot2 = "0.3.0" rp2040-boot2 = "0.3.0"
rp2040-hal = "0.11.0" rp2040-hal = "0.11.0"
sh1106-pico-rs = { path = "../sh1106-pico-rs" }
[features] [features]
logging = [] logging = []

View File

@@ -21,11 +21,13 @@ use rp2040_hal as hal;
// A shorter alias for the Peripheral Access Crate, which provides low-level // A shorter alias for the Peripheral Access Crate, which provides low-level
// register access // register access
use hal::pac; use hal::pac;
use hal::fugit::RateExtU32;
use sh1106_pico_rs::{sh1106::SH1106Dev, graphics::GraphicsBuf};
// Some traits we need // Some traits we need
use embedded_hal::delay::DelayNs;
use embedded_hal::digital::OutputPin; 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 /// 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. /// need this to help the ROM bootloader get our code up and running.
@@ -57,6 +59,7 @@ const SIXTY_C: u16 = 3200;
fn main() -> ! { fn main() -> ! {
// Grab our singleton objects // Grab our singleton objects
let mut pac = pac::Peripherals::take().unwrap(); let mut pac = pac::Peripherals::take().unwrap();
let core = pac::CorePeripherals::take().unwrap();
// Set up the watchdog driver - needed by the clock setup code // Set up the watchdog driver - needed by the clock setup code
let mut watchdog = hal::Watchdog::new(pac.WATCHDOG); let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
@@ -73,8 +76,7 @@ fn main() -> ! {
) )
.unwrap(); .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 // The single-cycle I/O block controls our GPIO pins
let sio = hal::Sio::new(pac.SIO); let sio = hal::Sio::new(pac.SIO);
@@ -88,22 +90,82 @@ fn main() -> ! {
// Configure GPIO25 as an output // Configure GPIO25 as an output
let mut led_pin = pins.gpio25.into_push_pull_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 = 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; 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 { loop {
led_pin.set_high().unwrap(); gfx_buf.clear();
timer.delay_ms(300);
led_pin.set_low().unwrap(); println!("{} samples", adc_fifo.len());
timer.delay_ms(300); for _ in 0..adc_fifo.len() {
let adc_val = adc.read(&mut adc_pin).unwrap(); adc_samps[adc_samp_i] = adc_fifo.read();
println!("ADC {=u16}, samp {=u16}", adc_val, test_val); 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 { let deg = match adc_val {
..ZERO_DEG_C => 0f32, ..ZERO_DEG_C => 0f32,
@@ -118,12 +180,66 @@ fn main() -> ! {
}; };
let deg = (deg * 10.) as u32; 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
// 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
}