52832 PPI for timer and PWM

hi, 

I want to use PPI function for timer trigger to PWM, but I noticed PWM doesn't have TASK_START register, please see the pictures.

how can I start PWM by timer?

thanks 

  • the wrong wave picture, only has PWM0 wave, and PWM1 disappear.

     the right wave picture, PWM0 wave and PWM1 are synchronized.

  • Not sure I understand your pictures. Did you post two of each picture?

    Have you debugged the application to see what is triggering the PWM interrupt handler, and if it disables the PWM output somehow?

    Could you upload your full application? That would make it easier to reproduce and debug your issue.

  • #include <stdbool.h>
    #include <stdint.h>
    #include "nrf.h"
    #include "nrf_drv_timer.h"
    #include "bsp.h"
    #include "app_error.h"
    #include "app_uart.h"
    #include "nrf_drv_pwm.h"
    #include "nrf_drv_ppi.h"
    #include "data.h"
    
    
    
    #if defined (UART_PRESENT)
    #include "nrf_uart.h"
    #endif
    #if defined (UARTE_PRESENT)
    #include "nrf_uarte.h"
    #endif
    
    //#define ENABLE_LOOPBACK_TEST  /**< if defined, then this example will be a loopback test, which means that TX should be connected to RX to get data loopback. */
    
    #define MAX_TEST_DATA_BYTES     (15U)                /**< max number of test bytes to be used for tx and rx. */
    #define UART_TX_BUF_SIZE 256                         /**< UART TX buffer size. */
    #define UART_RX_BUF_SIZE 256                         /**< UART RX buffer size. */
    
    void uart_error_handle(app_uart_evt_t * p_event)
    {
        if (p_event->evt_type == APP_UART_COMMUNICATION_ERROR)
        {
            APP_ERROR_HANDLER(p_event->data.error_communication);
        }
        else if (p_event->evt_type == APP_UART_FIFO_ERROR)
        {
            APP_ERROR_HANDLER(p_event->data.error_code);
        }
    }
    
    
    #ifdef ENABLE_LOOPBACK_TEST
    /* Use flow control in loopback test. */
    #define UART_HWFC APP_UART_FLOW_CONTROL_ENABLED
    
    /** @brief Function for setting the @ref ERROR_PIN high, and then enter an infinite loop.
     */
    static void show_error(void)
    {
    
        bsp_board_leds_on();
        while (true)
        {
            // Do nothing.
        }
    }
    
    
    /** @brief Function for testing UART loop back.
     *  @details Transmitts one character at a time to check if the data received from the loopback is same as the transmitted data.
     *  @note  @ref TX_PIN_NUMBER must be connected to @ref RX_PIN_NUMBER)
     */
    static void uart_loopback_test()
    {
        uint8_t * tx_data = (uint8_t *)("\r\nLOOPBACK_TEST\r\n");
        uint8_t   rx_data;
    
        // Start sending one byte and see if you get the same
        for (uint32_t i = 0; i < MAX_TEST_DATA_BYTES; i++)
        {
            uint32_t err_code;
            while (app_uart_put(tx_data[i]) != NRF_SUCCESS);
    
            nrf_delay_ms(10);
            err_code = app_uart_get(&rx_data);
    
            if ((rx_data != tx_data[i]) || (err_code != NRF_SUCCESS))
            {
                show_error();
            }
        }
        return;
    }
    #else
    /* When UART is used for communication with the host do not use flow control.*/
    #define UART_HWFC APP_UART_FLOW_CONTROL_DISABLED
    #endif
    
    #define PPI_EXAMPLE_TIMERS_PHASE_SHIFT_DELAY    (10)    // 1s = 10 * 100ms (Timer 0 interrupt)
    
    static volatile uint32_t m_counter;
    static const nrf_drv_timer_t m_timer0 = NRF_DRV_TIMER_INSTANCE(0);
    static const nrf_drv_timer_t m_timer1 = NRF_DRV_TIMER_INSTANCE(1);
    static const nrf_drv_timer_t m_timer2 = NRF_DRV_TIMER_INSTANCE(2);
    extern nrf_pwm_values_wave_form_t const seq_values_Ring[];
    extern nrf_pwm_values_wave_form_t const seq_values_Tip[];
    extern	int  Tip_Number;
    extern	int  Ring_Number;
    extern	int number_Tip[];
    extern	int number_Ring[];
    bool TipHandlerDone = false;
    bool RingHandlerDone = false;
    
    nrf_pwm_values_wave_form_t /*const*/ seq_values_Tip0[1];
    nrf_pwm_values_wave_form_t /*const*/ seq_values_Tip1[1];
    nrf_pwm_values_wave_form_t /*const*/ seq_values_Ring0[1];
    nrf_pwm_values_wave_form_t /*const*/ seq_values_Ring1[1];
    
    static nrf_drv_pwm_t m_pwm0 = NRF_DRV_PWM_INSTANCE(0);
    static nrf_drv_pwm_t m_pwm1 = NRF_DRV_PWM_INSTANCE(1);
    static nrf_drv_pwm_t m_pwm2 = NRF_DRV_PWM_INSTANCE(2);
    
    #define USED_PWM(idx) (1UL << idx)
    static uint8_t m_used = 0;
    
    static void TipRing0(void);
    
    uint32_t *start_pwm0_task;
    uint32_t *start_pwm1_task;
    
    int t0=1,t1=1,r0=1,r1=1;
    int Count_Tip0=0,Count_Tip1=1;	
    int Count_Ring0=0,Count_Ring1=1;	
    
    static nrf_ppi_channel_t m_ppi_channel1;
    
    
    static void TipHandler(nrfx_pwm_evt_type_t event_type)
    {
    	if (event_type == NRFX_PWM_EVT_END_SEQ0)
    	{		
    			Count_Tip0 = Count_Tip0+2;
    			seq_values_Tip0[0] = seq_values_Tip[Count_Tip0];
    			NRF_PWM0->SEQ[0].REFRESH = number_Tip[Count_Tip0]-1;			
    			t0++;
    			printf("\r\n t0=%d, Count_Tip0=%d, Tip0[0]=%d\r\n",t0,Count_Tip0,seq_values_Tip0[0].counter_top);	
    	}
    	if (event_type == NRFX_PWM_EVT_END_SEQ1)
    	{		
    			Count_Tip1 = Count_Tip1+2;
    			seq_values_Tip1[0] = seq_values_Tip[Count_Tip1];
    			NRF_PWM0->SEQ[1].REFRESH = number_Tip[Count_Tip1]-1;			
    			t1++;
    			printf("\r\n t1=%d, Count_Tip1=%d, Tip1[0]=%d\r\n",t1,Count_Tip1,seq_values_Tip1[0].counter_top);				
    			if(t1>(Tip_Number/2))
    				{
    					NRF_PWM0->TASKS_STOP = 1;	
    					t0=1;t1=1;Count_Tip0=0,Count_Tip1=1;
    					TipHandlerDone = true;
    					printf("\r\n tip finshed\r\n");
    					return;
    					
    				}
    	}
    	
    }
    
    static void RingHandler(nrfx_pwm_evt_type_t event_type) 
    {	
    		if (event_type == NRFX_PWM_EVT_END_SEQ0)
    		{	
    			Count_Ring0 = Count_Ring0+2;
    			seq_values_Ring0[0] = seq_values_Ring[Count_Ring0]; 
    			NRF_PWM1->SEQ[0].REFRESH = number_Ring[Count_Ring0]-1;							
    			r0++;	
    			printf("\r\n r0=%d, Count_Ring0=%d, Ring0[0]=%d\r\n",r0,Count_Ring0,seq_values_Ring0[0].counter_top);				
    		}
    	
    		if (event_type == NRFX_PWM_EVT_END_SEQ1)
    		{	
    			Count_Ring1 = Count_Ring1+2;
    			seq_values_Ring1[0] = seq_values_Ring[Count_Ring1]; 
    			NRF_PWM1->SEQ[1].REFRESH = number_Ring[Count_Ring1]-1;						
    			r1++;
    			printf("\r\n r1=%d, Count_Ring1=%d, Ring1[0]=%d\r\n",r1,Count_Ring1,seq_values_Ring1[0].counter_top);			
    			if(r1>(Ring_Number/2))
    			{
    				NRF_PWM1->TASKS_STOP = 1;
    				r0=1;r1=1;Count_Ring0=0,Count_Ring1=1;
    				RingHandlerDone = true;
    				printf("\r\n ring finshed\r\n");
    				return;
    			}
    		}		
    			
    }
    /**
     * @brief Handler for timer events.
     */
    void timer_led_event_handler(nrf_timer_event_t event_type, void* p_context)
    {
    
    	
    }
    
    static void PWMinit(void)
    {
    
    	seq_values_Tip0[0] = seq_values_Tip[Count_Tip0];
    	seq_values_Tip1[0] = seq_values_Tip[Count_Tip1];
    	
    	seq_values_Ring0[0] = seq_values_Ring[Count_Ring0];  
    	seq_values_Ring1[0] = seq_values_Ring[Count_Ring1];  
    	
    	nrf_drv_pwm_config_t const configTip0 =
    	{
    			.output_pins =
    			{
    					//BSP_LED_1 | NRF_DRV_PWM_PIN_INVERTED, // channel 0  free is high level
    					BSP_LED_1,															// 14-PIN-TIP-PWM0 channel 0  free is low level
    					NRF_DRV_PWM_PIN_NOT_USED,             // channel 1
    					NRF_DRV_PWM_PIN_NOT_USED,             // channel 2
    					NRF_DRV_PWM_PIN_NOT_USED,             // channel 3
    			},
    			.irq_priority = APP_IRQ_PRIORITY_LOWEST,
    			.base_clock   = NRF_PWM_CLK_16MHz,
    			.count_mode   = NRF_PWM_MODE_UP,
    			.top_value    = 0,												
    			.load_mode    = NRF_PWM_LOAD_WAVE_FORM,
    			.step_mode    = NRF_PWM_STEP_AUTO
    	};
    	nrf_drv_pwm_config_t const configRing0 =
    	{
    			.output_pins =
    			{
    					//BSP_LED_0 | NRF_DRV_PWM_PIN_INVERTED, // channel 0  free is high level
    					BSP_LED_0,															// 13-PIN-RING-PWM1 channel 0  free is low level
    					NRF_DRV_PWM_PIN_NOT_USED,             // channel 1
    					NRF_DRV_PWM_PIN_NOT_USED,             // channel 2
    					NRF_DRV_PWM_PIN_NOT_USED,             // channel 3
    			},
    			.irq_priority = APP_IRQ_PRIORITY_LOWEST,
    			.base_clock   = NRF_PWM_CLK_16MHz,
    			.count_mode   = NRF_PWM_MODE_UP,
    			.top_value    = 0,												
    			.load_mode    = NRF_PWM_LOAD_WAVE_FORM,
    			.step_mode    = NRF_PWM_STEP_AUTO
    	};
    	
    //    APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0, &configTip0, NULL));
    //    APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm1, &configRing0, NULL));
    		
    	APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0, &configTip0, TipHandler));
    	APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm1, &configRing0, RingHandler));
    
    
    	nrf_pwm_sequence_t const seq_Tip0 =
    	{
    			.values.p_wave_form = seq_values_Tip0,
    			.length          = NRF_PWM_VALUES_LENGTH(seq_values_Tip0),
    			.repeats         = number_Tip[Count_Tip0]-1,
    			.end_delay       = 0
    	};
    	
    	 nrf_pwm_sequence_t const seq_Tip1 =
    	{
    			.values.p_wave_form = seq_values_Tip1,
    			.length          = NRF_PWM_VALUES_LENGTH(seq_values_Tip1),
    			.repeats         = number_Tip[Count_Tip1]-1,
    			.end_delay       = 0
    	};
    		
    	nrf_pwm_sequence_t const seq_Ring0 =
    	{
    			.values.p_wave_form = seq_values_Ring0,
    			.length          = NRF_PWM_VALUES_LENGTH(seq_values_Ring0),
    			.repeats         = number_Ring[Count_Ring0]-1,
    			.end_delay       = 0
    	};
    	
    	nrf_pwm_sequence_t const seq_Ring1 =
    	{
    			.values.p_wave_form = seq_values_Ring1,
    			.length          = NRF_PWM_VALUES_LENGTH(seq_values_Ring1),
    			.repeats         = number_Ring[Count_Ring1]-1,
    			.end_delay       = 0
    	};
    
    
    //    (void)nrf_drv_pwm_simple_playback(&m_pwm0, &seq_Tip0, 1, NRFX_PWM_FLAG_LOOP);
    //    (void)nrf_drv_pwm_simple_playback(&m_pwm1, &seq_Ring0, 1, NRFX_PWM_FLAG_LOOP);
    	
    	(void)nrf_drv_pwm_complex_playback(&m_pwm0, &seq_Tip0, &seq_Tip1, (Tip_Number/2+1), NRFX_PWM_FLAG_STOP|NRF_DRV_PWM_FLAG_SIGNAL_END_SEQ0 |NRF_DRV_PWM_FLAG_SIGNAL_END_SEQ1);
    	(void)nrf_drv_pwm_complex_playback(&m_pwm1, &seq_Ring0, &seq_Ring1, (Tip_Number/2+1), NRFX_PWM_FLAG_STOP|NRF_DRV_PWM_FLAG_SIGNAL_END_SEQ0 |NRF_DRV_PWM_FLAG_SIGNAL_END_SEQ1);
      
    }
    
    
    /** @brief Function for initializing the PPI peripheral.
    */
    static void ppi_init(void)
    {
        uint32_t err_code = NRF_SUCCESS;
    
        err_code = nrf_drv_ppi_init();
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel1);
        APP_ERROR_CHECK(err_code);
        err_code = nrf_drv_ppi_channel_assign(m_ppi_channel1,
                                              nrf_drv_timer_event_address_get(&m_timer0,
                                                                              NRF_TIMER_EVENT_COMPARE0),
                                               nrf_drv_pwm_task_address_get(&m_pwm0,
                                                                             NRF_PWM_TASK_SEQSTART0));
        APP_ERROR_CHECK(err_code);
    	
    		err_code = nrf_drv_ppi_channel_fork_assign(m_ppi_channel1,                                         
                                              nrf_drv_pwm_task_address_get(&m_pwm1,
                                                                             NRF_PWM_TASK_SEQSTART0));
        APP_ERROR_CHECK(err_code);
    
    
        err_code = nrf_drv_ppi_channel_enable(m_ppi_channel1);
        APP_ERROR_CHECK(err_code);
    			
    }
    
    static void timer0_init(void)
    {
    	  uint32_t time_us = 15000; //Time(in miliseconds) between consecutive compare events.
        uint32_t time_ticks;
        uint32_t err_code = NRF_SUCCESS;
    	
    		NRF_CLOCK->TASKS_HFCLKSTART = 1; // For more accurate timing. //start high speed clock
    	
        nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
        err_code = nrf_drv_timer_init(&m_timer0, &timer_cfg, timer_led_event_handler);
        APP_ERROR_CHECK(err_code);
    
        time_ticks = nrf_drv_timer_us_to_ticks(&m_timer0, time_us);  //time_ticks = (times*16000)/512 = 31250
    
        nrf_drv_timer_extended_compare(
             &m_timer0, NRF_TIMER_CC_CHANNEL0, time_ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true);
    
        nrf_drv_timer_enable(&m_timer0);
    
    }
    
    
    int main(void)
    { 
    		uint32_t err_code;
    	
    	  NRF_CLOCK->EVENTS_HFCLKSTARTED=0;
    	  NRF_CLOCK->TASKS_HFCLKSTART=1;
    	  while(NRF_CLOCK->EVENTS_HFCLKSTARTED==0)
    		{
    		}
    		
        const app_uart_comm_params_t comm_params =
          {
              RX_PIN_NUMBER,
              TX_PIN_NUMBER,
              RTS_PIN_NUMBER,
              CTS_PIN_NUMBER,
              UART_HWFC,
              false,
    #if defined (UART_PRESENT)
              NRF_UART_BAUDRATE_115200
    #else
              NRF_UARTE_BAUDRATE_115200
    #endif
          };
    
        APP_UART_FIFO_INIT(&comm_params,
                             UART_RX_BUF_SIZE,
                             UART_TX_BUF_SIZE,
                             uart_error_handle,
                             APP_IRQ_PRIORITY_LOWEST,
                             err_code);
    
        APP_ERROR_CHECK(err_code);
    
    #ifndef ENABLE_LOOPBACK_TEST
        printf("\r\nMPP example started.\r\n");
    #endif	   
    	
    			
    	ppi_init();
    	timer0_init(); 			
    	PWMinit();
    				
    	while (1)
    	{	
    
    
    		 // Wait for an event.
    			__WFE();
    
    			// Clear the event register.
    			__SEV();
    			__WFE();
    	}
    }
    
    /** @} */
    
    please see my all code, I found if I use PPI for trigger PWM, the PWM can't come in interrupt. I print some information in two PWMs interrupt(RingHandler and TipHandler) by UART, but UART only print PWM0 interrupt information, didn't print PWM1 information, and the wave is wrong, only PWM0 has wave.

    But if I didn't use PPI, only use timer interrupt to trigger PWM, everything is ok. please see two pictures 

    this is using timer picture 

  • I tried to run your code, but there was too many missing defines for me to test it properly. Could you upload the full project, or at least the data.h file where the PWM sequences are defined?

    I checked that the PPI channel endpoints are set correctly, so there seems to be something in the driver or interrupt that cause the second PWM channel to stop and not being able to trigger through PPI.

Related