Clear PWM signal in watchdog event handler

Hi,

In our application we use a motor. In the case that the watchdog would trigger while the motor is running, we need to stop the motor. For this we need to set the PWM signal to 0 in the watchdog event handler. However I noticed that when I run 

 while (app_pwm_channel_duty_set(&PWM0, 0, 0) == NRF_ERROR_BUSY);
 to set the PWM signal to 0 percent, it doesn't stop the motor in the watchdog event handler. I think this might be because my PWM period is 20Khz, and I read somewhere it can take more then 2 PWM periods to change the value. This might be too long for the watchdog event handler, because when I set this value manually it immediately stops the motor.

Is there a way to disconnect the PWM immediately from a pin and set the pin to 0 with very low latency?

Parents
  • Hello,

    Is there a way to disconnect the PWM immediately from a pin and set the pin to 0 with very low latency?

    For the lowest possible latency between the pin triggering and the pwm stopping you could use the PPI peripheral to connect a GPIOTE IN_EVENT to the PWM peripherals TASKS_STOP. This will make the pin event immediately trigger the STOP task of the PWM peripheral, without requiring any CPU intervention (which might be what is adding to your delay here - if the CPU is busy with a higher-priority task or similar).

    If you are not already familiar with the PPI peripheral and how to use it, you could take a look at the PPI example or the  SAADC example which demonstrates its usage.

    Please do not hesitate to ask if you should encounter any issues or questions regarding this!

    Best regards,
    Karl

Reply
  • Hello,

    Is there a way to disconnect the PWM immediately from a pin and set the pin to 0 with very low latency?

    For the lowest possible latency between the pin triggering and the pwm stopping you could use the PPI peripheral to connect a GPIOTE IN_EVENT to the PWM peripherals TASKS_STOP. This will make the pin event immediately trigger the STOP task of the PWM peripheral, without requiring any CPU intervention (which might be what is adding to your delay here - if the CPU is busy with a higher-priority task or similar).

    If you are not already familiar with the PPI peripheral and how to use it, you could take a look at the PPI example or the  SAADC example which demonstrates its usage.

    Please do not hesitate to ask if you should encounter any issues or questions regarding this!

    Best regards,
    Karl

Children
  • Thanks for your reply. I don't get it working, I have this code:

    In ppi_init:

        uint32_t gpio_event_addr;
        uint32_t pwm_task_addr;
    
        nrf_drv_ppi_channel_alloc(&ppi_channel3);
        gpio_event_addr = nrfx_gpiote_in_event_addr_get(25);
        nrfx_gpiote_in_event_enable(25, true);
        pwm_task_addr = 0x4001C004;
        nrfx_ppi_channel_assign(ppi_channel3, gpio_event_addr, pwm_task_addr);
        nrfx_ppi_channel_enable(ppi_channel3);

    In gpio_init:

        nrf_gpio_cfg_input(25, NRF_GPIO_PIN_NOPULL);

    and then when I press a button to trigger the task from the event:

                nrf_gpiote_event_enable(25);

    But this doesn't work

  • Hello,

    First and foremost I notice that you are not checking any of the returned error codes. Please always make sure to pass the error codes returned by SDK function calls to an APP_ERROR_CHECK, or implement specific error handling, in order to be alerted in the case that a function call fails unexpectedly.
    Without checking the error codes you will never be alerted to a function call failing, and thus you will be unable to take any steps to avoid the program proceeding into a undefined state or general breakage.
    Could you show me your configuration of the GPIOTE pin as well?
    How did you acquire the pwm_task_addr?

    I also notice that you are referring to your pins with their number directly. I highly recommend making a #define for each pin you will be using, and targeting it using the NRF_GPIO_PIN_MAP macro to do so - to reduce the chance of targeting the wrong pin, and to increase maintainability of your code.

    Best regards.
    Karl

  • Hi,

    Yes you are right about the APP_ERROR_CHECK, I do this in all my code, but I forgot it in this part. Thanks for reminding me.

    Could you show me your configuration of the GPIOTE pin as well?

        err_code = nrfx_gpiote_in_init(25, &config2, gpiote_evt_handler);
        APP_ERROR_CHECK(err_code);

    How did you acquire the pwm_task_addr?

    Because I make use of the app_pwm library, I can't find the address of PWM0 by nrfx_pwm_event_address_get, because the instance type is different. Thats why I thought I can enter it manually because the base address of PWM0 is 0x4001C000 and TASK_STOP has an offset of 0x004, so that would be 0x4001C004. But now when I do the APP_ERROR_CHECK I get an NRF_ERROR_NO_MEM error so perhaps this is not correct.

    How can I retrieve the address of PWM0 when I use the app_timer library?

  • Gueston said:
    Yes you are right about the APP_ERROR_CHECK, I do this in all my code, but I forgot it in this part. Thanks for reminding me.

    No problem at all - it happens every now and then.
    Do you see any errors being generated when you check the returned error codes?

    Could you also show me your entire GPIOTE configuration, not just the init function call?

    Gueston said:

    Because I make use of the app_pwm library, I can't find the address of PWM0 by nrfx_pwm_event_address_get, because the instance type is different. Thats why I thought I can enter it manually because the base address of PWM0 is 0x4001C000 and TASK_STOP has an offset of 0x004, so that would be 0x4001C004. But now when I do the APP_ERROR_CHECK I get an NRF_ERROR_NO_MEM error so perhaps this is not correct.

    How can I retrieve the address of PWM0 when I use the app_timer library?

    Oh, I was under the impression that you were using the pwm peripheral, not the pwm library - my mistake.
    The difference between the pwm peripheral and the pwm library implementation is that the pwm library does not use the pwm peripheral at all, but instead generates the PWM using a combination of PPI, TIMER and GPIOTE instances.
    To stop the timer used by the PWM library by triggering the TIMER instance's TASKS_STOP task - this should immediately stop the PWM generation.

    Which function is returning the NRF_ERROR_NO_MEM? Please make sure to have DEBUG defined in your preprocessor defines, like shown in the included image. This will make the logger output a detailed error message whenever a non-NRF_SUCCESS error message is passed to an APP_ERROR_CHECK.


    Best regards,
    Karl

Related