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

  • 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.

Reply Children
No Data
Related