Skip to content

Commit f84333a

Browse files
committed
Add support for power button.
Short press turns PSU on. Long press turns PSU off.
1 parent 715b6f8 commit f84333a

2 files changed

Lines changed: 131 additions & 23 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ defmt-rtt = "0.2.0"
1515
cortex-m-rtic = "0.5"
1616
panic-probe = { version = "0.2.0", features = ["print-defmt"] }
1717
stm32f0xx-hal = { version = "0.17", features = ["stm32f031", "rt"] }
18-
18+
debouncr = "0.2"
1919

2020
[features]
2121
# set logging levels here

src/main.rs

Lines changed: 130 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ use rtic::app;
1212
use stm32f0xx_hal::{
1313
gpio::gpioa::{PA10, PA11, PA12, PA9},
1414
gpio::gpiob::{PB0, PB1},
15-
gpio::{Alternate, Output, PushPull, AF1},
15+
gpio::gpiof::{PF0, PF1},
16+
gpio::{Alternate, Input, Output, PullUp, PushPull, AF1},
1617
pac,
1718
prelude::*,
1819
serial,
@@ -22,19 +23,47 @@ use cortex_m::interrupt::free as disable_interrupts;
2223

2324
static VERSION: &'static str = include_str!(concat!(env!("OUT_DIR"), "/version.txt"));
2425

25-
const PERIOD_MS: u16 = 1000;
26+
const LED_PERIOD_MS: u16 = 1000;
27+
const DEBOUNCE_POLL_INTERVAL_MS: u16 = 75;
28+
29+
/// The states we can be in controlling the DC power
30+
#[derive(Copy, Clone, PartialEq, Eq)]
31+
#[repr(u8)]
32+
pub enum DcPowerState {
33+
/// We've just enabled the DC power (so ignore any incoming long presses!)
34+
Starting = 1,
35+
/// We are now fully on. Look for a long press to turn off.
36+
On = 2,
37+
/// We are fully off.
38+
Off = 0,
39+
}
2640

2741
#[app(device = crate::pac, peripherals = true, monotonic = crate::Tim3Monotonic)]
2842
const APP: () = {
2943
struct Resources {
30-
uart_cts: PA11<Alternate<AF1>>,
31-
uart_rts: PA12<Alternate<AF1>>,
44+
/// The power LED (D1101)
3245
led_power: PB0<Output<PushPull>>,
46+
/// The status LED (D1102)
3347
led_status: PB1<Output<PushPull>>,
48+
/// The FTDI UART header (J105)
3449
serial: serial::Serial<pac::USART1, PA9<Alternate<AF1>>, PA10<Alternate<AF1>>>,
50+
/// The Clear-To-Send line on the FTDI UART header (which the serial object can't handle)
51+
uart_cts: PA11<Alternate<AF1>>,
52+
/// The Ready-To-Receive line on the FTDI UART header (which the serial object can't handle)
53+
uart_rts: PA12<Alternate<AF1>>,
54+
/// The power button
55+
button_power: PF0<Input<PullUp>>,
56+
/// The reset button
57+
button_reset: PF1<Input<PullUp>>,
58+
/// Tracks power button state for short presses. 75ms x 2 = 150ms is a short press
59+
button_power_short_press: debouncr::Debouncer<u8, debouncr::Repeat2>,
60+
/// Tracks power button state for long presses. 75ms x 16 = 1200ms is a long press
61+
button_power_long_press: debouncr::Debouncer<u16, debouncr::Repeat16>,
62+
/// Tracks DC power state
63+
dc_power_enabled: DcPowerState,
3564
}
3665

37-
#[init(spawn = [led_status_blink])]
66+
#[init(spawn = [led_status_blink, button_poll])]
3867
fn init(ctx: init::Context) -> init::LateResources {
3968
defmt::info!("Neotron BMC version {:?} booting", VERSION);
4069

@@ -55,17 +84,28 @@ const APP: () = {
5584
defmt::info!("Creating pins...");
5685
let gpioa = dp.GPIOA.split(&mut rcc);
5786
let gpiob = dp.GPIOB.split(&mut rcc);
58-
let (uart_tx, uart_rx, uart_cts, uart_rts, mut led_power, led_status) =
59-
disable_interrupts(|cs| {
60-
(
61-
gpioa.pa9.into_alternate_af1(cs),
62-
gpioa.pa10.into_alternate_af1(cs),
63-
gpioa.pa11.into_alternate_af1(cs),
64-
gpioa.pa12.into_alternate_af1(cs),
65-
gpiob.pb0.into_push_pull_output(cs),
66-
gpiob.pb1.into_push_pull_output(cs),
67-
)
68-
});
87+
let gpiof = dp.GPIOF.split(&mut rcc);
88+
let (
89+
uart_tx,
90+
uart_rx,
91+
uart_cts,
92+
uart_rts,
93+
mut led_power,
94+
led_status,
95+
button_power,
96+
button_reset,
97+
) = disable_interrupts(|cs| {
98+
(
99+
gpioa.pa9.into_alternate_af1(cs),
100+
gpioa.pa10.into_alternate_af1(cs),
101+
gpioa.pa11.into_alternate_af1(cs),
102+
gpioa.pa12.into_alternate_af1(cs),
103+
gpiob.pb0.into_push_pull_output(cs),
104+
gpiob.pb1.into_push_pull_output(cs),
105+
gpiof.pf0.into_pull_up_input(cs),
106+
gpiof.pf1.into_pull_up_input(cs),
107+
)
108+
});
69109

70110
defmt::info!("Creating UART...");
71111

@@ -76,7 +116,9 @@ const APP: () = {
76116

77117
ctx.spawn.led_status_blink().unwrap();
78118

79-
led_power.set_high().unwrap();
119+
ctx.spawn.button_poll().unwrap();
120+
121+
led_power.set_low().unwrap();
80122

81123
defmt::info!("Init complete!");
82124

@@ -86,15 +128,20 @@ const APP: () = {
86128
uart_rts,
87129
led_power,
88130
led_status,
131+
button_power,
132+
button_reset,
133+
button_power_short_press: debouncr::debounce_2(false),
134+
button_power_long_press: debouncr::debounce_16(false),
135+
dc_power_enabled: DcPowerState::Off,
89136
}
90137
}
91138

92139
#[idle(resources = [])]
93-
fn idle(_: idle::Context) -> ! {
140+
fn idle(_ctx: idle::Context) -> ! {
94141
defmt::info!("Idle is running...");
95142
loop {
96143
cortex_m::asm::wfi();
97-
defmt::info!("It is now {}", crate::Instant::now().counts());
144+
defmt::trace!("It is now {}", crate::Instant::now().counts());
98145
}
99146
}
100147

@@ -116,7 +163,7 @@ const APP: () = {
116163
// Use the safe local `static mut` of RTIC
117164
static mut LED_STATE: bool = false;
118165

119-
defmt::info!("blink time {}", ctx.scheduled.counts());
166+
defmt::trace!("blink time {}", ctx.scheduled.counts());
120167

121168
if *LED_STATE {
122169
ctx.resources.led_status.set_low().unwrap();
@@ -125,11 +172,72 @@ const APP: () = {
125172
ctx.resources.led_status.set_high().unwrap();
126173
*LED_STATE = true;
127174
}
128-
let next = ctx.scheduled + PERIOD_MS.millis();
129-
defmt::info!("Next blink at {}", next.counts());
175+
let next = ctx.scheduled + LED_PERIOD_MS.millis();
176+
defmt::trace!("Next blink at {}", next.counts());
130177
ctx.schedule.led_status_blink(next).unwrap();
131178
}
132179

180+
#[task(schedule = [button_poll], resources = [led_power, button_power, button_power_short_press, button_power_long_press, dc_power_enabled])]
181+
fn button_poll(ctx: button_poll::Context) {
182+
// Poll button
183+
let pressed: bool = ctx.resources.button_power.is_low().unwrap();
184+
185+
// Update state
186+
let short_edge = ctx.resources.button_power_short_press.update(pressed);
187+
let long_edge = ctx.resources.button_power_long_press.update(pressed);
188+
189+
// Dispatch event
190+
if short_edge == Some(debouncr::Edge::Rising) {
191+
defmt::trace!(
192+
"Power short press in! {}",
193+
*ctx.resources.dc_power_enabled as u8
194+
);
195+
if *ctx.resources.dc_power_enabled == DcPowerState::Off {
196+
*ctx.resources.dc_power_enabled = DcPowerState::Starting;
197+
ctx.resources.led_power.set_high().unwrap();
198+
defmt::info!("Power on!");
199+
// TODO: Enable DC PSU here
200+
// TODO: Start monitoring 3.3V and 5.0V rails here
201+
// TODO: Take system out of reset when 3.3V and 5.0V are good
202+
}
203+
} else if short_edge == Some(debouncr::Edge::Falling) {
204+
defmt::trace!(
205+
"Power short press out! {}",
206+
*ctx.resources.dc_power_enabled as u8
207+
);
208+
match *ctx.resources.dc_power_enabled {
209+
DcPowerState::Starting => {
210+
*ctx.resources.dc_power_enabled = DcPowerState::On;
211+
}
212+
DcPowerState::On => {
213+
// TODO: Tell host that power off was requested
214+
}
215+
DcPowerState::Off => {
216+
// Ignore
217+
}
218+
}
219+
}
220+
221+
if long_edge == Some(debouncr::Edge::Rising) {
222+
defmt::trace!(
223+
"Power long press in! {}",
224+
*ctx.resources.dc_power_enabled as u8
225+
);
226+
if *ctx.resources.dc_power_enabled == DcPowerState::On {
227+
*ctx.resources.dc_power_enabled = DcPowerState::Off;
228+
ctx.resources.led_power.set_low().unwrap();
229+
defmt::info!("Power off!");
230+
// TODO: Put system in reset here
231+
// TODO: Disable DC PSU here
232+
}
233+
}
234+
235+
// Re-schedule the timer interrupt
236+
ctx.schedule
237+
.button_poll(ctx.scheduled + DEBOUNCE_POLL_INTERVAL_MS.millis())
238+
.unwrap();
239+
}
240+
133241
// Let it use the USB interrupt as a generic software interrupt.
134242
extern "C" {
135243
fn USB();

0 commit comments

Comments
 (0)