I need to generate a clean 10kHz sine wave. I tried using PWM (https://devzone.nordicsemi.com/f/nordic-q-a/89209/clarification-on-how-to-generate-a-10khz-sine-wave-with-pwm-and-dma), and it works, but it's hard to filter the resulting signal. Given the 16MHz clock limit, and the need to have a 10kHz sine, I can only use 40 values and 40 points in the LUT.
One of the ideas was to to use PDM instead of PWM. the nRF52840 doesn't have PDM out, nor a way to stream a sequence of bits using DMA (as far as I can tell).
I thought that I could use a PWM with only 2 pulse (possible values 0, 1, and 2) and with that build any stream of bits. It would be expensive in terms of memory (every 2 bits is stored in a 16 bit array), but would work for a 800 point LUT.
So I tried this:
#include <stdio.h>
#include <string.h>
#include "nrf_drv_pwm.h"
#include "app_util_platform.h"
#include "app_error.h"
#include "boards.h"
#include "bsp.h"
#include "app_timer.h"
#include "nrf_drv_clock.h"
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"
static nrf_drv_pwm_t m_pwm = NRF_DRV_PWM_INSTANCE(0);
static void init_bsp()
{
APP_ERROR_CHECK(nrf_drv_clock_init());
nrf_drv_clock_lfclk_request(NULL);
APP_ERROR_CHECK(app_timer_init());
}
static nrf_pwm_values_common_t /*const*/ m_demo_seq_values[] =
{
1 | 0x8000,
1 | 0x8000,
1 | 0x8000,
1 | 0x8000,
1 | 0x8000,
1 | 0x8000,
1 | 0x8000,
1 | 0x8000,
1 | 0x8000,
1 | 0x8000,
1 | 0x8000,
1 | 0x8000,
1 | 0x8000,
1 | 0x8000,
1 | 0x8000,
1 | 0x8000,
1 | 0x8000,
1 | 0x8000,
1 | 0x8000,
1 | 0x8000,
1 | 0x8000,
1 | 0x8000,
1 | 0x8000,
1 | 0x8000,
1 | 0x8000,
1 | 0x8000,
1 | 0x8000,
1 | 0x8000,
};
static void demo(void)
{
static nrf_pwm_sequence_t const m_demo_seq =
{
.values.p_wave_form = &m_demo_seq_values,
.length = NRF_PWM_VALUES_LENGTH(m_demo_seq_values),
.repeats = 0,
.end_delay = 0
};
nrf_drv_pwm_config_t const config =
{
.output_pins =
{
ARDUINO_7_PIN,
NRFX_PWM_PIN_NOT_USED,
NRFX_PWM_PIN_NOT_USED,
NRFX_PWM_PIN_NOT_USED
},
.irq_priority = APP_IRQ_PRIORITY_LOWEST,
.base_clock = NRF_PWM_CLK_16MHz,
.count_mode = NRF_PWM_MODE_UP,
.top_value = 2,
.load_mode = NRF_PWM_LOAD_COMMON,
.step_mode = NRF_PWM_STEP_AUTO
};
APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm, &config, NULL));
(void)nrf_drv_pwm_simple_playback(&m_pwm, &m_demo_seq, 1,
NRF_DRV_PWM_FLAG_LOOP);
}
int main(void)
{
APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
NRF_LOG_DEFAULT_BACKENDS_INIT();
init_bsp();
demo();
for (;;)
{
// Wait for an event.
__WFE();
// Clear the event register.
__SEV();
__WFE();
NRF_LOG_FLUSH();
}
}
but I got very weird results. On my scope, I see a single pulse at a ~490Hz frequency

When I change the clock to 8MHz (.base_clock = NRF_PWM_CLK_16MHz), clean signal as expected

My question is: am I using the SDK functions wrongly? Is there a way to make a 16MHz, two pulses, PWM signal work? Did I hit a hardware limitation?