1111use cortex_m:: interrupt:: free as disable_interrupts;
1212use rtic:: app;
1313use stm32f0xx_hal:: {
14- gpio:: gpioa:: { PA10 , PA11 , PA12 , PA9 } ,
14+ gpio:: gpioa:: { PA10 , PA11 , PA12 , PA2 , PA3 , PA9 } ,
1515 gpio:: gpiob:: { PB0 , PB1 } ,
1616 gpio:: gpiof:: { PF0 , PF1 } ,
1717 gpio:: { Alternate , Input , Output , PullUp , PushPull , AF1 } ,
@@ -54,28 +54,33 @@ const APP: () = {
5454 /// The FTDI UART header (J105)
5555 serial : serial:: Serial < pac:: USART1 , PA9 < Alternate < AF1 > > , PA10 < Alternate < AF1 > > > ,
5656 /// The Clear-To-Send line on the FTDI UART header (which the serial object can't handle)
57- uart_cts : PA11 < Alternate < AF1 > > ,
57+ pin_uart_cts : PA11 < Alternate < AF1 > > ,
5858 /// The Ready-To-Receive line on the FTDI UART header (which the serial object can't handle)
59- uart_rts : PA12 < Alternate < AF1 > > ,
59+ pin_uart_rts : PA12 < Alternate < AF1 > > ,
6060 /// The power button
6161 button_power : PF0 < Input < PullUp > > ,
6262 /// The reset button
6363 button_reset : PF1 < Input < PullUp > > ,
6464 /// Tracks power button state for short presses. 75ms x 2 = 150ms is a short press
65- button_power_short_press : debouncr:: Debouncer < u8 , debouncr:: Repeat2 > ,
65+ press_button_power_short : debouncr:: Debouncer < u8 , debouncr:: Repeat2 > ,
6666 /// Tracks power button state for long presses. 75ms x 16 = 1200ms is a long press
67- button_power_long_press : debouncr:: Debouncer < u16 , debouncr:: Repeat16 > ,
67+ press_button_power_long : debouncr:: Debouncer < u16 , debouncr:: Repeat16 > ,
6868 /// Tracks DC power state
69- dc_power_enabled : DcPowerState ,
69+ state_dc_power_enabled : DcPowerState ,
70+ /// Controls the DC-DC PSU
71+ pin_dc_on : PA3 < Output < PushPull > > ,
72+ /// Controls the Reset signal across the main board, putting all the
73+ /// chips (except this BMC!) in reset when pulled low.
74+ pin_sys_reset : PA2 < Output < PushPull > > ,
7075 }
7176
7277 /// The entry point to our application.
7378 ///
7479 /// Sets up the hardware and spawns the regular tasks.
7580 ///
76- /// * Task `led_status_blink ` - blinks the LED
81+ /// * Task `led_power_blink ` - blinks the LED
7782 /// * Task `button_poll` - checks the power and reset buttons
78- #[ init( spawn = [ led_status_blink , button_poll] ) ]
83+ #[ init( spawn = [ led_power_blink , button_poll] ) ]
7984 fn init ( ctx : init:: Context ) -> init:: LateResources {
8085 defmt:: info!( "Neotron BMC version {:?} booting" , VERSION ) ;
8186
@@ -100,12 +105,14 @@ const APP: () = {
100105 let (
101106 uart_tx,
102107 uart_rx,
103- uart_cts ,
104- uart_rts ,
108+ pin_uart_cts ,
109+ pin_uart_rts ,
105110 mut led_power,
106111 led_status,
107112 button_power,
108113 button_reset,
114+ mut pin_dc_on,
115+ mut pin_sys_reset,
109116 ) = disable_interrupts ( |cs| {
110117 (
111118 gpioa. pa9 . into_alternate_af1 ( cs) ,
@@ -116,17 +123,22 @@ const APP: () = {
116123 gpiob. pb1 . into_push_pull_output ( cs) ,
117124 gpiof. pf0 . into_pull_up_input ( cs) ,
118125 gpiof. pf1 . into_pull_up_input ( cs) ,
126+ gpioa. pa3 . into_push_pull_output ( cs) ,
127+ gpioa. pa2 . into_push_pull_output ( cs) ,
119128 )
120129 } ) ;
121130
131+ pin_sys_reset. set_low ( ) . unwrap ( ) ;
132+ pin_dc_on. set_low ( ) . unwrap ( ) ;
133+
122134 defmt:: info!( "Creating UART..." ) ;
123135
124136 let mut serial =
125137 serial:: Serial :: usart1 ( dp. USART1 , ( uart_tx, uart_rx) , 115_200 . bps ( ) , & mut rcc) ;
126138
127139 serial. listen ( serial:: Event :: Rxne ) ;
128140
129- ctx. spawn . led_status_blink ( ) . unwrap ( ) ;
141+ ctx. spawn . led_power_blink ( ) . unwrap ( ) ;
130142
131143 ctx. spawn . button_poll ( ) . unwrap ( ) ;
132144
@@ -136,15 +148,17 @@ const APP: () = {
136148
137149 init:: LateResources {
138150 serial,
139- uart_cts ,
140- uart_rts ,
151+ pin_uart_cts ,
152+ pin_uart_rts ,
141153 led_power,
142154 led_status,
143155 button_power,
144156 button_reset,
145- button_power_short_press : debouncr:: debounce_2 ( false ) ,
146- button_power_long_press : debouncr:: debounce_16 ( false ) ,
147- dc_power_enabled : DcPowerState :: Off ,
157+ press_button_power_short : debouncr:: debounce_2 ( false ) ,
158+ press_button_power_long : debouncr:: debounce_16 ( false ) ,
159+ state_dc_power_enabled : DcPowerState :: Off ,
160+ pin_dc_on,
161+ pin_sys_reset,
148162 }
149163 }
150164
@@ -182,78 +196,76 @@ const APP: () = {
182196 ///
183197 /// This task is called periodically. We check whether the status LED is currently on or off,
184198 /// and set it to the opposite. This makes the LED blink.
185- #[ task( schedule = [ led_status_blink ] , resources = [ led_status ] ) ]
186- fn led_status_blink ( ctx : led_status_blink :: Context ) {
199+ #[ task( schedule = [ led_power_blink ] , resources = [ led_power , state_dc_power_enabled ] ) ]
200+ fn led_power_blink ( ctx : led_power_blink :: Context ) {
187201 // Use the safe local `static mut` of RTIC
188202 static mut LED_STATE : bool = false ;
189203
190- defmt:: trace!( "blink time {}" , ctx. scheduled. counts( ) ) ;
191-
192- if * LED_STATE {
193- ctx. resources . led_status . set_low ( ) . unwrap ( ) ;
194- * LED_STATE = false ;
195- } else {
196- ctx. resources . led_status . set_high ( ) . unwrap ( ) ;
197- * LED_STATE = true ;
204+ if * ctx. resources . state_dc_power_enabled == DcPowerState :: Off {
205+ defmt:: trace!( "blink time {}" , ctx. scheduled. counts( ) ) ;
206+ if * LED_STATE {
207+ ctx. resources . led_power . set_low ( ) . unwrap ( ) ;
208+ * LED_STATE = false ;
209+ } else {
210+ ctx. resources . led_power . set_high ( ) . unwrap ( ) ;
211+ * LED_STATE = true ;
212+ }
213+ let next = ctx. scheduled + LED_PERIOD_MS . millis ( ) ;
214+ defmt:: trace!( "Next blink at {}" , next. counts( ) ) ;
215+ ctx. schedule . led_power_blink ( next) . unwrap ( ) ;
198216 }
199- let next = ctx. scheduled + LED_PERIOD_MS . millis ( ) ;
200- defmt:: trace!( "Next blink at {}" , next. counts( ) ) ;
201- ctx. schedule . led_status_blink ( next) . unwrap ( ) ;
202217 }
203218
204219 /// This task polls our power and reset buttons.
205220 ///
206221 /// We poll them rather than setting up an interrupt as we need to debounce them, which involves waiting a short period and checking them again. Given that we have to do that, we might as well not bother with the interrupt.
207- #[ task( schedule = [ button_poll] , resources = [ led_power, button_power, button_power_short_press, button_power_long_press, dc_power_enabled] ) ]
222+ #[ task(
223+ schedule = [ button_poll] ,
224+ spawn = [ led_power_blink] ,
225+ resources = [
226+ led_power, button_power, press_button_power_short, press_button_power_long, state_dc_power_enabled,
227+ pin_sys_reset, pin_dc_on
228+ ]
229+ ) ]
208230 fn button_poll ( ctx : button_poll:: Context ) {
209231 // Poll button
210232 let pressed: bool = ctx. resources . button_power . is_low ( ) . unwrap ( ) ;
211233
212234 // Update state
213- let short_edge = ctx. resources . button_power_short_press . update ( pressed) ;
214- let long_edge = ctx. resources . button_power_long_press . update ( pressed) ;
235+ let short_edge = ctx. resources . press_button_power_short . update ( pressed) ;
236+ let long_edge = ctx. resources . press_button_power_long . update ( pressed) ;
215237
216238 // Dispatch event
217- if short_edge == Some ( debouncr:: Edge :: Rising ) {
218- defmt:: trace!(
219- "Power short press in! {}" ,
220- * ctx. resources. dc_power_enabled as u8
221- ) ;
222- if * ctx. resources . dc_power_enabled == DcPowerState :: Off {
223- * ctx. resources . dc_power_enabled = DcPowerState :: Starting ;
239+ match ( long_edge, short_edge, * ctx. resources . state_dc_power_enabled ) {
240+ ( None , Some ( debouncr:: Edge :: Rising ) , DcPowerState :: Off ) => {
241+ defmt:: info!( "Power button pressed whilst off." ) ;
242+ // Button pressed - power on system
243+ * ctx. resources . state_dc_power_enabled = DcPowerState :: Starting ;
224244 ctx. resources . led_power . set_high ( ) . unwrap ( ) ;
225245 defmt:: info!( "Power on!" ) ;
226- // TODO: Enable DC PSU here
246+ ctx . resources . pin_dc_on . set_high ( ) . unwrap ( ) ;
227247 // TODO: Start monitoring 3.3V and 5.0V rails here
228248 // TODO: Take system out of reset when 3.3V and 5.0V are good
249+ ctx. resources . pin_sys_reset . set_high ( ) . unwrap ( ) ;
229250 }
230- } else if short_edge == Some ( debouncr:: Edge :: Falling ) {
231- defmt:: trace!(
232- "Power short press out! {}" ,
233- * ctx. resources. dc_power_enabled as u8
234- ) ;
235- match * ctx. resources . dc_power_enabled {
236- DcPowerState :: Starting => {
237- * ctx. resources . dc_power_enabled = DcPowerState :: On ;
238- }
239- DcPowerState :: On => {
240- // TODO: Tell host that power off was requested
241- }
242- DcPowerState :: Off => {
243- // Ignore
244- }
251+ ( None , Some ( debouncr:: Edge :: Falling ) , DcPowerState :: Starting ) => {
252+ defmt:: info!( "Power button released." ) ;
253+ // Button released after power on
254+ * ctx. resources . state_dc_power_enabled = DcPowerState :: On ;
245255 }
246- }
247-
248- if long_edge == Some ( debouncr:: Edge :: Rising ) {
249- defmt:: trace!(
250- "Power long press in! {}" ,
251- * ctx. resources. dc_power_enabled as u8
252- ) ;
253- if * ctx. resources . dc_power_enabled == DcPowerState :: On {
254- * ctx. resources . dc_power_enabled = DcPowerState :: Off ;
256+ ( Some ( debouncr:: Edge :: Rising ) , None , DcPowerState :: On ) => {
257+ defmt:: info!( "Power button held whilst on." ) ;
258+ * ctx. resources . state_dc_power_enabled = DcPowerState :: Off ;
255259 ctx. resources . led_power . set_low ( ) . unwrap ( ) ;
256260 defmt:: info!( "Power off!" ) ;
261+ ctx. resources . pin_sys_reset . set_low ( ) . unwrap ( ) ;
262+ // TODO: Wait for 100ms for chips to stop?
263+ ctx. resources . pin_dc_on . set_low ( ) . unwrap ( ) ;
264+ // Start LED blinking again
265+ ctx. spawn . led_power_blink ( ) . unwrap ( ) ;
266+ }
267+ _ => {
268+ // Do nothing
257269 // TODO: Put system in reset here
258270 // TODO: Disable DC PSU here
259271 }
0 commit comments