Stop and start PWM with a PPI

Hi,

Is there anyway to start after stopping the PWM with a PPI channel?

There is a start task in the PWM, but

But not defined in the nrfx pwm driver (nrf_pwm.h)

/** @brief PWM tasks. */
typedef enum
{
    NRF_PWM_TASK_STOP      = offsetof(NRF_PWM_Type, TASKS_STOP),             ///< Stops PWM pulse generation on all channels at the end of the current PWM period, and stops the sequence playback.
#if NRF_PWM_HAS_DMA_TASKS_EVENTS
    NRF_PWM_TASK_SEQSTART0 = offsetof(NRF_PWM_Type, TASKS_DMA.SEQ[0].START), ///< Starts playback of sequence 0.
    NRF_PWM_TASK_SEQSTART1 = offsetof(NRF_PWM_Type, TASKS_DMA.SEQ[1].START), ///< Starts playback of sequence 1.
#else
    NRF_PWM_TASK_SEQSTART0 = offsetof(NRF_PWM_Type, TASKS_SEQSTART[0]),      ///< Starts playback of sequence 0.
    NRF_PWM_TASK_SEQSTART1 = offsetof(NRF_PWM_Type, TASKS_SEQSTART[1]),      ///< Starts playback of sequence 1.
#endif
    NRF_PWM_TASK_NEXTSTEP  = offsetof(NRF_PWM_Type, TASKS_NEXTSTEP)          ///< Steps by one value in the current sequence if the decoder is set to @ref NRF_PWM_STEP_TRIGGERED mode.
} nrf_pwm_task_t;

I'm developing a battery application, and the PWM is only needed for a short time, in regular intervals.

