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

How to start pwm sequence after particular time delay of gpio event?

I am working on an application where I need to start pwm sequence after particular delay (accurate delay of multiple of 100us).

I am quite new to nordic environment.

Here is my code . Can please guide me what is wrong here??

#define    Period    20000 //20ms

const nrf_drv_timer_t TIMER_LED = NRF_DRV_TIMER_INSTANCE(0);

APP_PWM_INSTANCE(PWM1,1); 

static int gpio_flag =0;

void pwm_update(void)
{
      uint32_t value;
      value = duty_1;
      ready_flag = false;
      /* Set the duty cycle - keep trying until PWM is ready... */
      
      

      while ((app_pwm_channel_duty_set(&PWM1, 0, value))&(app_pwm_channel_duty_set(&PWM1, 1, value)) == NRF_ERROR_BUSY);
 
      /* ... or wait for callback. */
      while (!ready_flag);
      APP_ERROR_CHECK(app_pwm_channel_duty_set(&PWM1, 1, value));
      APP_ERROR_CHECK(app_pwm_channel_duty_set(&PWM1, 0, value));

}


void in_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
   gpio_flag =1;
   nrf_drv_timer_enable(&TIMER_LED);

   
}

void timer0_handler(nrf_timer_event_t event_type, void* p_context)
{
  switch (event_type)
    {
        case NRF_TIMER_EVENT_COMPARE0:
             timer_flg =1;
             nrf_drv_timer_clear(&TIMER_LED);
             gpio_flag=0;
             pwm_update();
            break;

        default:
            //Do nothing.
            break;
    }
}


void timer_init(void)
{
    uint32_t time_us = 500; 
    uint32_t time_ticks;
    uint32_t err_code = NRF_SUCCESS;

    nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
    err_code = nrf_drv_timer_init(&TIMER_LED, &timer_cfg, timer0_handler);
    APP_ERROR_CHECK(err_code);

    time_ticks = nrf_drv_timer_us_to_ticks(&TIMER_LED, time_us);
    nrf_drv_timer_compare(
         &TIMER_LED, NRF_TIMER_CC_CHANNEL0, time_ticks, true);

    
}

