@@ -12,7 +12,8 @@ use rtic::app;
1212use 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
2324static 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 ) ]
2842const 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