Not able to generate required sinewave using PWM and TIMER

I am using nRF5415-DK and SDK v3.2.2

I want to generate a sine wave using PWM and TIMER. The PWM repeat duty cycle method has limitations, so I am using a timer to trigger the PWM at the required rate.TIMER compare event is connected to the NEXTSTEP task in PWM. 

The required sine wave frequency is 1.5kHz with 9-bit resolution and 128 duty cycle values.

I am not able to generate the required frequency. I can only generate upto 250Hz.

#define SINE_RES_BITS        (9U)
#define SINE_SAMPLES_COUNT   (128U)
#define PWM_TOP_VAL          (1 << SINE_RES_BITS)   // 512

const float sineLookupTable[SINE_SAMPLES_COUNT] = {
    0.0000, 0.5025, 1.0037, 1.5025, 1.9977, 2.4881, 2.9725, 3.4498, 3.9187, 4.3782, 4.8271, 5.2644, 5.6890, 6.1000, 6.4962, 6.8768, 7.2408, 7.5873, 7.9156, 8.2248,
    8.5142, 8.7831, 9.0309, 9.2569, 9.4605, 9.6414, 9.7991, 9.9331, 10.0432, 10.1292, 10.1907, 10.2277, 10.2400, 10.2277, 10.1907, 10.1292, 10.0432, 9.9331, 9.7991, 9.6414,
    9.4605, 9.2569, 9.0309, 8.7831, 8.5142, 8.2248, 7.9156, 7.5873, 7.2408, 6.8768, 6.4962, 6.1000, 5.6890, 5.2644, 4.8271, 4.3782, 3.9187, 3.4498, 2.9725, 2.4881,
    1.9977, 1.5025, 1.0037, 0.5025, 0.0000, -0.5025, -1.0037, -1.5025, -1.9977, -2.4881, -2.9725, -3.4498, -3.9187, -4.3782, -4.8271, -5.2644, -5.6890, -6.1000, -6.4962, -6.8768,
    -7.2408, -7.5873, -7.9156, -8.2248, -8.5142, -8.7831, -9.0309, -9.2569, -9.4605, -9.6414, -9.7991, -9.9331, -10.0432, -10.1292, -10.1907, -10.2277, -10.2400, -10.2277, -10.1907, -10.1292,
    -10.0432, -9.9331, -9.7991, -9.6414, -9.4605, -9.2569, -9.0309, -8.7831, -8.5142, -8.2248, -7.9156, -7.5873, -7.2408, -6.8768, -6.4962, -6.1000, -5.6890, -5.2644, -4.8271, -4.3782,
    -3.9187, -3.4498, -2.9725, -2.4881, -1.9977, -1.5025, -1.0037, -0.5025
};

#define SINEBASELINE (PWM_TOP_VAL / 2)

static nrfx_pwm_t   m_pwm_ch1   = NRFX_PWM_INSTANCE(NRF_PWM_INST_GET(20));

static nrfx_timer_t m_timer_ch1 = NRFX_TIMER_INSTANCE(NRF_TIMER20);

static nrf_pwm_values_common_t sine_table[SINE_SAMPLES_COUNT];

static void Scale_Up_BufferF(const float bufferSource[], nrf_pwm_values_common_t bufferResult[], uint8_t val)
{
    uint16_t i;

    for (i = 0U; i < SINE_SAMPLES_COUNT; i++) {
        bufferResult[i] = SINEBASELINE+round(bufferSource[i] * val);
    }
}

void sinegen_startTrigger(uint32_t pin_no, uint32_t target_hz)
{
    if (target_hz == 0) return;

    Scale_Up_BufferF(sineLookupTable, sine_table, 10);
    nrfx_pwm_config_t config = NRFX_PWM_DEFAULT_CONFIG(pin_no, 
                                                      NRF_PWM_PIN_NOT_CONNECTED,
                                                      NRF_PWM_PIN_NOT_CONNECTED,
                                                      NRF_PWM_PIN_NOT_CONNECTED);
    config.top_value  = PWM_TOP_VAL;
    config.base_clock = NRF_PWM_CLK_16MHz;
    config.load_mode  = NRF_PWM_LOAD_COMMON;
    config.count_mode = NRF_PWM_MODE_UP;
    config.step_mode  = NRF_PWM_STEP_TRIGGERED;

    nrfx_pwm_init(&m_pwm_ch1, &config, NULL, NULL);

    /* ---------------- TIMER CONFIG ---------------- */

    uint32_t sample_rate = target_hz * SINE_SAMPLES_COUNT;

    nrfx_timer_config_t timer_cfg =NRFX_TIMER_DEFAULT_CONFIG(16000000);

    nrfx_timer_init(&m_timer_ch1, &timer_cfg, NULL);

    uint32_t ticks = 16000000U / sample_rate;
    // uint32_t ticks = nrfx_timer_us_to_ticks(&m_timer_ch1,4);
    nrfx_timer_extended_compare(&m_timer_ch1, NRF_TIMER_CC_CHANNEL0, ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,false);
    
    /* ---------------- PPI CONFIG ---------------- */
    nrfx_err_t err;
    /* Declare variables used to hold the (D)PPI channel number */
    nrfx_gppi_handle_t gppi_handle_update;

    /* Trigger task to update the duty cycle */
    err = nrfx_gppi_conn_alloc( nrfx_timer_compare_event_address_get(&m_timer_ch1, NRF_TIMER_CC_CHANNEL0),nrfx_pwm_task_address_get(&m_pwm_ch1, NRF_PWM_TASK_NEXTSTEP), &gppi_handle_update);
    if (err != 0) {
        // LOG_ERR("nrfx_gppi_conn_alloc error: %08x", err);
        return;
    }

    /* Enable (D)PPI channel */ 
    nrfx_gppi_conn_enable(gppi_handle_update);

    
    /* ---------------- START PWM PLAYBACK ---------------- */

    nrf_pwm_sequence_t const seq =
    {
        .values.p_common = sine_table,
        .length          = SINE_SAMPLES_COUNT,
        .repeats         = 0,
        .end_delay       = 0
    };

    nrfx_pwm_simple_playback(&m_pwm_ch1, &seq, 1, NRFX_PWM_FLAG_LOOP);

    nrfx_timer_enable(&m_timer_ch1);

}

  • Read the datasheet chapter about the PWM peripherial again. You are trying do do things that simply won't work.

    The (max) base frequency is 16MHz, and using 9 Bits requires 512 steps. That yields a max PWM refresh frequency of 16Mhz/512 = 31,250Hz. 

    But 1.5Khz with 128 cycle values would require a refresh rate of 192,000Hz - far above hardware specs.

    My recommendation: Ditch the TIMER altogether, use PWM auto refresh and calculate your 1.5Khz sine wave points using 31,250Hz base frequency. You may need to be a bit clever in calculation buffer length here, too - I don't think those frequencies match neatly.

Related