PWM frequency not being enforced by COUNTERTOP.

Following example in the nRF52810 product specification V1.3. The three LEDs are connected to PWM and waveform is configured in decoder. This means the first 3 values are used as COMP0, COMP1 and COMP2 respectively and the fourth index in the pwm_seq array i.e. CNT_TOP_MAX  is used as the counter top value.

The PWM period when running in up mode as per specs should be

T = Tpwmclk x COUNTERTOP

T = 1us x 19660 = 19.66ms

f = 50.8Hz

#define RISING_EDGE  0x0000
#define FALLING_EDGE 0x8000

#define PWM_POL RISING_EDGE

#define PWM_CH0_DUTY  ( 0x3FFE | PWM_POL )  /* COMP0 value 83.3% DC  */
#define PWM_CH1_DUTY  ( 0x3FFE | PWM_POL )  /* COMP1 value 83.3% DC  */
#define PWM_CH2_DUTY  ( 0x3FFE | PWM_POL )  /* COMP2 value 83.3% DC  */
#define CNT_TOP_MAX    0x4CCC               /* 0x7FFF   Max 15-bit counter value */

static void pwm_test()
{
    uint16_t pwm_seq[4] = {PWM_CH0_DUTY, PWM_CH1_DUTY, PWM_CH2_DUTY, CNT_TOP_MAX};
    NRF_PWM0->PSEL.OUT[0] = (PIN_LED_RED << PWM_PSEL_OUT_PIN_Pos) | (PWM_PSEL_OUT_CONNECT_Connected << PWM_PSEL_OUT_CONNECT_Pos);
    NRF_PWM0->PSEL.OUT[1] = (PIN_LED_GREEN << PWM_PSEL_OUT_PIN_Pos) | (PWM_PSEL_OUT_CONNECT_Connected << PWM_PSEL_OUT_CONNECT_Pos);
    NRF_PWM0->PSEL.OUT[2] = (PIN_LED_BLUE << PWM_PSEL_OUT_PIN_Pos) | (PWM_PSEL_OUT_CONNECT_Connected << PWM_PSEL_OUT_CONNECT_Pos);
    NRF_PWM0->ENABLE = (PWM_ENABLE_ENABLE_Enabled << PWM_ENABLE_ENABLE_Pos);
    NRF_PWM0->MODE = (PWM_MODE_UPDOWN_Up << PWM_MODE_UPDOWN_Pos);
    NRF_PWM0->PRESCALER = (PWM_PRESCALER_PRESCALER_DIV_16 << PWM_PRESCALER_PRESCALER_Pos); // 
    //NRF_PWM0->COUNTERTOP = (CNT_TOP_MAX << PWM_COUNTERTOP_COUNTERTOP_Pos); //1 msec
    NRF_PWM0->LOOP = (PWM_LOOP_CNT_Disabled << PWM_LOOP_CNT_Pos);
    NRF_PWM0->DECODER = (PWM_DECODER_LOAD_WaveForm << PWM_DECODER_LOAD_Pos) | (PWM_DECODER_MODE_RefreshCount << PWM_DECODER_MODE_Pos);
    NRF_PWM0->SEQ[0].PTR = ((uint32_t)(pwm_seq) << PWM_SEQ_PTR_PTR_Pos);
    NRF_PWM0->SEQ[0].CNT = ((sizeof(pwm_seq) / sizeof(uint16_t)) << PWM_SEQ_CNT_CNT_Pos);
    NRF_PWM0->SEQ[0].REFRESH = 0;
    NRF_PWM0->SEQ[0].ENDDELAY = 0;
    NRF_PWM0->TASKS_SEQSTART[0] = 1;
}

I can see through the logic analyzer traces that after (0x3FFE) counts @ 1us the OUT signal goes low. Based on COUNTERTOP setting after 0x4CCC counts the OUT should go back high.

This is not actually what happens. After 16.382ms of high pulse the OUT keep low until the 15-bit counter top completely overflows (0x7FFF) before going high again for 16.382ms.

  • Hello,

    The PWM peripheral is quite complex. I would suggest that you at least peek through and experiment with the demos in the SDK\examples\peripheral\pwm_driver example, or alternatively the pwm_library example, depending on what type of PWM signal you need (blinky pattern or motor control). 

    I see that you commented out the line setting the COUNTERTOP register. Is that on purpose? Does the counter stop(and reset) when you hit 0x4CCC if you uncomment line 20 in your snippet?

    Best regards,

    Edvin

  • @Edvin the PWM is configured in waveform mode we do not need set countertop separately. This is due to the fact COUTERTOP is actually the 4th index of pwm_seq array.

    Even if I uncomment it, nothing happens. 

  • I see. I wasn't aware of the waveform option. 

    I tested your snippet now, and this is what I see on a logic trace:

    Note the values in the screenshot.

    Is that not what you see? If not, can you please try to zip your application project and upload it here, so that I can have a look and try to reproduce it?

    Best regards,

    Edvin

  • This line is incorrect, as it is the setting for (say) a single channel in non-waveform mode:

    NRF_PWM0->SEQ[0].CNT = ((sizeof(pwm_seq) / sizeof(uint16_t)) << PWM_SEQ_CNT_CNT_Pos);

    For waveform mode, as you noted above each "step" has 4 entries, not 1:

    NRF_PWM0->SEQ[0].CNT = ((sizeof(pwm_seq) / (4*sizeof(uint16_t))) << PWM_SEQ_CNT_CNT_Pos);

    There is an issue setting LOOP to 0, if I recall correctly:

    Try changing
        NRF_PWM0->LOOP = (PWM_LOOP_CNT_Disabled << PWM_LOOP_CNT_Pos);
    to
        NRF_PWM0->LOOP = 1; // 1 or more ..

    I also remember always setting both SEQ 0 and 1 to the same values when only using a single sequence, but that maybe have been an older device.

    You might also find this useful to end the sequence:

      NRF_PWM0->SHORTS = NRF_PWM_SHORT_LOOPSDONE_STOP_MASK;

Related