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?