Parents
  • Hi!,

    Yes, use NRF_PWM_TASK_SEQSTART0

    #include <hal/nrf_pwm.h>

  • Yes, use NRF_PWM_TASK_SEQSTART0

    No, it cannot start after stopping the PWM (it triggers only once after initialization).

  • Something is then maybe wrong with your PPI configuration. 

    this simple "bare-metal" test shows that it can re-start using that task.

    #include <nrf.h>
    
    #include <hal/nrf_pwm.h>
    
    #include <zephyr/kernel.h>
    
    
    #define PWM_PIN (13UL)
    
    
    int16_t buf[] = {(1 << 15) | 10000}; // Inverse polarity (bit 15)
    
    int main(void)
    {
      // Start accurate HFCLK (XOSC)
      NRF_CLOCK->TASKS_HFCLKSTART = 1;
      while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) ;
      NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
      
      // Configure PWM_PIN as output, and set it to 0
      NRF_GPIO->DIRSET = (1 << PWM_PIN);
      NRF_GPIO->OUTCLR = (1 << PWM_PIN);
      
      
      NRF_PWM0->PRESCALER   = PWM_PRESCALER_PRESCALER_DIV_16; // 1 us
      NRF_PWM0->PSEL.OUT[0] = PWM_PIN;
      NRF_PWM0->MODE        = (PWM_MODE_UPDOWN_Up << PWM_MODE_UPDOWN_Pos);
      NRF_PWM0->DECODER     = (PWM_DECODER_LOAD_Common       << PWM_DECODER_LOAD_Pos) | 
                              (PWM_DECODER_MODE_RefreshCount << PWM_DECODER_MODE_Pos);
      NRF_PWM0->LOOP        = (PWM_LOOP_CNT_Disabled << PWM_LOOP_CNT_Pos);
      
      NRF_PWM0->COUNTERTOP = 20000; // 20ms period
      
      
      NRF_PWM0->SEQ[0].CNT = ((sizeof(buf) / sizeof(uint16_t)) << PWM_SEQ_CNT_CNT_Pos);
      NRF_PWM0->SEQ[0].ENDDELAY = 0;
      NRF_PWM0->SEQ[0].PTR = (uint32_t)&buf[0];
      NRF_PWM0->SEQ[0].REFRESH = 0;
      NRF_PWM0->SHORTS = 0;
      
      NRF_PWM0->ENABLE = 1;
      NRF_PWM0->TASKS_SEQSTART[0] = 1;
    
      k_msleep(1000);
      nrf_pwm_task_trigger(NRF_PWM0,NRF_PWM_TASK_STOP);
      k_msleep(1000);
      nrf_pwm_task_trigger(NRF_PWM0,NRF_PWM_TASK_SEQSTART0);
    
    
      k_msleep(1000);
      nrf_pwm_task_trigger(NRF_PWM0,NRF_PWM_TASK_STOP);
      k_msleep(1000);
      nrf_pwm_task_trigger(NRF_PWM0,NRF_PWM_TASK_SEQSTART0);
    
    
    }

  • I have used the PPI to trigger GPIOTE and it works fine, but don't know why it not works with PWM, the code is below:

  • I have used the PPI to trigger GPIOTE and it works fine, but don't know why it not works with PWM, the code is below:

    #include <nrfx_saadc.h>
    #include <nrfx_timer.h>
    #include <nrfx_ppi.h>
    #include <nrfx_pwm.h>
    #include <nrfx_rtc.h>
    #include <nrfx_gpiote.h>
    #include <zephyr/kernel.h>
    #include <zephyr/logging/log.h>

    #define LED0    DT_GPIO_PIN(DT_ALIAS(led0), gpios)
    #define LED1    DT_GPIO_PIN(DT_ALIAS(led1), gpios)
    // #define LED0    14
    // #define LED1   15

    #define RTC_INSTANCE    2
    // #define SAMPLE_INTERVAL_MS      250
    #define SAMPLE_INTERVAL_MS      4000
    static nrfx_pwm_t pwm_instance = NRFX_PWM_INSTANCE(0);

    LOG_MODULE_REGISTER(pwm_gen, LOG_LEVEL_DBG);

    const nrfx_rtc_t rtc = NRFX_RTC_INSTANCE(RTC_INSTANCE);

    void pwm_event_handler(nrfx_pwm_evt_type_t event_type, void *p_context)
    {
        switch (event_type) {
            case NRFX_PWM_EVT_FINISHED:
                //nrfx_pwm_stop(&pwm_instance, false);
                LOG_DBG("PWM finished");
                break;
            case NRFX_PWM_EVT_END_SEQ0:
                LOG_DBG("PWM end sequence 0");
                break;
            case NRFX_PWM_EVT_END_SEQ1:
                LOG_DBG("PWM end sequence 1");
                break;
            case NRFX_PWM_EVT_STOPPED:
                LOG_DBG("PWM stopped");
                break;
            default:
                LOG_ERR("PWM event: %d", event_type);
                break;
        }
    }

    void rtc_event_handler(nrfx_rtc_int_type_t int_type)
    {
        static int32_t cnt = 0;
        LOG_DBG("RTC Interrupt: %d, %d", int_type, cnt++);
    }

    #define NRFX_RTC_IRQ_HANDLER(instance) CONCAT(nrfx_rtc_, instance, _irq_handler)

    #define NRFX_RTC_MS_TO_TICKS(us,freq) (((us) * (freq)) / 1000U)

    #define GPIOTE_INST NRF_DT_GPIOTE_INST(DT_ALIAS(led3), gpios)
    #define OUTPUT_PIN NRF_DT_GPIOS_TO_PSEL(DT_ALIAS(led3), gpios)
    void init_pwm()
    {
        static nrfx_pwm_config_t pwm_config = NRFX_PWM_DEFAULT_CONFIG(LED0, LED1,
            NRF_PWM_PIN_NOT_CONNECTED, NRF_PWM_PIN_NOT_CONNECTED);

        IRQ_CONNECT(DT_IRQN(DT_NODELABEL(pwm0)),
                    DT_IRQ(DT_NODELABEL(pwm0), priority),
                    nrfx_isr, nrfx_pwm_0_irq_handler, 0);

        pwm_config.base_clock = NRF_PWM_CLK_125kHz;
        pwm_config.count_mode = NRF_PWM_MODE_UP_AND_DOWN;
        pwm_config.load_mode = NRF_PWM_LOAD_INDIVIDUAL;
        pwm_config.step_mode = NRF_PWM_STEP_TRIGGERED;
        pwm_config.top_value = 65535;    
        pwm_config.pin_inverted[0] = true;
        pwm_config.pin_inverted[1] = true;

        if (nrfx_pwm_init(&pwm_instance, &pwm_config, &pwm_event_handler, &pwm_instance) != NRFX_SUCCESS) {
            LOG_ERR("Failed to initialize PWM");
            return;
        }

        static nrf_pwm_values_individual_t seq_values = {
            .channel_0 = 16383,
            .channel_1 = 4095,
            .channel_2 = 0,
            .channel_3 = 0
        };

        static nrf_pwm_sequence_t seq = {
            .values.p_individual = &seq_values,
            .length = NRF_PWM_VALUES_LENGTH(seq_values),
            .repeats = 0,
            .end_delay = 0
        };

        static uint32_t pwm0_start_task;
        pwm0_start_task = nrfx_pwm_simple_playback(&pwm_instance, &seq, 1,  NRFX_PWM_FLAG_STOP);
        //pwm0_start_task = nrfx_pwm_simple_playback(&pwm_instance, &seq, 1, NRFX_PWM_FLAG_STOP);
        LOG_DBG("PWM initialized");

        nrfx_err_t err;
        nrfx_rtc_config_t rtc_cfg = NRFX_RTC_DEFAULT_CONFIG;
        IRQ_CONNECT(DT_IRQN(DT_NODELABEL(CONCAT(rtc, RTC_INSTANCE))),
                    DT_IRQ(DT_NODELABEL(CONCAT(rtc, RTC_INSTANCE)), priority),
                    nrfx_isr, NRFX_RTC_IRQ_HANDLER(RTC_INSTANCE), 0);

        err = nrfx_rtc_init(&rtc, &rtc_cfg, rtc_event_handler);
        if (err != NRFX_SUCCESS) {
            LOG_ERR("Failed to initialize RTC-0, %d", err);
            return;
        }

        uint32_t rtc_ticks = NRFX_RTC_MS_TO_TICKS(SAMPLE_INTERVAL_MS, NRF_RTC_INPUT_FREQ);

        err = nrfx_rtc_cc_set(&rtc, 0, rtc_ticks, false);
        if (err != NRFX_SUCCESS) {
            LOG_ERR("Failed to set RTC comparator");
        }

        nrf_ppi_channel_t m_rtc_counter_clear;
        nrfx_ppi_channel_alloc(&m_rtc_counter_clear);

        /* channel, eep, tep task endpoint address*/
        nrfx_ppi_channel_assign(m_rtc_counter_clear,
            nrfx_rtc_event_address_get(&rtc, NRF_RTC_EVENT_COMPARE_0),
                nrfx_rtc_task_address_get(&rtc, NRF_RTC_TASK_CLEAR));

        nrfx_ppi_channel_fork_assign(m_rtc_counter_clear, nrfx_pwm_task_address_get(&pwm_instance, NRF_PWM_TASK_SEQSTART0));

        //nrfx_ppi_channel_fork_assign(m_rtc_counter_clear, nrfx_pwm_task_address_get(&pwm_instance, NRF_PWM_TASK_STOP));
        //nrfx_ppi_channel_fork_assign(m_rtc_counter_clear, pwm0_start_task);

        // Enable PPI before enabling RTC
        nrfx_ppi_channel_enable(m_rtc_counter_clear);

        nrfx_rtc_enable(&rtc);
    }



  • Hi!
    I will test your code.
    Could you let me know what SDK version you are using?

Reply Children
No Data
Related