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:

    Definitely ,Because I did it STM32 by using TIMER in one pulse mode and its working properly .

    As BLE is needed in this application ,I am trying to do in Nordic.

    And yes duty cycle of incoming PWM always will be same .

    Yes ,My goal is to output the same waveform out ,as it was received in, with only the delay added from (100 Âµs to 10ms).

    Here I am attaching the pictures of what I achieved in STM32 for your reference.

    Thank you for elaborating on the requirements and operation.
    If the duty cycle will always be the same, that makes things easier.

    Then, you might be able to do this more easily, using less resources, if you instead set up the waveform using the PWM driver to ready the waveform - setting the NRFX_PWM_FLAG_LOOP flag as the last argument of the _playback() function call - , and then have the TIMER CC event trigger the TASKS_SEQSTART[0] to start the PWM peripheral instead. Once started you could disable the GPIOTE low_to_high detection, and the PPI channels.
    This way, you will not have to continuously monitor and set/clear the output pin.
    This will ensure that the PWM sequence is started after a given delay, and run until stopped. If you need to change the delay you will need to stop the PWM, re-enable the GPIOTE in event, and re-enable the PPI channels.

    RAM_MS said:
    I will definitely do this a try.

    Great, let me know if you encounter any issues or questions!

    Best regards,
    Karl

  • Hello Karl,

    then have the TIMER CC event trigger the TASKS_SEQSTART[0] to start the PWM peripheral instead. Once started you could disable the GPIOTE low_to_high detection, and the PPI channels.

    Can you give me a demo of specially these things (inside the quote).

    Or any link where this scenario is there.

    Thanks & regards,
    Ram

  • I unfortunately do not have a demo of this particular usage, no, but you could look at the PWM driver demonstration from the pwm_driver example in the SDK to see how you could set up a PWM waveform, then, see the SAADC example for how to connect an event to a task through PPI, then connect these events as described.

    If going by the pwm driver approach you should also have some logic disable the PPI channels and GPIOTE detection to save resources, as mentioned in my previous comment.

    Give this a try, and let me know if you encounter any issues or questions.

    Best regards,
    Karl

  • Hello Karl,

    Give this a try, and let me know if you encounter any issues or questions.

    Definitely ,I am trying to test what you suggested and will update you soon about it.

    Best regards,
    Ram

  • Great, I look forward to hearing about it! :)

    Best regards,
    Karl