Parents
  • Hello,

    I am quite new to nordic environment.

    Welcome! Please do not hesitate to ask if you should have any questions.

    I am working on an application where I need to start pwm sequence after particular delay (accurate delay of multiple of 100us).

    Could you show me the entirety of your main.c code?
    Right now I do not see where you setup your gpio for triggering the enabling of the timer, so I am not sure that the timer is ever started, for instance.

    Could you also elaborate on what behavior you are seeing, and how this differs from what you would expect? Does the device reset for example, or is it just that 'nothing' is happening, for example?

    Best regards,
    Karl

  • Hello Karl,

    Right now I do not see where you setup your gpio for triggering the enabling of the timer, so I am not sure that the timer is ever started, for instance.

    void in_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
    {
       gpio_flag =1;
       nrf_drv_timer_enable(&TIMER_LED);
    
       
    }

    Could you also elaborate on what behavior you are seeing, and how this differs from what you would expect? Does the device reset for example, or is it just that 'nothing' is happening, for example?

    I am not getting the delay I am expecting.

    I think I have done some wrong configuration in linking to timer from gpio interrupt routine. 

    Best regards,
    Ram



  • Hello Karl,

    You could check that the EVENTS_SEQSTARTED[n] event happens. This will indicate that the sequence has started.
    You can add this event as a case in the pwm handler, and then either add a breakpoint or a logger statement to indicate that the event is generated as expected.

    I created as you told please verify it ,is it right ?

    void pwm_handler(nrf_pwm_event_t event_type)
    {
     switch(event_type)
     {
       case NRF_PWM_EVENT_SEQSTARTED0:
            NRF_LOG_INFO("PWM IS STARTED");
            break;
       default:
       // nothing
            break;
     }
    }
    
    APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0, &config0, pwm_handler));
     

    Because after adding this code , I tested with debug breakpoint but it never come true that is cursor goes to default never stop in case NRF_PWM_EVENT_SEQSTARTED0.

    Best regards,
    Ram 

  • Hello Ram,

    It looks like you are doing it correct in your code, so I was surprised that this did not work. I looked into the nrfx_pwm driver, which is what is being called behind the scenes of the nrf_drv_pwm_init call, and it seems that it does not forward the _SEQSTARTED0 event after all, my mistake.
    You could either edit the driver to pass the STARTED event to the application layer, or use the SEQEND0 event instead. I would recommend the latter, since it comes with lower risk (modifying a driver is always a potential way to break it), and since using the SEQEND event instead should be fine in this case - we only need to know that the PWM has successfully started in order to disable the startup peripherals.
    Apologies for the inconvenience. Could you try this, and let me know if this resolves the compounded delay issue?

    Best regards,
    Karl

  • Hello Karl,

    You could either edit the driver to pass the STARTED event to the application layer, or use the SEQEND0 event instead. I would recommend the latter, since it comes with lower risk (modifying a driver is always a potential way to break it), and since using the SEQEND event instead should be fine in this case - we only need to know that the PWM has successfully started in order to disable the startup peripherals.

    First I didn't understand how SEQEND0 will return because sequence is going on .

    But still I tried with case NRF_PWM_EVENT_SEQEND0: in the place of  case NRF_PWM_EVENT_SEQSTARTED0: . But in debug the cursor isn't coming as  event_type return 0x0000 value.

    Maybe some where initialization is not happening correctly or some other reason.

    Apologies for the inconvenience. Could you try this, and let me know if this resolves the compounded delay issue?

    No issue Karl,  I am really happy with your support and I really positively hoping  that we are going to solve this very soon.

    Best regards,
    Ram 

  • RAM_MS said:
    No issue Karl,  I am really happy with your support and I really positively hoping  that we are going to solve this very soon.

    I am happy to you that, Ram!
    I too hope we will resolve this last issue soon.

    RAM_MS said:

    First I didn't understand how SEQEND0 will return because sequence is going on .

    But still I tried with case NRF_PWM_EVENT_SEQEND0: in the place of  case NRF_PWM_EVENT_SEQSTARTED0: . But in debug the cursor isn't coming as  event_type return 0x0000 value.

    Maybe some where initialization is not happening correctly or some other reason.

    Oh, I should have been more explicit again, my apologies.
    Please check for the NRFX_PWM_EVT_END_SEQ0 event in your pwm_handler.
    The PWM HAL event NRF_PWM_EVENT_SEQEND0 is only ever used by the nrfx driver behind the scenes, and not actually ever forwarded to the application layer event handler. If the NRF_PWM_EVENT_SEQEND0 event is generated by the PWM peripheral, the nrfx driver will forward NRFX_PWM_EVT_END_SEQ0 to the application layer.

    If my understanding of the end event is correct then the end event will be generated whenever the section has been played once, before it loops.

    void pwm_handler(nrf_pwm_event_t event_type)
    {
        switch(event_type)
        {
            case NRFX_PWM_EVT_END_SEQ0:
                NRF_LOG_INFO("PWM IS STARTED");
                disable_pwm_startup_peripherals();
                break;
            default:
                // nothing
                break;
        }
    }


    Alternatively, you could for debugging's sake add the following to the nrfx_pwm.c irq_handler function:
    ..
        if (nrf_pwm_event_check(p_pwm, NRF_PWM_EVENT_EVENTS_SEQSTARTED0))
        {
            nrf_pwm_event_clear(p_pwm, NRF_PWM_EVENT_EVENTS_SEQSTARTED0);
    
            p_cb->handler(NRFX_PWM_EVT_STARTED_SEQ0);
        }
    ..

    But since the nrfx_pwm driver does not have a dedicated started event we will also have to add the following modification into nrfx_pwm.h:
    ..
    /** @brief PWM driver event type. */
    typedef enum
    {
        NRFX_PWM_EVT_FINISHED, ///< Sequence playback finished.
        NRFX_PWM_EVT_END_SEQ0, /**< End of sequence 0 reached. Its data can be
                                    safely modified now. */
        NRFX_PWM_EVT_END_SEQ1, /**< End of sequence 1 reached. Its data can be
                                    safely modified now. */
        NRFX_PWM_EVT_STOPPED,  ///< The PWM peripheral has been stopped.
        NRFX_PWM_EVT_STARTED_SEQ0,  ///< Sequence 0 has started.
    } nrfx_pwm_evt_type_t;
    ..


    In general it is recommended to avoid modifying the provided drivers directly, since this may break them or place them into invalid states, but I list it here as an alternative approach nevertheless, since we are in the process of debugging the issue of the growing phase shift of the outputted PWM.
    Keep in mind that this driver modification will make the started event be sent to your application layer frequently, so you should add a check in the pwm_handler so that it only disables the peripherals the first time it is generated.

    The important part here is that we get to test if the disabling of the startup peripherals will resolve the phase shifting issue of the output PWM.

    Best regards,
    Karl

  • Hello Karl,

    Please check for the NRFX_PWM_EVT_END_SEQ0 event in your pwm_handler.
    The PWM HAL event NRF_PWM_EVENT_SEQEND0 is only ever used by the nrfx driver behind the scenes, and not actually ever forwarded to the application layer event handler. If the NRF_PWM_EVENT_SEQEND0 event is generated by the PWM peripheral, the nrfx driver will forward NRFX_PWM_EVT_END_SEQ0 to the application layer.

    NRFX_PWM_EVT_END_SEQ0 is not returned instead NRFX_PWM_EVT_FINISHED is returning.

    So I edited program like this

    static void disable_pwm_startup_peripherals()
    {
     nrf_drv_gpiote_in_event_disable(PIN_IN);
     nrf_drv_ppi_channel_disable(ppi_channel_1);
     nrfx_timer_disable(&PWM_DELAY_TIMER);
     nrf_drv_ppi_channel_disable(ppi_channel_2);
    
    }
    
    static void irq_handler(nrfx_pwm_evt_type_t event_type)
    {
     
     switch(event_type)
     {
       case NRFX_PWM_EVT_FINISHED:
            NRF_LOG_INFO("PWM IS STARTED");
            disable_pwm_startup_peripherals();
            break;
       default:
       // nothing
            break;
     }
    }
    

    But this also not solving the issue .

    Best regards,
    Ram 

