@@ -14,8 +14,8 @@ pub use stm32_metapac::timer::vals::{FilterValue, Mms as MasterMode, Sms as Slav
1414
1515use super :: * ;
1616use crate :: pac:: timer:: vals;
17- use crate :: rcc;
1817use crate :: time:: Hertz ;
18+ use crate :: { dma, rcc} ;
1919
2020/// Input capture mode.
2121#[ derive( Clone , Copy ) ]
@@ -656,6 +656,219 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
656656 }
657657 }
658658
659+ /// Generate a sequence of PWM waveform
660+ ///
661+ /// Note:
662+ /// you will need to provide corresponding TIMx_UP DMA channel to use this method.
663+ pub async fn waveform_up ( & mut self , dma : Peri < ' _ , impl super :: UpDma < T > > , channel : Channel , duty : & [ u16 ] ) {
664+ #[ allow( clippy:: let_unit_value) ] // eg. stm32f334
665+ let req = dma. request ( ) ;
666+
667+ let original_update_dma_state = self . get_update_dma_state ( ) ;
668+
669+ if !original_update_dma_state {
670+ self . enable_update_dma ( true ) ;
671+ }
672+
673+ self . waveform_helper ( dma, req, channel, duty) . await ;
674+
675+ // Since DMA is closed before timer update event trigger DMA is turn off,
676+ // this can almost always trigger a DMA FIFO error.
677+ //
678+ // optional TODO:
679+ // clean FEIF after disable UDE
680+ if !original_update_dma_state {
681+ self . enable_update_dma ( false ) ;
682+ }
683+ }
684+
685+ /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events.
686+ ///
687+ /// This method utilizes the timer's DMA burst transfer capability to update multiple CCRx registers
688+ /// in sequence on each update event (UEV). The data is written via the DMAR register using the
689+ /// DMA base address (DBA) and burst length (DBL) configured in the DCR register.
690+ ///
691+ /// The `duty` buffer must be structured as a flattened 2D array in row-major order, where each row
692+ /// represents a single update event and each column corresponds to a specific timer channel (starting
693+ /// from `starting_channel` up to and including `ending_channel`).
694+ ///
695+ /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like:
696+ ///
697+ /// ```rust,ignore
698+ /// let dma_buf: [u16; 16] = [
699+ /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1
700+ /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2
701+ /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3
702+ /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4
703+ /// ];
704+ /// ```
705+ ///
706+ /// Each group of `N` values (where `N` is number of channels) is transferred on one update event,
707+ /// updating the duty cycles of all selected channels simultaneously.
708+ ///
709+ /// Note:
710+ /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method.
711+ /// Also be aware that embassy timers use one of timers internally. It is possible to
712+ /// switch this timer by using `time-driver-timX` feature.
713+ ///
714+ pub async fn waveform_up_multi_channel (
715+ & mut self ,
716+ dma : Peri < ' _ , impl super :: UpDma < T > > ,
717+ starting_channel : Channel ,
718+ ending_channel : Channel ,
719+ duty : & [ u16 ] ,
720+ ) {
721+ let cr1_addr = self . regs_gp16 ( ) . cr1 ( ) . as_ptr ( ) as u32 ;
722+ let start_ch_index = starting_channel. index ( ) ;
723+ let end_ch_index = ending_channel. index ( ) ;
724+
725+ assert ! ( start_ch_index <= end_ch_index) ;
726+
727+ let ccrx_addr = self . regs_gp16 ( ) . ccr ( start_ch_index) . as_ptr ( ) as u32 ;
728+ self . regs_gp16 ( )
729+ . dcr ( )
730+ . modify ( |w| w. set_dba ( ( ( ccrx_addr - cr1_addr) / 4 ) as u8 ) ) ;
731+ self . regs_gp16 ( )
732+ . dcr ( )
733+ . modify ( |w| w. set_dbl ( ( end_ch_index - start_ch_index) as u8 ) ) ;
734+
735+ #[ allow( clippy:: let_unit_value) ] // eg. stm32f334
736+ let req = dma. request ( ) ;
737+
738+ let original_update_dma_state = self . get_update_dma_state ( ) ;
739+ if !original_update_dma_state {
740+ self . enable_update_dma ( true ) ;
741+ }
742+
743+ unsafe {
744+ #[ cfg( not( any( bdma, gpdma) ) ) ]
745+ use crate :: dma:: { Burst , FifoThreshold } ;
746+ use crate :: dma:: { Transfer , TransferOptions } ;
747+
748+ let dma_transfer_option = TransferOptions {
749+ #[ cfg( not( any( bdma, gpdma) ) ) ]
750+ fifo_threshold : Some ( FifoThreshold :: Full ) ,
751+ #[ cfg( not( any( bdma, gpdma) ) ) ]
752+ mburst : Burst :: Incr4 ,
753+ ..Default :: default ( )
754+ } ;
755+
756+ Transfer :: new_write (
757+ dma,
758+ req,
759+ duty,
760+ self . regs_gp16 ( ) . dmar ( ) . as_ptr ( ) as * mut u16 ,
761+ dma_transfer_option,
762+ )
763+ . await
764+ } ;
765+
766+ if !original_update_dma_state {
767+ self . enable_update_dma ( false ) ;
768+ }
769+ }
770+
771+ /// Generate a sequence of PWM waveform
772+ pub async fn waveform < C : TimerChannel > ( & mut self , dma : Peri < ' _ , impl super :: Dma < T , C > > , duty : & [ u16 ] ) {
773+ use crate :: pac:: timer:: vals:: Ccds ;
774+
775+ #[ allow( clippy:: let_unit_value) ] // eg. stm32f334
776+ let req = dma. request ( ) ;
777+
778+ let cc_channel = C :: CHANNEL ;
779+
780+ let original_cc_dma_on_update = self . get_cc_dma_selection ( ) == Ccds :: ON_UPDATE ;
781+ let original_cc_dma_enabled = self . get_cc_dma_enable_state ( cc_channel) ;
782+
783+ // redirect CC DMA request onto Update Event
784+ if !original_cc_dma_on_update {
785+ self . set_cc_dma_selection ( Ccds :: ON_UPDATE )
786+ }
787+
788+ if !original_cc_dma_enabled {
789+ self . set_cc_dma_enable_state ( cc_channel, true ) ;
790+ }
791+
792+ self . waveform_helper ( dma, req, cc_channel, duty) . await ;
793+
794+ // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off,
795+ // this can almost always trigger a DMA FIFO error.
796+ //
797+ // optional TODO:
798+ // clean FEIF after disable UDE
799+ if !original_cc_dma_enabled {
800+ self . set_cc_dma_enable_state ( cc_channel, false ) ;
801+ }
802+
803+ if !original_cc_dma_on_update {
804+ self . set_cc_dma_selection ( Ccds :: ON_COMPARE )
805+ }
806+ }
807+
808+ async fn waveform_helper (
809+ & mut self ,
810+ dma : Peri < ' _ , impl dma:: Channel > ,
811+ req : dma:: Request ,
812+ channel : Channel ,
813+ duty : & [ u16 ] ,
814+ ) {
815+ let original_duty_state = self . get_compare_value ( channel) ;
816+ let original_enable_state = self . get_channel_enable_state ( channel) ;
817+
818+ if !original_enable_state {
819+ self . enable_channel ( channel, true ) ;
820+ }
821+
822+ unsafe {
823+ #[ cfg( not( any( bdma, gpdma) ) ) ]
824+ use crate :: dma:: { Burst , FifoThreshold } ;
825+ use crate :: dma:: { Transfer , TransferOptions } ;
826+
827+ let dma_transfer_option = TransferOptions {
828+ #[ cfg( not( any( bdma, gpdma) ) ) ]
829+ fifo_threshold : Some ( FifoThreshold :: Full ) ,
830+ #[ cfg( not( any( bdma, gpdma) ) ) ]
831+ mburst : Burst :: Incr8 ,
832+ ..Default :: default ( )
833+ } ;
834+
835+ match self . bits ( ) {
836+ TimerBits :: Bits16 => {
837+ Transfer :: new_write (
838+ dma,
839+ req,
840+ duty,
841+ self . regs_1ch ( ) . ccr ( channel. index ( ) ) . as_ptr ( ) as * mut u16 ,
842+ dma_transfer_option,
843+ )
844+ . await
845+ }
846+ #[ cfg( not( any( stm32l0) ) ) ]
847+ TimerBits :: Bits32 => {
848+ #[ cfg( not( any( bdma, gpdma) ) ) ]
849+ panic ! ( "unsupported timer bits" ) ;
850+
851+ #[ cfg( any( bdma, gpdma) ) ]
852+ Transfer :: new_write (
853+ dma,
854+ req,
855+ duty,
856+ self . regs_1ch ( ) . ccr ( channel. index ( ) ) . as_ptr ( ) as * mut u32 ,
857+ dma_transfer_option,
858+ )
859+ . await
860+ }
861+ } ;
862+ } ;
863+
864+ // restore output compare state
865+ if !original_enable_state {
866+ self . enable_channel ( channel, false ) ;
867+ }
868+
869+ self . set_compare_value ( channel, original_duty_state) ;
870+ }
871+
659872 /// Get capture value for a channel.
660873 pub fn get_capture_value ( & self , channel : Channel ) -> u32 {
661874 self . get_compare_value ( channel)
0 commit comments