Reply Children
  • Hello Karl,

                   I tried with what you said.

                  Can please verify the code ,because I am not sure do I done it right way or not.

                 So I am attaching the main.c  file in zip format for your reference.8400.main.zip

       I am waiting for your verification responses.

       Best regards,
      Ram

  • Hello again, Ram

    I have taken a look at the provided file, and here are my general comments:

    Overall I think you are on the right path, but it seems you have not implemented everything just yet. Your current pwm_update_duty_cycle is called by the CPU - which leaves it vulnerable to added delay due to other tasks of higher priority that the CPU has to finish handling before triggering the task.
    Instead, you should provide the NRF_DRV_PWM_FLAG_START_VIA_TASK flag to your simple_playback function as well, and connect the TIMER CC event to the PWM START_SEQ task through PPI - this will start the readied sequence as soon as the timer expires. You can read more about this particular usage in the PWM Driver usage documentation. You should also disable the PPI channels and timer + GPIOTE when the PWM is successfully started, and re-enable them if you need to update your delay later.
    You will also have to implement a function for updating the delay, that changes the expiration timeout and starts the timer. I am not yet sure what logic you would like to have trigger this change, but you could start by tying it up to a button press if you have not already decided what should trigger it.
    I.e implement a button handler that updates the delay, re-enables the PPI channels and the GPIOTE + TIMER peripherals, so that the new PWM's delay is updated.

    Additionally, some other minor comments:

    - You should use the NRF_GPIO_PIN_MAP macro for defining pins, instead of just writing their number. This will let you effortlessly use pins from both ports without any added issue, and make sure that the output of the macro is as expected by the SDK functions.

    - In general I would advice you to have power management running in you main loop, like demonstrated in the BLE peripheral examples. This will drastically reduce your power consumption in comparison to your current empty main loop, while not degrading your performance at all.

    Keep on the current path, this looks very promising! :) 

    Best regards,
    Karl

  • Hello Karl,

    You will also have to implement a function for updating the delay, that changes the expiration timeout and starts the timer. I am not yet sure what logic you would like to have trigger this change, but you could start by tying it up to a button press if you have not already decided what should trigger it

    At this point I don't want to update the delay instead at constant 1ms delay my pwm output should follow the in put pwm signal.

    our current pwm_update_duty_cycle is called by the CPU - which leaves it vulnerable to added delay due to other tasks of higher priority that the CPU has to finish handling before triggering the task.
    Instead, you should provide the NRF_DRV_PWM_FLAG_START_VIA_TASK flag to your simple_playback function as well, and connect the TIMER CC event to the PWM START_SEQ task through PPI - this will start the readied sequence as soon as the timer expires.

     pwm_start_task_address =
            nrf_drv_pwm_simple_playback(&m_pwm0, &seq, 1,
                NRF_DRV_PWM_FLAG_LOOP |
                NRF_DRV_PWM_FLAG_START_VIA_TASK);
                
    
    compare_event_addr = nrf_drv_timer_task_address_get(&PWM_DELAY_TIMER,NRF_TIMER_CC_CHANNEL0);
    
    nrf_drv_ppi_channel_assign(ppi_channel, compare_event_addr,pwm_start_task_address);

    Is this right way to connecting?

    Best regards,
    Ram

  • Hello Ram,

    RAM_MS said:
    At this point I don't want to update the delay instead at constant 1ms delay my pwm output should follow the in put pwm signal.

    Thank you for clarifying - you can then disregard the section of my previous comment relating to the updated-delay implementation.

    RAM_MS said:
    Is this right way to connecting?

    No, not exactly. Your call to nrf_drv_pwm_simple_playback looks correct, but it should be called at the end of your pwm init function, since this will ready the PWM to output the specified sequence once the start task is triggered. Remember to check its returned error code as well.

    You will need to use the nrf_pwm_task_address_get function to get the task address of the NRF_PWM_TASK_SEQSTART0.

    Best regards,
    Karl

  • Hello Karl,

    No, not exactly. Your call to nrf_drv_pwm_simple_playback looks correct, but it should be called at the end of your pwm init function, since this will ready the PWM to output the specified sequence once the start task is triggered. Remember to check its returned error code as well.

    You will need to use the nrf_pwm_task_address_get function to get the task address of the NRF_PWM_TASK_SEQSTART0.

    static void gpio_init(void)
    {
        ret_code_t err_code;
        
    
        err_code = nrf_drv_gpiote_init();
        APP_ERROR_CHECK(err_code);
    
        // Initialize the PPI module, make sure its only enabled once in your code
        err_code = nrf_drv_ppi_init();
        APP_ERROR_CHECK(err_code);
    
    // Allocate the channel from the available PPI channels
        err_code = nrf_drv_ppi_channel_alloc(&ppi_channel);
        APP_ERROR_CHECK(err_code);
    
        nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_LOTOHI(true);
        in_config.pull = NRF_GPIO_PIN_PULLDOWN;
    
        err_code = nrf_drv_gpiote_in_init(PIN_IN, &in_config, in_pin_handler);
        APP_ERROR_CHECK(err_code);
    
    
    
        nrf_drv_gpiote_in_event_enable(PIN_IN, true);
    }
    
    void timers_init(void)
    {
        uint32_t err_code;
        uint32_t timer_ticks;
        
        nrfx_timer_config_t timer_cfg = NRFX_TIMER_DEFAULT_CONFIG;
        timer_cfg.frequency = NRF_TIMER_FREQ_1MHz;
        err_code = nrfx_timer_init(&PWM_DELAY_TIMER, &timer_cfg,pwm_delay_timeout_handler);
        APP_ERROR_CHECK(err_code);
    
        timer_ticks = nrfx_timer_us_to_ticks(&PWM_DELAY_TIMER, PWM_DELAY_US);
    
        nrfx_timer_extended_compare(&PWM_DELAY_TIMER, 
                                      NRF_TIMER_CC_CHANNEL0, 
                                      timer_ticks,
                                      NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
                                      true);
    
       nrfx_timer_enable(&PWM_DELAY_TIMER);
    
        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, compare_event_addr,pwm_task_addr);
        APP_ERROR_CHECK(err_code);
    
    
        err_code = nrf_drv_ppi_channel_enable(ppi_channel);
        APP_ERROR_CHECK(err_code);
    
    }
    
    
    
    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_LOOP|NRF_DRV_PWM_FLAG_START_VIA_TASK));
                    
    }
    

    Did this initialization and connection right ???

     Best regards,
     Ram

Related