Reply
  • Hello Karl,

    Please check for the NRFX_PWM_EVT_END_SEQ0 event in your pwm_handler.
    The PWM HAL event NRF_PWM_EVENT_SEQEND0 is only ever used by the nrfx driver behind the scenes, and not actually ever forwarded to the application layer event handler. If the NRF_PWM_EVENT_SEQEND0 event is generated by the PWM peripheral, the nrfx driver will forward NRFX_PWM_EVT_END_SEQ0 to the application layer.

    NRFX_PWM_EVT_END_SEQ0 is not returned instead NRFX_PWM_EVT_FINISHED is returning.

    So I edited program like this

    static void disable_pwm_startup_peripherals()
    {
     nrf_drv_gpiote_in_event_disable(PIN_IN);
     nrf_drv_ppi_channel_disable(ppi_channel_1);
     nrfx_timer_disable(&PWM_DELAY_TIMER);
     nrf_drv_ppi_channel_disable(ppi_channel_2);
    
    }
    
    static void irq_handler(nrfx_pwm_evt_type_t event_type)
    {
     
     switch(event_type)
     {
       case NRFX_PWM_EVT_FINISHED:
            NRF_LOG_INFO("PWM IS STARTED");
            disable_pwm_startup_peripherals();
            break;
       default:
       // nothing
            break;
     }
    }
    

    But this also not solving the issue .

    Best regards,
    Ram 

