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 again,

    RAM_MS said:
    Thank you so much Karl ,this was the problem in my code after arranging this correcting now I am able to get exact TIMING Delay .

    No problem at all, Ram - I am happy to hear that this resolved the added delay issue!

    RAM_MS said:
    Sorry ,You are right I shared the code that was used to test the TIMER's accuracy.
    RAM_MS said:
    Yes ,you are right I am using that variable to store it and pass it to the PPI assign function.
    RAM_MS said:
    As always Yellow one is INPUTTED PWM SIGNAL and Blue one is my OUTPUT PWM .

    Thank you for clarifying these things.

    RAM_MS said:

    For testing purpose I generated inputting PWM signal from PIN NO 11( that is PWM_PIN 11) in the code you might seen it and giving it to (PIN_IN 22). 

    But in real case the inputting PWM will come from outside .

    So for create this scenario I created inputted PWM signal from a different NRF52 DK .

    And what I observed is my Generated PWM Signal is Shifting with time.

    For your reference I am attaching the video of it.

    That is very strange indeed. Perhaps there is something amiss with the sequence looping.

    Could you share the newest version of the code - the code you ran when you observed this - so I may see if I can spot any potential explanation for this?

    Best regards,
    Karl

  • Hello Karl,

    I am happy to hear that this resolved the added delay issue!

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

    And Thank you a lot .

    That is very strange indeed. Perhaps there is something amiss with the sequence looping.

    Could you share the newest version of the code - the code you ran when you observed this - so I may see if I can spot any potential explanation for this?

    For your reference I am attaching code here.

    7384.main.zip

    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);

    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

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

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

Related