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



  • RAM_MS said:

    I am really happy that we patiently progress up to this stage.

    And Thank you a lot .

    No problem at all, Ram - I am happy to help!

    RAM_MS said:

    As earlier I said for testing purpose I generated 1st in the same DK in PWM_PIN(11).

    But for now I commented this part by commenting pwm1_init();

    And giving INPUTTED PWM Signal from Different DK in PIN_IN (22);

    Thank you for clarifying this.

    RAM_MS said:
    For your reference I am attaching code here.

    I do not immediately see the reason for why the phase shift would be happening, but I suspect that it might be because the PPI channels are left enabled throughout the run of the application, so that there might be happening a brief stopping and re-starting of the waveform generation, or similar, causing the phase shift.
    Could you try to implement the stopping / disabling of the GPIOTE pin, TIMER instance and PPI channels when the PWM is successfully started, like we talked about earlier?

    By the way, you can change your GPIOTE_CONFIG_IN_SENSE_LOTOHI(true); back to false again - since this was not the cause of the added delay earlier.

    I also notice that you are still not using the pwm start task address returned from the nrf_drv_pwm_simple_playback call, since it is overwritten by the call to nrf_pwm_task_addr_get in the ppi_init - this should not matter though, since they should point to the same address - but I point it out to avoid any confusion since you have stated earlier that it is this return address that is being used.

    Best regards,
    Karl

  • Hello Karl,

    By the way, you can change your GPIOTE_CONFIG_IN_SENSE_LOTOHI(true); back to false again - since this was not the cause of the added delay earlier.

    Yes ,I did that.

    I also notice that you are still not using the pwm start task address returned from the nrf_drv_pwm_simple_playback call, since it is overwritten by the call to nrf_pwm_task_addr_get in the ppi_init - this should not matter though, since they should point to the same address - but I point it out to avoid any confusion since you have stated earlier that it is this return address that is being used.

    Yes ,actually that was just a by mistake . I made it proper.

    I do not immediately see the reason for why the phase shift would be happening, but I suspect that it might be because the PPI channels are left enabled throughout the run of the application, so that there might be happening a brief stopping and re-starting of the waveform generation, or similar, causing the phase shift.
    Could you try to implement the stopping / disabling of the GPIOTE pin, TIMER instance and PPI channels when the PWM is successfully started, like we talked about earlier?

    How to check if PWM is successfully started ?

    Is there any function in SDK which return this ?

    Because I checked with return value from pwm_init () which is NRF_SUCCESS.

    But this is not right way may be .

    So can you please guide me here.

    Best regards,
    Ram 

  • Hello Ram,

    RAM_MS said:
    Yes ,actually that was just a by mistake . I made it proper.

    Great - it is no problem at all, I just thought I should mention it since I noticed.

    RAM_MS said:

    How to check if PWM is successfully started ?

    Is there any function in SDK which return this ?

    Because I checked with return value from pwm_init () which is NRF_SUCCESS.

    But this is not right way may be .

    So can you please guide me here.

    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.
    You could use this event to indicate that the PWM is successfully running, so that you may disable the GPIOTE, TIMER and PPI channels used to set it up.

    Best regards,
    Karl

  • 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

Reply
  • 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

Children
  • 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 

  • 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

Related