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,

    Try this, and let me know what the logger says when this happens.

    <info> app: PPI example started.

    <info> app: LED started.

    <info> app: gpio started.

    <info> app: timer started.

    <error> app: ERROR 1073856524 [Unknown error code] at C:\Nordic\DeviceDownload\nRF5SDK1702d674dde\nRF5_SDK_17.0.2_d674dde\examples\my proj\pwm_common_mode\main.c:176

    PC at: 0x00006BA5

    <error> app: End of error report

    And my main code is:

    int main(void)
    {
    
        uint32_t err_code;
    
        err_code = NRF_LOG_INIT(NULL);
        APP_ERROR_CHECK(err_code);
    
        NRF_LOG_DEFAULT_BACKENDS_INIT();
        ppi_init();
        NRF_LOG_INFO("PPI example started.");
        bsp_board_init(BSP_INIT_LEDS);
        NRF_LOG_INFO("LED started.");
        gpio_init();
        NRF_LOG_INFO("gpio started.");
        timers_init();
        NRF_LOG_INFO("timer  started.");
        pwm_init();
        
        NRF_LOG_INFO("pwm started.");
         
        unsigned int val;
        
        while (1)
        {
          
          
           
        }
    }

    This means Its coming from pwm_init() function.

    Best regards,
     Ram

  • Hello again, Ram

    RAM_MS said:

    <error> app: ERROR 1073856524 [Unknown error code] at C:\Nordic\DeviceDownload\nRF5SDK1702d674dde\nRF5_SDK_17.0.2_d674dde\examples\my proj\pwm_common_mode\main.c:176

    PC at: 0x00006BA5

    Exactly which function call returned the error code passed to an APP_ERROR_CHECK on line 176 of main?
    Could you share the entire pwm_init function as it currently is? To proceed we will need to pinpoint which APP_ERROR_CHECK that triggered the reset, and which function generated the error.

    Best regards,
    Karl

  • Hello Karl,

    Exactly which function call returned the error code passed to an APP_ERROR_CHECK on line 176 of main?
    Could you share the entire pwm_init function as it currently is? To proceed we will need to pinpoint which APP_ERROR_CHECK that triggered the reset, and which function generated the error.

    Here is my entire  pwm_init function as it currently is.

    And on line 176  [[APP_ERROR_CHECK(nrf_drv_pwm_simple_playback(&m_pwm0, &seq, 1,NRF_DRV_PWM_FLAG_LOOP | NRF_DRV_PWM_FLAG_START_VIA_TASK));]]

    static void pwm_init(void)
    {
        nrf_drv_pwm_config_t const config0 =
        {
            .output_pins =
            {
                OUTPUT_PIN, // channel 0
                OUTPUT_PIN2,             // 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_1MHz,
            .count_mode   = NRF_PWM_MODE_UP,
            .top_value    = 20000,
            .load_mode    = NRF_PWM_LOAD_INDIVIDUAL,
            .step_mode    = NRF_PWM_STEP_AUTO
        };
        // Init PWM without error handler
        APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0, &config0, NULL));
    
        seq_values->channel_0 = duty ;
        seq_values->channel_1 = duty | 0x8000;
        
        APP_ERROR_CHECK(nrf_drv_pwm_simple_playback(&m_pwm0, &seq, 1,NRF_DRV_PWM_FLAG_STOP | NRF_DRV_PWM_FLAG_START_VIA_TASK));
                    
    }

    Best regards,
     Ram

  • Oh, I see now - you should not do an APP_ERROR_CHECK on the return value from nrf_drv_pwm_simple_playback, as mention in its API Reference it returns the address of the task you should trigger to start the sequence (this address should be equal to the one you got from the addres_get function in the PPI setup).
    The documentation exempt reads:

    Address of the task to be triggered to start the playback if the NRFX_PWM_FLAG_START_VIA_TASK flag was used, 0 otherwise.

    Best regards,
    Karl

  • Hello Karl,

    Oh, I see now - you should not do an APP_ERROR_CHECK on the return value from nrf_drv_pwm_simple_playback, as mention in its API Reference it returns the address of the task you should trigger to start the sequence (this address should be equal to the one you got from the addres_get function in the PPI setup).

    You mean to say that what Get from  pwm_task_addr= nrf_drv_pwm_task_address_get(&m_pwm0,NRF_PWM_TASK_SEQSTART0);

    Same should use here  like pwm_task_addr=nrf_drv_pwm_simple_playback(&m_pwm0, &seq, 1,NRF_DRV_PWM_FLAG_STOP | NRF_DRV_PWM_FLAG_START_VIA_TASK);


    {
    //ppi init part
    compare_event_addr = nrf_drv_timer_task_address_get(&PWM_DELAY_TIMER,NRF_TIMER_CC_CHANNEL0);
    pwm_task_addr =  nrf_drv_pwm_task_address_get(&m_pwm0,NRF_PWM_TASK_SEQSTART0);
    
    err_code = nrf_drv_ppi_channel_assign(ppi_channel_2, compare_event_addr,pwm_task_addr);
    }
    
    {
    // pwm init part 
    pwm_task_addr=nrf_drv_pwm_simple_playback(&m_pwm0, &seq, 1,NRF_DRV_PWM_FLAG_LOOP | NRF_DRV_PWM_FLAG_START_VIA_TASK);
    }

    Thanks Karl It is working now .

    Many Many Thanks .

    But now there is a problem.

    1)the timing is not correct .Here I am attaching the snapshot of it 

    From the picture you can clearly see that the delay time is more than 1ms .

    How can I make it exact??

    Best regards,
     Ram

