This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Pwm Stays on even when proper stop functions are called

Hello,

I am running an application on the s140 soft device using the buttonless dfu as an example. I am using the dual ramp pwm scheme to generate complementary pwm waveforms for a DC-DC converter. This works great for me but I am running into an issue that is perplexing me.

Background:

I have successfully been able to implement an initialization function using nrfx_pwm_init. I then use nrfx_pwm_simple_playback to start my pwm as follows which I got from a dual ramp example I found.

PWM_LoopTable[0].channel_0 = 0x8000 | PwmHiaDutyCycle;
PWM_LoopTable[0].channel_1 = PwmLiaDutyCycle;
(void)nrfx_pwm_simple_playback(&myPWM, &seq0, 1,NRFX_PWM_FLAG_LOOP);

The Pwm starts up with no issues.

When I want to stop the PWM but not completely deinitialize it, I call nrfx_pwm_stop as follows 

 nrfx_pwm_stop(&myPWM, 1);

I understand that the 1 I am passing indicates to the function that it should wait until the pwm is stopped before exiting the function.

Typically, this function works great. I use it very often to regulate voltage. i.e. when the voltage exceeds a certain value I call this function and when it returns back below a threshold I call the nrfx_pwm_simple_playback again and the pwm starts back up.

Occasionally, and with no discernable pattern that I can find, the stop function will not actually stop the pwm. I will call the function, and as I said before it is waiting until nrfx_pwm_is_stopped returns true before exiting. However, The pwm remains on. I have another check in my code every second that is polling nrfx_pwm_is_stopped and this is returning true to me even though the pwm itself is still on. 

I have no idea how this is the case. For now, I am planning on putting a timer based check into my code that will be able to find tis error state and call a full deinitialization of the pwm in an attempt to stop it. However, this feels like a brute force way to do this. Is there some other check function I can use that will determine if pwm is occurring that is deeper than nrfx_pwm_is_stopped() ?? Is there some other way that would ensure the pwm is off when I call nrfx_pwm_stop? 

Any ideas or assistance on this would be much appreciated.

Austin

