Rotary dial added to enable target temp changing, GPIO 15 enabled as a gate pin for a mosfet power control

This commit is contained in:
JP Stringham
2026-01-05 13:10:08 -05:00
parent 271069504e
commit 4c32ace581
2 changed files with 172 additions and 16 deletions

View File

@@ -9,6 +9,8 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
mod rotary;
use defmt::println; use defmt::println;
// Ensure we halt the program on panic (if we don't mention this crate it won't // Ensure we halt the program on panic (if we don't mention this crate it won't
// be linked) // be linked)
@@ -27,6 +29,10 @@ use sh1106_pico_rs::{sh1106::SH1106Dev, graphics::GraphicsBuf};
// Some traits we need // Some traits we need
use embedded_hal::digital::OutputPin; use embedded_hal::digital::OutputPin;
use embedded_hal::digital::InputPin;
use crate::rotary::RotaryEnc;
use crate::rotary::RotaryResponse;
/// 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.
@@ -125,7 +131,52 @@ fn main() -> ! {
let mut tick = 0u32; let mut tick = 0u32;
let mut last_gfx_push = 0u32; let mut last_gfx_push = 0u32;
let mut target_temp = 220;
let mut gate_pin = pins.gpio15.into_push_pull_output_in_state(rp2040_hal::gpio::PinState::Low);
let mut rotary = RotaryEnc::default();
// rotary encoder
let mut clk_pin = pins.gpio16.into_pull_up_input();
let mut dt_pin = pins.gpio17.into_pull_up_input();
let mut last_rotary = 0u32;
const CYCLES_PER_FRAME:u32 = 100_000;
const CYCLES_PER_ROTARY:u32 = 10_000;
let mut rotary_input_allowed = true;
loop { loop {
if tick - last_rotary > CYCLES_PER_ROTARY {
rotary_input_allowed = true;
}
if rotary_input_allowed {
let clk_now = clk_pin.is_low().unwrap();
let dt_now = dt_pin.is_low().unwrap();
match rotary.update(clk_now, dt_now) {
RotaryResponse::UP => {
target_temp += 1;
rotary_input_allowed = false;
last_rotary = tick;
// println!("Rotary up");
}
RotaryResponse::DOWN => {
target_temp -= 1;
rotary_input_allowed = false;
last_rotary = tick;
// println!("Rotary dn");
}
_ => ()
}
}
let samples = adc_fifo.len(); let samples = adc_fifo.len();
if samples > 4 { if samples > 4 {
@@ -141,7 +192,7 @@ fn main() -> ! {
tick = tick.wrapping_add(1); tick = tick.wrapping_add(1);
// tweak this magic number for lower/higher fps // tweak this magic number for lower/higher fps
if tick.abs_diff(last_gfx_push) < 100_000 { if tick.abs_diff(last_gfx_push) < CYCLES_PER_FRAME {
continue; continue;
} }
last_gfx_push = tick; last_gfx_push = tick;
@@ -180,7 +231,7 @@ fn main() -> ! {
let adc_val = (adc_avg / accepted_samps) as u16; let adc_val = (adc_avg / accepted_samps) as u16;
println!("ADC {=u16}, samps {}", adc_val, accepted_samps); // 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,
@@ -194,26 +245,32 @@ fn main() -> ! {
} }
}; };
let deg = (deg * 10.) as u32; const START_Y:u8 = 8;
let mut itoa_result = itoa(deg); gfx_buf.draw_string(10, START_Y, "Probe temp");
let probe_temp = (deg * 10.) as u32;
let mut itoa_result = itoa(probe_temp);
let deg_str = itoa_result.as_degrees_decicentigrade_str();
let deg_buf = &mut itoa_result.buf; // println!("tenths of a deg {}, deg {}", probe_temp, deg_str);
for i in 1..10usize { gfx_buf.draw_textfield(10, START_Y+12, 100, 60, deg_str);
deg_buf[i-1] = deg_buf[i];
}
deg_buf[9] = b'.'; gfx_buf.draw_string(10, START_Y+30, "Target Temp");
itoa_result.len += 1;
gfx_buf.draw_string(10, 10, "Probe temp"); gate_pin.set_state(match target_temp > probe_temp {
let deg_str = itoa_result.as_str(); true => rp2040_hal::gpio::PinState::High,
false => rp2040_hal::gpio::PinState::Low
println!("tenths of a deg {}, deg {}", deg, deg_str); }).unwrap();
gfx_buf.draw_textfield(10, 22, 100, 60, deg_str);
// dancing sprite just for heartbeat visibility
gfx_buf.sprites[0].y = 40 + ((test_val as i32) / 4) % 2; gfx_buf.sprites[0].y = 40 + ((test_val as i32) / 4) % 2;
itoa_result = itoa(target_temp);
let deg_str = itoa_result.as_degrees_decicentigrade_str();
gfx_buf.draw_string(10, START_Y+42, deg_str);
gfx_buf.redraw(); gfx_buf.redraw();
sh1106_dev.blit_framebuffer(&mut i2c, &mut gfx_buf); sh1106_dev.blit_framebuffer(&mut i2c, &mut gfx_buf);
@@ -221,7 +278,6 @@ fn main() -> ! {
} }
} }
// Can't return str without a ref to a lifetime somewhere else, // Can't return str without a ref to a lifetime somewhere else,
// so instead return a buffer of the maximum i32 length // so instead return a buffer of the maximum i32 length
pub struct ItoaResult { pub struct ItoaResult {
@@ -242,6 +298,18 @@ impl ItoaResult {
pub fn as_str(&self) -> &str { pub fn as_str(&self) -> &str {
core::str::from_utf8(&self.buf[11usize.checked_sub(self.len).unwrap()..]).unwrap() core::str::from_utf8(&self.buf[11usize.checked_sub(self.len).unwrap()..]).unwrap()
} }
pub fn as_degrees_decicentigrade_str(&mut self) -> &str {
let itoa_buf = &mut self.buf;
for i in 1..10usize {
itoa_buf[i-1] = itoa_buf[i];
}
itoa_buf[9] = b'.';
self.len += 1;
self.as_str()
}
} }
pub fn itoa(mut value: u32) -> ItoaResult { pub fn itoa(mut value: u32) -> ItoaResult {

88
src/rotary.rs Normal file
View File

@@ -0,0 +1,88 @@
#[derive(Clone, Default)]
pub struct RotaryEnc {
state: RotaryEncState,
wait_for_reset: bool,
primed: bool
}
impl RotaryEnc {
pub fn update(&mut self, clk: bool, dt: bool) -> RotaryResponse {
let state_now = RotaryEncState::from_pin_states(clk, dt);
if state_now == self.state {
return RotaryResponse::NOTHING;
}
let response = match state_now {
RotaryEncState::BOTH_OFF => {
self.primed = false;
self.wait_for_reset = false;
RotaryResponse::NOTHING
}
RotaryEncState::CLK_ON | RotaryEncState::DT_ON => {
match (self.wait_for_reset, self.state) {
(false, RotaryEncState::BOTH_OFF) => {
self.primed = true;
}
_ => ()
}
RotaryResponse::NOTHING
},
RotaryEncState::BOTH_ON => {
match (self.primed, self.state) {
(true, RotaryEncState::CLK_ON) => {
self.primed = false;
RotaryResponse::UP
}
(true, RotaryEncState::DT_ON) => {
self.primed = false;
RotaryResponse::DOWN
}
_ => {
self.primed = false;
self.wait_for_reset = true;
RotaryResponse::NOTHING
}
}
}
};
self.state = state_now;
response
}
}
pub enum RotaryResponse {
NOTHING,
UP,
DOWN
}
#[derive(Clone, Copy, Default, PartialEq, Eq)]
enum RotaryEncState {
#[default]
BOTH_OFF,
CLK_ON,
DT_ON,
BOTH_ON
}
impl RotaryEncState {
pub fn from_pin_states(clk: bool, dt: bool) -> Self {
if clk && dt {
return Self::BOTH_ON;
}
if clk {
return Self::CLK_ON;
}
if dt {
return Self::DT_ON;
}
Self::BOTH_OFF
}
}