Children
  • Hello Karl,

                    I am implemented a new logic with a different controller and its working properly .

                     And its very small code also using only external interrupt ,Timer (in one shot mode)and GPIO

                    without PWM.

    I am attaching that for your reference .

    void EINT024_IRQHandler(void)
    {
        /* To check if PA.6 external interrupt occurred */
        if(GPIO_GET_INT_FLAG(PA, BIT6))
        {
    			  
                  GPIO_CLR_INT_FLAG(PA, BIT6);
    			  flg_Start_PWM=1;
    			  TIMER_SET_CMP_VALUE(TIMER0, delay);
    			  TIMER_Start(TIMER0);	
    		}
       
    }
    
    void TMR0_IRQHandler(void)
    {
        if(TIMER_GetIntFlag(TIMER0) == 1)
        {
    			  if(PA6 == 1) // Checking either rising edge or falling
    			 {
    				/* Clear Timer0 time-out interrupt flag */
            TIMER_ClearIntFlag(TIMER0);
    				
    				PB5=1;
    				PB4=0;
    				
    	     }
    			 else    //logic for falling edge
    				{
    					/* Clear Timer0 time-out interrupt flag */
              TIMER_ClearIntFlag(TIMER0);
    					
    					PB5=0;
    				    PB4=1;
    			    
    					
    				}      
        }
                
     }
    

    Explanation for details.

    1)Enabled External Interrupt pin For both edge for INPUTTED PWM Signal.

    2) Then start Timer in Both Edge in ONE SHOT MODE.(That's why I loaded Compare value every time)

    3)Then in TMR_INTERRUPT HANDLER handle the GPIO as per required.

    4) And Finally Tested with external INPUTTED PWM SIGNAL .

    I am attaching video of that for your reference .

    So Karl Please guide to implement  these steps in NORDIC .

    Best regards,
    Ram 

  • Hello again, Ram

    RAM_MS said:
    I am implemented a new logic with a different controller and its working properly .
    RAM_MS said:
    So Karl Please guide to implement  these steps in NORDIC .

    The big difference between the implementation you have made for the other µC and the one we have been working on is that the one for the other µC uses the CPU to set and clear the GPIO.
    This is not a viable approach for a larger project - since you will not have any guarantees that the CPU will not be busy in a higher / equal priority interrupt, which will delay the set/clear execution and lead to a incorrect waveform.

    I've attached an example that shows how what you describe can be done. A toggle on the PIN_IN starts a timer that repeatedly toggles the PIN_OUT at a given interval. If you would like the start delay and pulse length to be different you will need to add another timer instance to implement the start delay, and then have it start the toggle timer upon expiring.
    __delayed_gpiote_toggle.zip

    Best regards,
    Karl

  • Hello Karl,

    The big difference between the implementation you have made for the other µC and the one we have been working on is that the one for the other µC uses the CPU to set and clear the GPIO.
    This is not a viable approach for a larger project - since you will not have any guarantees that the CPU will not be busy in a higher / equal priority interrupt, which will delay the set/clear execution and lead to a incorrect waveform.

    Actually I just want to discuss same ,is this approach is ok?

    At the same time I am trying the method what we discussed yet and given you the tested result but you didn't reply that.

    Please reply on that also.

    Best regards,
    Ram

  • Hello again, Ram

    RAM_MS said:
    Actually I just want to discuss same ,is this approach is ok?

    It will depend on your applications requirements and constraints. For example, how detrimental is it that the waveform is accurately periodic and well-behaved?
    If you use the CPU to generate the waveform and you have other things happening in your program with equal or higher priority you will never be able to guarantee a correct waveform with the CPU approach, which is why this often is not a viable method for waveform generation.

    RAM_MS said:

    At the same time I am trying the method what we discussed yet and given you the tested result but you didn't reply that.

    Please reply on that also.

    Oh, I thought your previous comment meant that you had moved on from that approach.

    Do you see any difference to the behavior of the waveform when you disable the peripherals?

    Best regards,
    Karl

  • Hello Karl,

    It will depend on your applications requirements and constraints. For example, how detrimental is it that the waveform is accurately periodic and well-behaved?

    Yes till now Its accurately periodic and well-behaved but till now CPU only handling GPIO , GPIO interrupt ,Timer and UART .

    It might be problematic later .

    Do you see any difference to the behavior of the waveform when you disable the peripherals?

    No ,after disabling also same shifting of waveform issue is there.

    Best regards,
    Ram

Related