@@ -16,10 +16,12 @@ use core::convert::TryFrom;
1616use heapless:: spsc:: { Consumer , Producer , Queue } ;
1717use rtic:: app;
1818use stm32f0xx_hal:: {
19- gpio:: gpioa:: { PA10 , PA11 , PA12 , PA15 , PA2 , PA3 , PA4 , PA8 , PA9 } ,
19+ adc,
20+ adc:: Adc ,
21+ gpio:: gpioa:: { PA0 , PA10 , PA11 , PA12 , PA15 , PA2 , PA3 , PA4 , PA8 , PA9 } ,
2022 gpio:: gpiob:: { PB0 , PB3 , PB4 , PB5 } ,
2123 gpio:: gpiof:: { PF0 , PF1 } ,
22- gpio:: { Alternate , Floating , Input , Output , PullDown , PullUp , PushPull , AF1 } ,
24+ gpio:: { Alternate , Analog , Floating , Input , Output , PullDown , PullUp , PushPull , AF1 } ,
2325 pac,
2426 prelude:: * ,
2527 rcc, serial,
@@ -41,6 +43,10 @@ const DEBOUNCE_POLL_INTERVAL_MS: u64 = 75;
4143/// Length of a reset pulse, in milliseconds
4244const RESET_DURATION_MS : u64 = 250 ;
4345
46+ /// Minimum voltage level of the 3.3V rail before we start
47+ /// driving the IRQ pin high, to avoid back-powering.
48+ const POWER_GOOD_THRESHOLD_MV : u16 = 3200 ;
49+
4450/// The states we can be in controlling the DC power
4551#[ derive( Copy , Clone , PartialEq , Eq ) ]
4652#[ repr( u8 ) ]
@@ -170,6 +176,10 @@ mod app {
170176 rcc : Option < rcc:: Rcc > ,
171177 /// IRQ pin
172178 pin_irq : PA8 < Output < PushPull > > ,
179+ /// 3.3V monitor pin
180+ pin_3v3_monitor : PA0 < Analog > ,
181+ /// ADC
182+ adc : Adc ,
173183 }
174184
175185 #[ monotonic( binds = SysTick , default = true ) ]
@@ -204,6 +214,14 @@ mod app {
204214 // Initialize the monotonic timer using the Cortex-M SysTick peripheral
205215 let mono = Systick :: new ( cp. SYST , rcc. clocks . sysclk ( ) . 0 ) ;
206216
217+ let mut adc = Adc :: new ( dp. ADC , & mut rcc) ;
218+ // 8 bit precision should be plenty for our use case
219+ adc. set_precision ( adc:: AdcPrecision :: B_8 ) ;
220+ // Approx. sampling time required to charge the 8pF sample & hold
221+ // capacitor through the 10kΩ resistor and to get a reading with 8 bit accuracy:
222+ // 8pF * 10kΩ * ln(2^8) ≈ 440ns ≈ 6 ADC clock cycles
223+ adc. set_sample_time ( adc:: AdcSampleTime :: T_7 ) ;
224+
207225 defmt:: info!( "Creating pins..." ) ;
208226 let gpioa = dp. GPIOA . split ( & mut rcc) ;
209227 let gpiob = dp. GPIOB . split ( & mut rcc) ;
@@ -236,6 +254,8 @@ mod app {
236254 pin_cipo,
237255 pin_copi,
238256 mut pin_irq,
257+ pin_3v3_monitor,
258+ adc,
239259 ) = cortex_m:: interrupt:: free ( |cs| {
240260 (
241261 // uart_tx,
@@ -281,6 +301,10 @@ mod app {
281301 gpioa. pa7 . into_alternate_af0 ( cs) ,
282302 // pin_irq
283303 gpioa. pa8 . into_push_pull_output ( cs) ,
304+ // pin_3v3_monitor
305+ gpioa. pa0 . into_analog ( cs) ,
306+ // ADC
307+ adc,
284308 )
285309 } ) ;
286310
@@ -364,6 +388,8 @@ mod app {
364388 press_button_reset_short : debouncr:: debounce_2 ( false ) ,
365389 rcc : Some ( rcc) ,
366390 pin_irq,
391+ pin_3v3_monitor,
392+ adc,
367393 } ;
368394 let init = init:: Monotonics ( mono) ;
369395 ( shared_resources, local_resources, init)
@@ -372,7 +398,7 @@ mod app {
372398 /// Our idle task.
373399 ///
374400 /// This task is called when there is nothing else to do.
375- #[ idle( shared = [ msg_q_out, msg_q_in, spi, state_dc_power_enabled, pin_dc_on, pin_sys_reset, speaker] , local = [ pin_irq, rcc, speaker_task_handle: Option <speaker_pwm_stop:: MyMono :: SpawnHandle > = None ] ) ]
401+ #[ idle( shared = [ msg_q_out, msg_q_in, spi, state_dc_power_enabled, pin_dc_on, pin_sys_reset, speaker] , local = [ pin_irq, rcc, speaker_task_handle: Option <speaker_pwm_stop:: MyMono :: SpawnHandle > = None , adc , pin_3v3_monitor ] ) ]
376402 fn idle ( mut ctx : idle:: Context ) -> ! {
377403 // TODO: Get this from the VERSION static variable or from PKG_VERSION
378404 let mut register_state = RegisterState {
@@ -420,7 +446,8 @@ mod app {
420446 }
421447 }
422448 Some ( Message :: PowerButtonLongPress ) => {
423- if ctx. shared . state_dc_power_enabled . lock ( |r| * r) == DcPowerState :: On {
449+ let power_state = ctx. shared . state_dc_power_enabled . lock ( |r| * r) ;
450+ if power_state == DcPowerState :: On || power_state == DcPowerState :: Starting {
424451 defmt:: info!( "Power off requested!" ) ;
425452 ctx. shared
426453 . state_dc_power_enabled
@@ -452,26 +479,13 @@ mod app {
452479 ctx. shared . pin_sys_reset . lock ( |pin| pin. set_low ( ) . unwrap ( ) ) ;
453480 // Step 4 - Turn on PSU
454481 ctx. shared . pin_dc_on . set_high ( ) . unwrap ( ) ;
455- // Step 5 - Leave it in reset for a while.
456- // TODO: Start monitoring 3.3V and 5.0V rails here
457- // TODO: Take system out of reset when 3.3V and 5.0V are good
458- // Returns an error if it's already scheduled (but we don't care)
459- let _ = exit_reset:: spawn_after ( RESET_DURATION_MS . millis ( ) ) ;
460- // Set 6 - unmask the IRQ
461- irq_forced_low = false ;
482+ // Taking the system out of reset and enabling the IRQ line happens
483+ // later, when the power rail is settled
484+ defmt:: info!( "Waiting for power-good" ) ;
462485 }
463486 }
464487 Some ( Message :: PowerButtonRelease ) => {
465- if ctx. shared . state_dc_power_enabled . lock ( |r| * r) == DcPowerState :: Starting {
466- defmt:: info!( "Power button released." ) ;
467- // Button released after power on. Change the power
468- // state machine t "On". We were in 'Starting' to ignore
469- // any further button events until the button had been
470- // released.
471- ctx. shared
472- . state_dc_power_enabled
473- . lock ( |r| * r = DcPowerState :: On ) ;
474- }
488+ defmt:: info!( "Power button released." ) ;
475489 }
476490 Some ( Message :: ResetButtonShortPress ) => {
477491 // Is the board powered on? Don't do a reset if it's powered off.
@@ -493,7 +507,7 @@ mod app {
493507 }
494508 }
495509 Some ( Message :: SpiEnable ) => {
496- if ctx. shared . state_dc_power_enabled . lock ( |r| * r) != DcPowerState :: Off {
510+ if ctx. shared . state_dc_power_enabled . lock ( |r| * r) == DcPowerState :: On {
497511 // Turn on the SPI peripheral and expect four bytes (the
498512 // length of a Request).
499513 ctx. shared . spi . lock ( |s| s. start ( 4 ) ) ;
@@ -583,7 +597,51 @@ mod app {
583597 ) ;
584598 }
585599 }
586- // TODO: Read ADC for 3.3V and 5.0V rails and check good
600+ // TODO: Also monitor 5.0V rail
601+
602+ // Wait for power-good
603+ if ctx. shared . state_dc_power_enabled . lock ( |r| * r) == DcPowerState :: Starting {
604+ // Reads the absolute voltage of the mon_3v3 line in mV.
605+ // This line is connected to the 3v3 supply through a 50% voltage divider,
606+ // so it should read 1650[mV] nominally.
607+ //
608+ // Note that read_abs_mv is relatively slow, as it internally reads
609+ // ADC values from both the pin and an internal voltage reference, and
610+ // does calculations including integer divisions.
611+ //
612+ // As we do not really require an accurate absolute voltage, but only
613+ // need to be sure that the 3v3 rail is reasonably close to the 3.3VP
614+ // rail (the permanent power rail supplying the BMC), this could be
615+ // rewritten using `ctx.local.adc.read(ctx.local.pin_3v3_monitor)`
616+ // in case performance becomes an issue.
617+ let mon_3v3 = ctx. local . adc . read_abs_mv ( ctx. local . pin_3v3_monitor ) ;
618+ defmt:: trace!( "mon_3v3 reading: {} mV" , mon_3v3) ;
619+ if mon_3v3 < POWER_GOOD_THRESHOLD_MV / 2 {
620+ defmt:: info!(
621+ "mon_3v3 below threshold of {} mV: {} mV" ,
622+ POWER_GOOD_THRESHOLD_MV / 2 ,
623+ mon_3v3
624+ ) ;
625+ } else {
626+ defmt:: info!(
627+ "Power good. Mon_3v3 at {} mV. Continue with startup sequence." ,
628+ mon_3v3
629+ ) ;
630+ // Change the power state machine to "On". We were in 'Starting' to ignore
631+ // any further button events until the button had been
632+ // released and to wait for power good.
633+ ctx. shared
634+ . state_dc_power_enabled
635+ . lock ( |r| * r = DcPowerState :: On ) ;
636+ // Wait a bit before taking system out of reset.
637+ // Returns an error if it's already scheduled (but we don't care)
638+ let _ = exit_reset:: spawn_after ( RESET_DURATION_MS . millis ( ) ) ;
639+ // Unmask the IRQ
640+ irq_forced_low = false ;
641+ }
642+ }
643+ // TODO: Shutdown system if mon_3v3 falls below threshold
644+ // TODO: Maybe report voltages to CPU?
587645 }
588646 }
589647
@@ -797,7 +855,7 @@ mod app {
797855 #[ task( shared = [ pin_sys_reset, state_dc_power_enabled] ) ]
798856 fn exit_reset ( mut ctx : exit_reset:: Context ) {
799857 defmt:: debug!( "End reset" ) ;
800- if ctx. shared . state_dc_power_enabled . lock ( |r| * r) != DcPowerState :: Off {
858+ if ctx. shared . state_dc_power_enabled . lock ( |r| * r) == DcPowerState :: On {
801859 // Raising the reset line takes the rest of the system out of reset
802860 ctx. shared . pin_sys_reset . lock ( |pin| pin. set_high ( ) . unwrap ( ) ) ;
803861 }
0 commit comments