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.

Parents Reply Children
  • 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