Reply
  • Hello Karl,

    Oh, I see now - you should not do an APP_ERROR_CHECK on the return value from nrf_drv_pwm_simple_playback, as mention in its API Reference it returns the address of the task you should trigger to start the sequence (this address should be equal to the one you got from the addres_get function in the PPI setup).

    You mean to say that what Get from  pwm_task_addr= nrf_drv_pwm_task_address_get(&m_pwm0,NRF_PWM_TASK_SEQSTART0);

    Same should use here  like pwm_task_addr=nrf_drv_pwm_simple_playback(&m_pwm0, &seq, 1,NRF_DRV_PWM_FLAG_STOP | NRF_DRV_PWM_FLAG_START_VIA_TASK);


    {
    //ppi init part
    compare_event_addr = nrf_drv_timer_task_address_get(&PWM_DELAY_TIMER,NRF_TIMER_CC_CHANNEL0);
    pwm_task_addr =  nrf_drv_pwm_task_address_get(&m_pwm0,NRF_PWM_TASK_SEQSTART0);
    
    err_code = nrf_drv_ppi_channel_assign(ppi_channel_2, compare_event_addr,pwm_task_addr);
    }
    
    {
    // pwm init part 
    pwm_task_addr=nrf_drv_pwm_simple_playback(&m_pwm0, &seq, 1,NRF_DRV_PWM_FLAG_LOOP | NRF_DRV_PWM_FLAG_START_VIA_TASK);
    }

    Thanks Karl It is working now .

    Many Many Thanks .

    But now there is a problem.

    1)the timing is not correct .Here I am attaching the snapshot of it 

    From the picture you can clearly see that the delay time is more than 1ms .

    How can I make it exact??

    Best regards,
     Ram

