@@ -17,7 +17,7 @@ use heapless::spsc::{Consumer, Producer, Queue};
1717use rtic:: app;
1818use stm32f0xx_hal:: {
1919 gpio:: gpioa:: { PA10 , PA11 , PA12 , PA15 , PA2 , PA3 , PA4 , PA8 , PA9 } ,
20- gpio:: gpiob:: { PB0 , PB1 , PB3 , PB4 , PB5 } ,
20+ gpio:: gpiob:: { PB0 , PB3 , PB4 , PB5 } ,
2121 gpio:: gpiof:: { PF0 , PF1 } ,
2222 gpio:: { Alternate , Floating , Input , Output , PullDown , PullUp , PushPull , AF1 } ,
2323 pac,
@@ -26,7 +26,7 @@ use stm32f0xx_hal::{
2626} ;
2727
2828use neotron_bmc_commands:: Command ;
29- use neotron_bmc_pico as _;
29+ use neotron_bmc_pico:: { self as _, speaker } ;
3030use neotron_bmc_protocol as proto;
3131
3232/// Version string auto-generated by git.
@@ -66,6 +66,8 @@ pub struct RegisterState {
6666 /// without re-doing a FIFO read. This happens if our response gets a CRC
6767 /// error.
6868 last_req : Option < proto:: Request > ,
69+ /// The config of the speaker
70+ speaker : speaker:: RegisterState ,
6971}
7072
7173#[ app( device = crate :: pac, peripherals = true , dispatchers = [ USB , USART3_4_5_6 , TIM14 , TIM15 , TIM16 , TIM17 , PVD ] ) ]
@@ -94,16 +96,17 @@ mod app {
9496 ResetButtonShortPress ,
9597 /// The UART got some data
9698 UartByte ( u8 ) ,
99+ /// The speaker's config should be reset
100+ SpeakerDisable ,
97101 }
98102
99103 #[ shared]
100104 struct Shared {
101105 /// The power LED (D1101)
102106 #[ lock_free]
103107 led_power : PB0 < Output < PushPull > > ,
104- /// The status LED (D1102)
105- #[ lock_free]
106- _buzzer_pwm : PB1 < Output < PushPull > > ,
108+ /// The speaker (J1006)
109+ speaker : speaker:: Hardware ,
107110 /// The FTDI UART header (J105)
108111 #[ lock_free]
109112 serial : serial:: Serial < pac:: USART1 , PA9 < Alternate < AF1 > > , PA10 < Alternate < AF1 > > > ,
@@ -219,7 +222,7 @@ mod app {
219222 _pin_uart_cts,
220223 _pin_uart_rts,
221224 mut led_power,
222- mut _buzzer_pwm ,
225+ _speaker_pwm ,
223226 button_power,
224227 button_reset,
225228 mut pin_dc_on,
@@ -245,8 +248,8 @@ mod app {
245248 gpioa. pa12 . into_alternate_af1 ( cs) ,
246249 // led_power,
247250 gpiob. pb0 . into_push_pull_output ( cs) ,
248- // _buzzer_pwm ,
249- gpiob. pb1 . into_push_pull_output ( cs) ,
251+ // speaker_pwm ,
252+ gpiob. pb1 . into_alternate_af0 ( cs) ,
250253 // button_power,
251254 gpiof. pf0 . into_pull_up_input ( cs) ,
252255 // button_reset,
@@ -289,8 +292,6 @@ mod app {
289292 pin_irq. set_high ( ) . unwrap ( ) ;
290293 // Power LED is off
291294 led_power. set_low ( ) . unwrap ( ) ;
292- // Buzzer is off
293- _buzzer_pwm. set_low ( ) . unwrap ( ) ;
294295
295296 defmt:: info!( "Creating UART..." ) ;
296297
@@ -306,6 +307,10 @@ mod app {
306307 & mut rcc,
307308 ) ;
308309
310+ led_power. set_low ( ) . unwrap ( ) ;
311+
312+ speaker:: RegisterState :: default ( ) . setup ( & mut rcc, & dp. TIM14 ) ;
313+
309314 // Set EXTI15 to use PORT A (PA15) - button input
310315 dp. SYSCFG . exticr4 . modify ( |_r, w| w. exti15 ( ) . pa15 ( ) ) ;
311316
@@ -336,7 +341,7 @@ mod app {
336341 _pin_uart_cts,
337342 _pin_uart_rts,
338343 led_power,
339- _buzzer_pwm ,
344+ speaker : speaker :: Hardware :: new ( dp . TIM14 ) ,
340345 button_power,
341346 button_reset,
342347 state_dc_power_enabled : DcPowerState :: Off ,
@@ -367,7 +372,7 @@ mod app {
367372 /// Our idle task.
368373 ///
369374 /// This task is called when there is nothing else to do.
370- #[ idle( shared = [ msg_q_out, msg_q_in, spi, state_dc_power_enabled, pin_dc_on, pin_sys_reset] , local = [ rcc, pin_irq ] ) ]
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 ] ) ]
371376 fn idle ( mut ctx : idle:: Context ) -> ! {
372377 // TODO: Get this from the VERSION static variable or from PKG_VERSION
373378 let mut register_state = RegisterState {
@@ -436,20 +441,23 @@ mod app {
436441 if ctx. shared . state_dc_power_enabled . lock ( |r| * r) == DcPowerState :: Off {
437442 defmt:: info!( "Power up requested!" ) ;
438443 // Button pressed - power on system.
439- // Step 1 - Note our new power state
444+ // Step 1 - enable speaker and play power-up tune
445+ ctx. shared . speaker . lock ( |speaker| speaker. enable ( ) ) ;
446+ speaker_init_tune:: spawn ( ) . unwrap ( ) ;
447+ // Step 2 - Note our new power state
440448 ctx. shared
441449 . state_dc_power_enabled
442450 . lock ( |r| * r = DcPowerState :: Starting ) ;
443- // Step 2 - Hold reset line (active) low
451+ // Step 3 - Hold reset line (active) low
444452 ctx. shared . pin_sys_reset . lock ( |pin| pin. set_low ( ) . unwrap ( ) ) ;
445- // Step 3 - Turn on PSU
453+ // Step 4 - Turn on PSU
446454 ctx. shared . pin_dc_on . set_high ( ) . unwrap ( ) ;
447- // Step 4 - Leave it in reset for a while.
455+ // Step 5 - Leave it in reset for a while.
448456 // TODO: Start monitoring 3.3V and 5.0V rails here
449457 // TODO: Take system out of reset when 3.3V and 5.0V are good
450458 // Returns an error if it's already scheduled (but we don't care)
451459 let _ = exit_reset:: spawn_after ( RESET_DURATION_MS . millis ( ) ) ;
452- // Set 5 - unmask the IRQ
460+ // Set 6 - unmask the IRQ
453461 irq_masked = false ;
454462 }
455463 }
@@ -469,7 +477,13 @@ mod app {
469477 // Is the board powered on? Don't do a reset if it's powered off.
470478 if ctx. shared . state_dc_power_enabled . lock ( |r| * r) == DcPowerState :: On {
471479 defmt:: info!( "Reset!" ) ;
472- // Step 1 - Stop any SPI stuff that's currently going on (the host is about to be reset)
480+ ctx. shared . pin_sys_reset . lock ( |pin| pin. set_low ( ) . unwrap ( ) ) ;
481+
482+ // play power-up tune
483+ ctx. shared . speaker . lock ( |speaker| speaker. enable ( ) ) ;
484+ speaker_init_tune:: spawn ( ) . unwrap ( ) ;
485+
486+ ctx. shared . pin_sys_reset . lock ( |pin| pin. set_low ( ) . unwrap ( ) ) ;
473487 ctx. shared . spi . lock ( |s| s. reset ( & mut rcc) ) ;
474488 // Step 2 - Hold reset line (active) low
475489 ctx. shared . pin_sys_reset . lock ( |pin| pin. set_low ( ) . unwrap ( ) ) ;
@@ -532,10 +546,43 @@ mod app {
532546 // TODO: Copy byte to software buffer and turn UART RX
533547 // interrupt off if buffer is full
534548 }
549+ Some ( Message :: SpeakerDisable ) => {
550+ defmt:: trace!( "Speaker disabled" ) ;
551+ ctx. shared . speaker . lock ( |speaker| speaker. disable ( ) ) ;
552+ register_state. speaker . set_duration ( 0 ) ;
553+ }
535554 None => {
536555 // No messages
537556 }
538557 }
558+
559+ // The speaker PWM needs to be updated (register was updated)
560+ if register_state. speaker . needs_update ( ) {
561+ defmt:: info!( "speaker PWM update" ) ;
562+ register_state. speaker . set_needs_update ( false ) ;
563+
564+ let register = & mut register_state. speaker ;
565+ let task_handle = ctx. local . speaker_task_handle . take ( ) ;
566+
567+ let keep_playing = ctx. shared . speaker . lock ( |speaker| {
568+ speaker. update ( register, || {
569+ if let Some ( h) = task_handle {
570+ // if there's a running "stop" task, reschedule it
571+ defmt:: trace!( "Speaker task cancelled!" ) ;
572+ h. cancel ( ) . unwrap_or_default ( ) ;
573+ }
574+ } )
575+ } ) ;
576+
577+ if keep_playing {
578+ // otherwise, spawn a new one
579+ defmt:: trace!( "Speaker task spawned!" ) ;
580+ ctx. local . speaker_task_handle . replace (
581+ speaker_pwm_stop:: spawn_after ( ( register. duration ( ) as u64 ) . millis ( ) )
582+ . unwrap ( ) ,
583+ ) ;
584+ }
585+ }
539586 // TODO: Read ADC for 3.3V and 5.0V rails and check good
540587 }
541588 }
@@ -602,6 +649,28 @@ mod app {
602649 }
603650 }
604651
652+ /// Initialization melody, played directly by the BMC
653+ #[ task( shared = [ speaker, msg_q_in] ) ]
654+ fn speaker_init_tune ( mut ctx : speaker_init_tune:: Context ) {
655+ defmt:: trace!( "Playing startup tone" ) ;
656+
657+ ctx. shared . speaker . lock ( |speaker| {
658+ // F4
659+ speaker. set_note ( 100 , 137 , 10 ) ;
660+ } ) ;
661+
662+ speaker_pwm_stop:: spawn_after ( 100 . millis ( ) ) . unwrap ( ) ;
663+ }
664+ /// Task which stops the speaker from playing
665+ #[ task( shared = [ msg_q_in] ) ]
666+ fn speaker_pwm_stop ( mut ctx : speaker_pwm_stop:: Context ) {
667+ defmt:: trace!( "Speaker stopped" ) ;
668+ let _ = ctx
669+ . shared
670+ . msg_q_in
671+ . lock ( |q| q. enqueue ( Message :: SpeakerDisable ) ) ;
672+ }
673+
605674 /// This is the SPI1 task.
606675 ///
607676 /// It fires whenever there is new data received on SPI1. We should flag to the host
@@ -752,6 +821,9 @@ where
752821 // We were not sent what we were sent last time, so forget the previous request.
753822 register_state. last_req = None ;
754823
824+ // temporary buffer to hold serialized data while the response is generated
825+ let mut data = [ 0u8 ; 1 ] ;
826+
755827 // What do they want?
756828 let rsp = match ( req. request_type , Command :: try_from ( req. register ) ) {
757829 ( proto:: RequestType :: Read , Ok ( Command :: ProtocolVersion ) )
@@ -803,6 +875,48 @@ where
803875 proto:: Response :: new_without_data ( proto:: ResponseResult :: BadLength )
804876 }
805877 }
878+ ( proto:: RequestType :: Read , Ok ( Command :: SpeakerDuration ) ) => {
879+ defmt:: debug!( "Reading speaker duration" ) ;
880+ data[ 0 ] = ( register_state. speaker . duration ( ) / 10 ) as u8 ;
881+ proto:: Response :: new_ok_with_data ( & data)
882+ }
883+ ( proto:: RequestType :: ShortWrite , Ok ( Command :: SpeakerDuration ) ) => {
884+ defmt:: debug!( "Writing speaker duration ({})" , req. length_or_data) ;
885+ register_state
886+ . speaker
887+ . set_duration ( req. length_or_data as u16 * 10 ) ;
888+ proto:: Response :: new_without_data ( proto:: ResponseResult :: Ok )
889+ }
890+ ( proto:: RequestType :: Read , Ok ( Command :: SpeakerPeriodHigh ) ) => {
891+ defmt:: debug!( "Reading speaker period (high)" ) ;
892+ data[ 0 ] = register_state. speaker . period_high ( ) ;
893+ proto:: Response :: new_ok_with_data ( & data)
894+ }
895+ ( proto:: RequestType :: ShortWrite , Ok ( Command :: SpeakerPeriodHigh ) ) => {
896+ defmt:: debug!( "Writing speaker period (high = {})" , req. length_or_data) ;
897+ register_state. speaker . set_period_low ( req. length_or_data ) ;
898+ proto:: Response :: new_without_data ( proto:: ResponseResult :: Ok )
899+ }
900+ ( proto:: RequestType :: Read , Ok ( Command :: SpeakerPeriodLow ) ) => {
901+ defmt:: debug!( "Reading speaker period (low)" ) ;
902+ data[ 0 ] = register_state. speaker . period_low ( ) ;
903+ proto:: Response :: new_ok_with_data ( & data)
904+ }
905+ ( proto:: RequestType :: ShortWrite , Ok ( Command :: SpeakerPeriodLow ) ) => {
906+ defmt:: debug!( "Writing speaker period (low = {})" , req. length_or_data) ;
907+ register_state. speaker . set_period_high ( req. length_or_data ) ;
908+ proto:: Response :: new_without_data ( proto:: ResponseResult :: Ok )
909+ }
910+ ( proto:: RequestType :: Read , Ok ( Command :: SpeakerDutyCycle ) ) => {
911+ defmt:: debug!( "Reading speaker duty cycle" ) ;
912+ data[ 0 ] = register_state. speaker . duty_cycle ( ) ;
913+ proto:: Response :: new_ok_with_data ( & data)
914+ }
915+ ( proto:: RequestType :: ShortWrite , Ok ( Command :: SpeakerDutyCycle ) ) => {
916+ defmt:: debug!( "Writing speaker duty cycle ({})" , req. length_or_data) ;
917+ register_state. speaker . set_duty_cycle ( req. length_or_data ) ;
918+ proto:: Response :: new_without_data ( proto:: ResponseResult :: Ok )
919+ }
806920 _ => {
807921 // Sorry, that register / request type is not supported
808922 proto:: Response :: new_without_data ( proto:: ResponseResult :: BadRegister )
0 commit comments