This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

NRF52 help configuring TIMER + DMA + PWM

I need help configuring TIMER + DMA + PWM ( nrf52 ).

I would like to set the value of PWM with an interval of 100 ms and PWM configured to frequency of 20-40kHz.

Optinal use of EasyDMA for the PWM module is redundant over the resources used and the frequency of the PWM is not fixed.

Typically when using DMA, data at a specified interval is copied to pre-configured PWM module.

We are using the NRF52832 and the documentation describes EasyDMA in the context of using peripherals but, I did not find an implementation description for the mode copy RAM to peripheral register with timer set interval.

Does anyone know how to implement such a system?

  • Hi

    Do you have any experience with the TIMER and PPI modules?

    If you configure the PWM in the NextStep mode it will only read a new value from the buffer once you activate the NEXTSTEP task:

    NRF_PWM0->TASKS_NEXTSTEP = 1;
    

    By using the PPI controller you can connect one of the TIMER COMPARE events to the NEXTSTEP task, to have the timer control the PWM update frequency.

    Before you do this you can set up a RAM buffer for the PWM to read from, so that you can run the PWM in the background without having to continuously feed it new data.

    Best regards
    Torbjørn

  • Yes, we are inexperienced with using PPI for NRF52.

    The problem seems to be deeper, PWM configured to 50kHz produces such a strange signal that you could doubt the controller's settings the PWM does not work above 25kHz, any duty not equal to 50% at 25kHz gives the wrong and strange pwm signal.

    #define LLOW  0 
    #define LHIGH 1 
    #define TIMER_RELOAD     		8000   
    #define TIMER_RELOAD_CC_NUM  	5
    #define PWM0_PPI_CH_A       	0
    
    void PWM0_IRQHandler ( void )   // not init now 
    {
    if ( NRF_PWM0->EVENTS_PWMPERIODEND )
    {
        NRF_PWM0->EVENTS_PWMPERIODEND = 0;
    	nrf_gpio_pin_write	( 17, LLOW );
    }
    }
    
    //!   __O  uint32_t  TASKS_STOP;                        < Stops PWM pulse generation on all channels at the end of current
    //!                                                       PWM period, and stops sequence playback                               
    //!   __O  uint32_t  TASKS_SEQSTART[2];                 < Description collection[0]: Loads the first PWM value on all
    //!                                                       enabled channels from sequence 0, and starts playing that sequence
    //!                                                        at the rate defined in SEQ[0]REFRESH and/or DECODER.MODE. Causes
    //!                                                        PWM generation to start it was not running.                          
    //!   __O  uint32_t  TASKS_NEXTSTEP;                    < Steps by one value in the current sequence on all enabled channels
    //!                                                       if DECODER.MODE=NextStep. Does not cause PWM generation to start
    //!                                                        it was not running.                                                  
    //!   __IO uint32_t  EVENTS_STOPPED;                    < Response to STOP task, emitted when PWM pulses are no longer
    //!                                                       generated                                                             
    //!   __IO uint32_t  EVENTS_SEQSTARTED[2];              < Description collection[0]: First PWM period started on sequence
    //!                                                       0                                                                     
    //!   __IO uint32_t  EVENTS_SEQEND[2];                  < Description collection[0]: Emitted at end of every sequence
    //!                                                       0, when last value from RAM has been applied to wave counter          
    //!   __IO uint32_t  EVENTS_PWMPERIODEND;               < Emitted at the end of each PWM period                                 
    //!   __IO uint32_t  EVENTS_LOOPSDONE;                  < Concatenated sequences have been played the amount of times
    //!                                                       defined in LOOP.CNT                                                   
    //!   __IO uint32_t  SHORTS;                            < Shortcut register                                                     
    //!   __IO uint32_t  INTEN;                             < Enable or disable interrupt                                           
    //!   __IO uint32_t  INTENSET;                          < Enable interrupt                                                      
    //!   __IO uint32_t  INTENCLR;                          < Disable interrupt                                                     
    //!   __IO uint32_t  ENABLE;                            < PWM module enable register                                            
    //!   __IO uint32_t  MODE;                              < Selects operating mode of the wave counter                            
    //!   __IO uint32_t  COUNTERTOP;                        < Value up to which the pulse generator counter counts                  
    //!   __IO uint32_t  PRESCALER;                         < Configuration for PWM_CLK                                             
    //!   __IO uint32_t  DECODER;                           < Configuration of the decoder                                          
    //!   __IO uint32_t  LOOP;                              < Amount of playback of a loop                                          
    //!	PWM_SEQ_Type;
    //! __IO uint32_t  SEQx.PTR;                               /*!< Description cluster[0]: Beginning address in Data RAM of this
    //!                                                       sequence                                                              */
    //!__IO uint32_t  SEQx.CNT;                               /*!< Description cluster[0]: Amount of values (duty cycles) in this
    //!                                                       sequence                                                              */
    //!__IO uint32_t  SEQx.REFRESH;                           /*!< Description cluster[0]: Amount of additional PWM periods between
    //!                                                       samples loaded into compare register                                  */
    //!__IO uint32_t  SEQx.ENDDELAY;                          /*!< Description cluster[0]: Time added after the sequence                 */
    
    void config_pwm_hardware (  void )
    {
    
    	nrf_gpio_cfg_output ( PIN_TST_PWM ); 
    	nrf_gpio_pin_write	( PIN_TST_PWM, LLOW );
    
        nrf_gpio_cfg_output ( 17 ); 
    	nrf_gpio_pin_write	( 17, LHIGH );
    
    //  GPIO clock ?
    // 	
    
    // init timer3,  clock source  next step
    // 24-bit, base frequency 16 MHz, auto clear on COMPARE5 match (CC5 = TIMER_RELOAD)
     NRF_TIMER3->BITMODE                 = TIMER_BITMODE_BITMODE_24Bit << TIMER_BITMODE_BITMODE_Pos;
     NRF_TIMER3->PRESCALER               = 0;
     NRF_TIMER3->SHORTS                  = TIMER_SHORTS_COMPARE0_CLEAR_Msk << TIMER_RELOAD_CC_NUM;
     NRF_TIMER3->MODE                    = TIMER_MODE_MODE_Timer << TIMER_MODE_MODE_Pos;
     NRF_TIMER3->CC[TIMER_RELOAD_CC_NUM] = TIMER_RELOAD;    
    
     NRF_PWM0->PSEL.OUT[0] =   (PIN_TST_PWM << PWM_PSEL_OUT_PIN_Pos) | (PWM_PSEL_OUT_CONNECT_Connected << PWM_PSEL_OUT_CONNECT_Pos);
     NRF_PWM0->PSEL.OUT[1] =   (0 << PWM_PSEL_OUT_PIN_Pos) | (PWM_PSEL_OUT_CONNECT_Disconnected << PWM_PSEL_OUT_CONNECT_Pos);
     NRF_PWM0->PSEL.OUT[2] =   (1 << PWM_PSEL_OUT_PIN_Pos) | (PWM_PSEL_OUT_CONNECT_Disconnected << PWM_PSEL_OUT_CONNECT_Pos);
     NRF_PWM0->PSEL.OUT[3] =   (2 << PWM_PSEL_OUT_PIN_Pos) | (PWM_PSEL_OUT_CONNECT_Disconnected << PWM_PSEL_OUT_CONNECT_Pos);
        
     NRF_PWM0->ENABLE 		= (PWM_ENABLE_ENABLE_Enabled << PWM_ENABLE_ENABLE_Pos);
     NRF_PWM0->MODE 		= (PWM_MODE_UPDOWN_Up 			<< PWM_MODE_UPDOWN_Pos);
     NRF_PWM0->PRESCALER 	= (PWM_PRESCALER_PRESCALER_DIV_1 << PWM_PRESCALER_PRESCALER_Pos);
     NRF_PWM0->COUNTERTOP 	= ( cfg_IR_Transmit.Carrier_period_pwm << PWM_COUNTERTOP_COUNTERTOP_Pos);
     NRF_PWM0->DECODER 		= (PWM_DECODER_LOAD_Common << PWM_DECODER_LOAD_Pos)|(PWM_DECODER_MODE_NextStep << PWM_DECODER_MODE_Pos);
     NRF_PWM0->SEQ[0].PTR 	= ((uint32_t)(ARRAY_PWM_DATA) << PWM_SEQ_PTR_PTR_Pos);
     NRF_PWM0->SEQ[0].CNT 	= 0; // ( length_data << PWM_SEQ_CNT_CNT_Pos); 
    
     NRF_PWM0->SEQ[0].REFRESH 	= 0; 
     NRF_PWM0->SEQ[0].ENDDELAY  = 0; 
     NRF_PWM0->SEQ[1].PTR 		= 0;  
     NRF_PWM0->SEQ[1].CNT 		= 0;  
     NRF_PWM0->SEQ[1].REFRESH 	= 0;  
     NRF_PWM0->SEQ[1].ENDDELAY 	= 0;  
     NRF_PWM0->SHORTS 			= 0;  
     NRF_PWM0->LOOP 			= 0;  
     NRF_PWM0->TASKS_NEXTSTEP   = 1;  
     NRF_PWM0->EVENTS_PWMPERIODEND = 1;
    // init PPI 
     NRF_PPI->CH[PWM0_PPI_CH_A].EEP = (uint32_t)&NRF_TIMER3->EVENTS_COMPARE[TIMER_RELOAD_CC_NUM];
     NRF_PPI->CH[PWM0_PPI_CH_A].TEP = (uint32_t)&NRF_PWM0->TASKS_NEXTSTEP;
     NRF_PPI->CHENSET               = (1 << PWM0_PPI_CH_A);
    
    }	
    
    // infocenter.nordicsemi.com/.../nRF52832_PS_v1.1.pdf page 504 
    	
    // 47.3 Limitations
    // The previous compare value will be repeated if the PWM period is selected to be shorter than the time it
    // takes for the EasyDMA to fetch from RAM and update the internal compare registers.
    // This is to ensure a glitch-free operation even if very short PWM periods are chosen.
    
    // 47.4 Pin configuration
    // The OUT[n] (n=0..3) signals associated to each channel of the PWM module are mapped to
    // physical pins according to the configuration specified in the respective PSEL.OUT[n] registers. If a
    // PSEL.OUT[n].CONNECT is set to Disconnected, the associated PWM module signal will not be connected
    // to any physical pins.
    // The PSEL.OUT[n] registers and their configurations are only used as long as the PWM module is enabled
    // and PWM generation is active (wave counter started), and retained only as long as the device is in System
    // ON mode, see POWER chapter for more information about power modes.
    // To ensure correct behaviour in the PWM module, the pins used by the PWM module must be configured in
    // the GPIO peripheral as described in Table 117: Recommended GPIO configuration before starting PWM
    // generation on page 504 before enabling the PWM module. The pins' idle state is defined by the OUT
    // registers in the GPIO module. This is to ensure that the pins used by the PWM module are driven correctly,
    // if PWM generation is stopped through a STOP task, the PWM module itself is temporarily disabled, or the
    // device temporarily enters System OFF. This configuration must be retained in the GPIO for the selected IOs
    // as long as the PWM module is supposed to be connected to an external PWM circuit.
    // Only one peripheral can be assigned to drive a particular GPIO pin at a time. Failing to do so may result in
    // unpredictable behaviour.
    
    
    void restart_DMA_generate_test ( uint16_t length_data )
    {
      config_pwm_hardware ();
      NRF_PWM0->SEQ[0].CNT 					= ( length_data << PWM_SEQ_CNT_CNT_Pos); 
      NRF_PWM0->TASKS_SEQSTART[0] 	= 1;
      NRF_TIMER3->TASKS_START 		= 1;	
    }`
    

    the clock pwm frequency is probably incorrectly set? 1kHz frequencies work well

  • Hi

    What is the cfg_IR_Transmit.Carrier_period_pwm variable set to, and what values do you put into the PWM buffer?

    It might be easier if you can just zip your project and attach it as a file. Then I can more easily compile and test your code.

    Best regards

  • hello, the code in the example works on the PWM frequency 25KHz but nrf5d dk can not start correctly at 50 kHz

    pwm is integrated into the working project. I'll make an example later

    examples from the library are designed for a small frequency of 1 kHz

    if you have an example at 50 kHz or hex for nrf52 DK with implemented PWM. happy to check on nrf52 DK

    In any way it is impossible to generate a correct signal. what are the peaks is the way out but it's wrong.

    devzone.nordicsemi.com/.../

    this example generates only 222 kHz perhaps this will tell where the problem. is compiled on nrf52 dk keil, my project sdk_config.h

  • Hi

    What have you set the PRESCALER and COUNTERTOP values to, when switching to 50kHz?

    Best regards

Related