Children
  • Hello again, Ram

    RAM_MS said:

    You mean to say that what Get from  pwm_task_addr= nrf_drv_pwm_task_address_get(&m_pwm0,NRF_PWM_TASK_SEQSTART0);

    Same should use here  like pwm_task_addr=nrf_drv_pwm_simple_playback(&m_pwm0, &seq, 1,NRF_DRV_PWM_FLAG_STOP | NRF_DRV_PWM_FLAG_START_VIA_TASK);

    Yes, you could use the address returned by nrf_drv_pwm_simple_playback as the input to the PPI connection.

    RAM_MS said:

    Thanks Karl It is working now .

    Many Many Thanks .

    But now there is a problem.

    Great, I am happy to hear that we have progressed closer to the desired functionality.

    RAM_MS said:

    From the picture you can clearly see that the delay time is more than 1ms .

    How can I make it exact??

    This does indeed not look correct - that is quite the added delay, more than 200 µs it seems.
    Could you clarify exactly what you are scoping here with the blue and yellow line? I assume it is the inputted PWM and the resulting delayed PWM output, but it would be good if you could confirm / specify this.

    To debug this we will need to find out where in the chain of events this delay is introduced.
    Could you try this again with different delays, so we may see if there is an issue with the timer configuration for instance?
    Alternatively, as a debugging measure, you could change to have the TIMER CC event connected to a pin toggle TASK, so we may verify that the timer is producing the correct delay.
    I look forward to hearing what you observe when you test this!

    Best regards,
    Karl

  • Hello Karl,

    This does indeed not look correct - that is quite the added delay, more than 200 µs it seems.
    Could you clarify exactly what you are scoping here with the blue and yellow line? I assume it is the inputted PWM and the resulting delayed PWM output, but it would be good if you could confirm / specify this

    Yes ,you assumed right .Yellow line is inputted PWM and blue line is delayed PWM output.

    To debug this we will need to find out where in the chain of events this delay is introduced.
    Could you try this again with different delays, so we may see if there is an issue with the timer configuration for instance?

    I tested with different delays that is 1ms ,2ms ,3ms etc .Every where the same added delay around 200 µs

    coming.

    Alternatively, as a debugging measure, you could change to have the TIMER CC event connected to a pin toggle TASK, so we may verify that the timer is producing the correct delay.

    I tested this and the output is attached here in below .

    Here yellow line is inputted PWM and blue line is gpio toggle output .

    Here you can clearly notice that 2ms timing is exact.

    Best regards,
    Ram

  • Hello again Ram,

    Thank you for the clarification and update.

    RAM_MS said:

    I tested with different delays that is 1ms ,2ms ,3ms etc .Every where the same added delay around 200 Âµs

    coming.

    RAM_MS said:
    I tested this and the output is attached here in below .
    RAM_MS said:

    Here yellow line is inputted PWM and blue line is gpio toggle output .

    Here you can clearly notice that 2ms timing is exact.

    This is great steps for the debugging - well done!
    So, it it clear that the timer toggles the GPIOTE at a precise interval, and that the delay is added elsewhere. 

    Could you check whether you are receiving any EVENTS_SEQSTARTED events in your pwm event handler?
    To find out how long it takes from the PWM start task is triggered until the SEQSTARTED event is received you could use the nrf_timer_cc_read function in the timer's event handler for the CC event, and then again in the PWM handler for the SEQSTARTED event - then subtract the initial value and the added delay to find the difference.

    Could you show me your code as it is right now? I will take a look and see if I can spot what might be adding the delay.
    Did you change it so that you are providing the pwm_start_task_address returned from the nrf_drv_simple_playback call to the PPI initialization?

    Best regards,
    Karl

  • Hello Karl,

    Could you show me your code as it is right now? I will take a look and see if I can spot what might be adding the delay.

    Here I attached the code for your reference.

    0310.main.zip

    Did you change it so that you are providing the pwm_start_task_address returned from the nrf_drv_simple_playback call to the PPI initialization?

    Yes ,I changed .

    Please verify once is it right in provided code.

    Could you check whether you are receiving any EVENTS_SEQSTARTED events in your pwm event handler?
    To find out how long it takes from the PWM start task is triggered until the SEQSTARTED event is received you could use the nrf_timer_cc_read function in the timer's event handler for the CC event, and then again in the PWM handler for the SEQSTARTED event - then subtract the initial value and the added delay to find the difference.

    I am trying to test this .I will update you soon.

    Best regards,
    Ram

  • RAM_MS said:
    Here I attached the code for your reference.

    Thank you for sharing the code.
    Looking through the code I notice that you are initializing the GPIOTE as a low-accuracy input, in the line:

    nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_LOTOHI(false);

    Could you try to change this to high accuracy, by changing the configuration parameter to true?
    I do not really think that this should matter, since the PORT event will be triggered just as fast as the IN_EVENT, but it would be good to have ruled out as well.

    I also recommend that you define your pins using the NRF_GPIO_PIN_MAP macro instead of just writing their pin number, but this is a very minor comment on my part and it will work just as well with the pins number directly.

    RAM_MS said:

    Yes ,I changed .

    Please verify once is it right in provided code.

    I do not see this change implemented in the code you just shared - it seems like this might be the code that was used to test the TIMER's accuracy.
    I see that you have created a global variable to store the pwm_task_addr returned by the nrf_drv_pwm_simple_playback function call, so I assume you will be using that variable to store it, and pass it to the PPI assign function.
    I would urge you to add a check to the PPI init function that checks if the pwm_task_addr and gpiote_task_addr variables has been set before you proceed to pass them to the PPI peripheral.
    You will also need to run the GPIOTE and PWM init functions before the PPI init function, to make sure that the addresses has been updated before they are connected through the PPI.

    RAM_MS said:
    I am trying to test this .I will update you soon.

    Great, I look forward to hearing what you find out! :) 

    Best regards,
    Karl

Related