Parents
  • Here is some more detailed code pieces

    static DeviceState_t PwmState = Off;

    // This is for tracking PWM instances being used, so we can unintialize only
    // the relevant ones when switching from one demo to another.
    #define USED_PWM(idx) (1UL << idx)
    static uint8_t m_used = 0;


    static uint16_t const m_demo1_top = 10000;
    static uint16_t const m_demo1_step = 200;
    static uint8_t m_demo1_phase;
    static nrf_pwm_values_individual_t m_demo1_seq_values;
    static nrf_pwm_sequence_t const m_demo1_seq =
    {
    .values.p_individual = &m_demo1_seq_values,
    .length = NRF_PWM_VALUES_LENGTH(m_demo1_seq_values),
    .repeats = 0,
    .end_delay = 0
    };

    #define MAX_TOP 44 // Operating Frequency = 16MHz/(2*MAX_TOP)
    #define DEAD_BAND 2 // 8 is 1uSec dead band either side at Prescaler div 2 - base clock frequency /2 ->8MHz
    #define PHASE_HIGH (DEAD_BAND+(MAX_TOP/2))

    #define PWM_HIA_START 1
    #define PWM_LIA_START 3

    uint8_t PwmHiaDutyCycle = PWM_HIA_START; //This number must be manipulated as follows: PWM_LoopTable[0].channel_0 = 0x80000 | (PwmHiaDutyCycle);
    uint8_t PwmLiaDutyCycle = PWM_LIA_START; //This number is set directly as follows: PWM_LoopTable[0].channel_1 = PwmLiaDutyCycle;

    // This table is designed to use the Loops mode
    nrf_pwm_values_wave_form_t PWM_LoopTable[] = {
    // Index PWM-1 PWM-2 Other Top Value
    // ===== ====================== ============== =========== ============
    { /* - */ 0x8000 | (PWM_HIA_START), (PWM_LIA_START), (0), (MAX_TOP) }
    };

    static nrfx_pwm_t myPWM = NRFX_PWM_INSTANCE(2);


    #define PIN_PWM_HIA NRF_GPIO_PIN_MAP(0,26)
    #define PIN_PWM_LIA NRF_GPIO_PIN_MAP(0,27)

    void InitDualRampPWM(void)
    {
    uint32_t err_code;
    // Declare a configuration structure and use a macro to instantiate it with default parameters.
    nrfx_pwm_config_t pwm_config = NRFX_PWM_DEFAULT_CONFIG;
    // Configure PWM pins as outputs, and set to 0
    //nrf_gpio_cfg_output(PIN_PWM_HIA);
    //nrf_gpio_cfg_output(PIN_PWM_LIA);
    nrf_gpio_pin_clear(PIN_PWM_HIA);
    nrf_gpio_pin_clear(PIN_PWM_LIA);
    // Boost differential pwm driver pins to high drive if direct drive and not FET inputs
    nrf_gpio_cfg(PIN_PWM_HIA, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
    nrf_gpio_cfg(PIN_PWM_LIA, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
    // Override default PWM parameters:
    pwm_config.output_pins[0] = PIN_PWM_HIA;
    pwm_config.output_pins[1] = PIN_PWM_LIA;
    pwm_config.output_pins[2] = NRFX_PWM_PIN_NOT_USED;
    pwm_config.output_pins[3] = NRFX_PWM_PIN_NOT_USED;

    pwm_config.load_mode = NRF_PWM_LOAD_WAVE_FORM; // Mode of loading sequence data from RAM: 3 == Use individual duty cycle for each PWM channel
    pwm_config.base_clock = PWM_PRESCALER_PRESCALER_DIV_1; // Prescaler - Base clock frequenc: 4 == /16, so 1MHz
    pwm_config.count_mode = PWM_MODE_UPDOWN_UpAndDown; // Operating mode of the pulse generator counter
    pwm_config.irq_priority = 6; // Interrupt priority
    pwm_config.step_mode = 0; // Decoder - Mode of advancing the active sequence: 0 = Sequence
    pwm_config.top_value = 100; // 16-bit Value up to which the pulse generator counter counts: 3-32767, Not used in waveform mode

    // Pass config structure into driver init() function
    err_code = nrfx_pwm_init(&myPWM, &pwm_config, NULL); // (nrfx_pwm_t p_instance, nrfx_pwm_config_t p_config, nrfx_pwm_handler_t handler)
    APP_ERROR_CHECK(err_code);
    }

    void UninitDualRampPWM(void){
    uint32_t err_code;
    nrfx_pwm_uninit(&myPWM); //(nrfx_pwm_t p_instance)
    PwmState = Off;
    }


    nrf_pwm_sequence_t const seq0 =
    {
    .values.p_wave_form = PWM_LoopTable,
    .length = NRF_PWM_VALUES_LENGTH(PWM_LoopTable),
    .repeats = 1,
    .end_delay = 0
    };


    Bool StopPwm(void){
    GpioOutputMosfetDisable();
    nrfx_pwm_stop(&myPWM, 1);
    PwmState = Off;
    }

    bool CheckPwmStop(void){
    return nrfx_pwm_is_stopped(&myPWM);
    }

    void StartPwm(void){
    //Set Duty Cycle and Start PWM playback
    if(PwmHiaDutyCycle == 0){
    PwmHiaDutyCycle = MIN_HIA;
    PwmLiaDutyCycle = MIN_LIA;
    }
    else if(PwmHiaDutyCycle >MAX_HIA){
    PwmHiaDutyCycle = MAX_HIA;
    PwmLiaDutyCycle = MAX_LIA;
    }
    PWM_LoopTable[0].channel_0 = 0x8000 | PwmHiaDutyCycle;
    PWM_LoopTable[0].channel_1 = PwmLiaDutyCycle;
    (void)nrfx_pwm_simple_playback(&myPWM, &seq0, 1,NRFX_PWM_FLAG_LOOP); //TODO: Does this void parenthesis work well?
    //DataHubSetChargingStatus();//Charging Status should always be on any time StartPWM is called
    PwmState = True;
    }

  • Not sure if this is your issue, but if only using 1 value there is an errata which may be applicable, not sue of the internal Nordic nRFx implementation, might be worth checking that:

    [183] PWM: False SEQEND[0] and SEQEND[1] events

    This anomaly applies to IC Rev. Revision 2, build codes CKAA-Dx0, QIAA-Dx0.

    It was inherited from the previous IC revision Engineering D.

    Symptoms

    False SEQEND[0] and SEQEND[1] events are being generated.

    Conditions

    Any of the LOOPSDONE_SEQSTARTn shortcuts are enabled. LOOP register is non-zero and sequence 1 is one value long.

    Consequences

    SEQEND[0] and SEQEND[1] events might falsely trigger other tasks if these are routed through the PPI.

    Workaround

    Avoid using the LOOPSDONE_SEQSTARTn shortcuts, when LOOP register is non-zero and sequence 1 is one value long

  • Looks like I am using this. I am using the nrfx_pwm_simple_playback function and passing in NRFX_PWM_FLAG_LOOP as I pulled this from an exapmle.

    Here is the function I am using.

    uint32_t nrfx_pwm_simple_playback(nrfx_pwm_t const * const p_instance,
    nrf_pwm_sequence_t const * p_sequence,
    uint16_t playback_count,
    uint32_t flags)
    {
    pwm_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
    NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
    NRFX_ASSERT(playback_count > 0);
    NRFX_ASSERT(nrfx_is_in_ram(p_sequence->values.p_raw));

    // To take advantage of the looping mechanism, we need to use both sequences
    // (single sequence can be played back only once).
    nrf_pwm_sequence_set(p_instance->p_registers, 0, p_sequence);
    nrf_pwm_sequence_set(p_instance->p_registers, 1, p_sequence);
    bool odd = (playback_count & 1);
    nrf_pwm_loop_set(p_instance->p_registers,
    (playback_count / 2) + (odd ? 1 : 0));

    uint32_t shorts_mask;
    if (flags & NRFX_PWM_FLAG_STOP)
    {
    shorts_mask = NRF_PWM_SHORT_LOOPSDONE_STOP_MASK;
    }
    else if (flags & NRFX_PWM_FLAG_LOOP)
    {
    shorts_mask = odd ? NRF_PWM_SHORT_LOOPSDONE_SEQSTART1_MASK
    : NRF_PWM_SHORT_LOOPSDONE_SEQSTART0_MASK;
    }
    else
    {
    shorts_mask = 0;
    }
    nrf_pwm_shorts_set(p_instance->p_registers, shorts_mask);

    NRFX_LOG_INFO("Function: %s, sequence length: %d.",
    __func__,
    p_sequence->length);
    NRFX_LOG_DEBUG("Sequence data:");
    NRFX_LOG_HEXDUMP_DEBUG((uint8_t *)p_sequence->values.p_raw,
    p_sequence->length * sizeof(uint16_t));
    return start_playback(p_instance, p_cb, flags,
    odd ? NRF_PWM_TASK_SEQSTART1 : NRF_PWM_TASK_SEQSTART0);
    }

    It looks to me like the if statement I have bolded is what you are referring to which is then used in the function call nrf_pwm_shorts_set(p_instance->p_registers, shorts_mask);

    Could you explain what this shorts_set is doing? Are you recommending that I simply pass in 0 to the nrfx_pwm_simple_playback() funcitonso that this shorts_mask gets set to 0? What are the second/third order effects from this change? I am unfamiliar with what exactly is happening with all of these calls as I took this from an example.

    Something to note, today I switched from the stoppwm function call to fully uninitializing pwm each time and reinitializing it when I want to turn it back on. I have not seen the issue reappear since implementing this scheme but it has been only a half day or so of testing at this point. This is not ideal but it is potentially an acceptable workaround if nothing else works.

    Thank you for your help,

    Austin

Reply
  • Looks like I am using this. I am using the nrfx_pwm_simple_playback function and passing in NRFX_PWM_FLAG_LOOP as I pulled this from an exapmle.

    Here is the function I am using.

    uint32_t nrfx_pwm_simple_playback(nrfx_pwm_t const * const p_instance,
    nrf_pwm_sequence_t const * p_sequence,
    uint16_t playback_count,
    uint32_t flags)
    {
    pwm_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
    NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
    NRFX_ASSERT(playback_count > 0);
    NRFX_ASSERT(nrfx_is_in_ram(p_sequence->values.p_raw));

    // To take advantage of the looping mechanism, we need to use both sequences
    // (single sequence can be played back only once).
    nrf_pwm_sequence_set(p_instance->p_registers, 0, p_sequence);
    nrf_pwm_sequence_set(p_instance->p_registers, 1, p_sequence);
    bool odd = (playback_count & 1);
    nrf_pwm_loop_set(p_instance->p_registers,
    (playback_count / 2) + (odd ? 1 : 0));

    uint32_t shorts_mask;
    if (flags & NRFX_PWM_FLAG_STOP)
    {
    shorts_mask = NRF_PWM_SHORT_LOOPSDONE_STOP_MASK;
    }
    else if (flags & NRFX_PWM_FLAG_LOOP)
    {
    shorts_mask = odd ? NRF_PWM_SHORT_LOOPSDONE_SEQSTART1_MASK
    : NRF_PWM_SHORT_LOOPSDONE_SEQSTART0_MASK;
    }
    else
    {
    shorts_mask = 0;
    }
    nrf_pwm_shorts_set(p_instance->p_registers, shorts_mask);

    NRFX_LOG_INFO("Function: %s, sequence length: %d.",
    __func__,
    p_sequence->length);
    NRFX_LOG_DEBUG("Sequence data:");
    NRFX_LOG_HEXDUMP_DEBUG((uint8_t *)p_sequence->values.p_raw,
    p_sequence->length * sizeof(uint16_t));
    return start_playback(p_instance, p_cb, flags,
    odd ? NRF_PWM_TASK_SEQSTART1 : NRF_PWM_TASK_SEQSTART0);
    }

    It looks to me like the if statement I have bolded is what you are referring to which is then used in the function call nrf_pwm_shorts_set(p_instance->p_registers, shorts_mask);

    Could you explain what this shorts_set is doing? Are you recommending that I simply pass in 0 to the nrfx_pwm_simple_playback() funcitonso that this shorts_mask gets set to 0? What are the second/third order effects from this change? I am unfamiliar with what exactly is happening with all of these calls as I took this from an example.

    Something to note, today I switched from the stoppwm function call to fully uninitializing pwm each time and reinitializing it when I want to turn it back on. I have not seen the issue reappear since implementing this scheme but it has been only a half day or so of testing at this point. This is not ideal but it is potentially an acceptable workaround if nothing else works.

    Thank you for your help,

    Austin

Children
  • It's a little hard to help as I now only use low-level drivers and not the Nordic libraries; also you might use the Insert->Code->C when posting code to make it easier to read, like this:

       pNRF_PWM->SHORTS = NRF_PWM_SHORT_LOOPSDONE_STOP_MASK;

    So here you can see I use a different SHORTS, but also I have more than 1 entry in my sequence. Using waveform mode allows a table, in this case a table of entries for the two pins non-overlapping and out-of-phase, then there can be more than just a value of 1 entry (the table lines can repeat).

    Something like this:

    //  DECODER.LOAD = Repeat
    //  =====================
    //  Charge PWM with Nordic library code
    //
    //   +----------------------------------> COMP0 OUT[0] Compare 0 - PIN_PWM_1
    //   |         +------------------------> COMP1 OUT[1] Compare 1 - PIN_PWM_2
    //   |         |         +--------------> COMP2 OUT[2] Compare 2 - spare
    //   |         |         |               (COMP3 OUT[3] Compare 3 not used)
    //   |         |         |
    // +---------+---------+---------+---------+
    // | Compare | Compare | Compare | Top     | Cycle N
    // +---------+---------+---------+---------+
    // | Compare | Compare | Compare | Top     | Cycle N+1
    // +---------+---------+---------+---------+
    //                                 |
    //                                 +----> COUNTERTOP Cycle Period 4 steps, 1MHz clocks. Range 3-32767

    Which gives this waveform:

    //
    //           |<------------- Charge Cycle 1 ----------->|<------------- Charge Cycle 2 -------->|
    //           |                                          |                                       |
    //           |<---- Chan 0 ---->| |<------ Chan 1 -->|  |<---- Chan 0 ---->| |<------ Chan 1 -->|
    //           |                  | |                  |  |                  |                    |
    //           |                  | |                  |  |                  |                    |
    //           |               -->| |<-Dead-band A  -->|  |<-Dead-band B     |                    |
    //           |                  | |                  |  |                  |                    |
    //                                |                  |                     |
    //           +==================+ |                  |  +================+ |                    +=
    //           |                  | |                  |  |                | |                    |
    //  PWM-1    |  Phase 1         |                       |     Phase 1    |                      |
    // ==========+                  +=======================+                +======================+
    //                                |                  |                     |
    //
    //                                +==================+                     +=================+
    //                                |                  |                     |                 |
    //  PWM-2                         |   Phase 2        |                     |   Phase 2       |
    // ===============================+                  +=====================+                 +====

    This avoids the errata issue since there is more than 1 entry in the sequence table. Something like this:

    nrf_pwm_values_wave_form_t PWM_StepTable[] = {
     //   Index   PIN-PWM-1       PIN-PWM-2  PIN-SPARE Top Value
     //   =====   =============== =========  ========= ============
     { /*   0  */ (0x8000 | HIA), (LIA),     TOP,      TOP },
     { /*   1  */ (0x8000 | HIA), (LIA),     TOP,      TOP },
    };

  • Thank you for this suggestion. I will give it a go. This makes sense to me. Didn't know there was an issue with having only 1 entry in the pwm table. I appreciate your help with this.

Related