This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Generating a sweeping tone (2.5KHz to 4.5KHZ at 4Hz rate) from GPIO.

HI,

In need to generate a sweeping tone to drive a piezzo element, i determined the tone should be sweeping back and forth between 2.5KHz and 4.5KHZ at an approximative rate of 4Hz, what is the best approach to achieve this? i am already using hardware PWM for another part of the system (to drive a MOS) but it doesnt seem very compatible with such use case since it has to be reconfigured each time the frequency is modified.

Thanks

  • Hi,

    You can use the PWM peripheral's Waveform Mode and EasyDMA to achieve this. From the documentation:

    "A special mode of operation is available when DECODER.LOAD is set to WaveForm. In this mode, up to three PWM channels can be enabled - OUT[0] to OUT[2]. In RAM, four values are loaded at a time: the first, second and third location are used to load the values, and the fourth RAM location is used to load the COUNTERTOP register. This way one can have up to three PWM channels with a frequency base that changes on a per PWM period basis. This mode of operation is useful for arbitrary wave form generation in applications such as LED lighting."

    By configuring the waveform correctly you can make frequency sweeps like this and have it run autonomously:

    Here is some code to get you started (SDK 15.2.0).

    #include <stdio.h>
    #include <string.h>
    #include "nrf_drv_pwm.h"
    #include "app_util_platform.h"
    #include "app_error.h"
    #include "boards.h"
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    #define WAVE_FORM_LENGTH 8
    
    // Declare a PWM driver instance structure
    nrfx_pwm_t m_pwm0 = NRFX_PWM_INSTANCE(0);
    
    // Function for initializing the PWM
    void init_pwm(void)
    {
        uint32_t err_code;
        // Declare a configuration structure and use a macro to instantiate it with default parameters.
        nrfx_pwm_config_t pwm_config = NRFX_PWM_DEFAULT_CONFIG;
    
        // We must override some of the parameters:
        pwm_config.output_pins[0] = LED_1; // Connect LED_1 on the nRF52832 DK to PWM Channel 0
        pwm_config.load_mode    = NRF_PWM_LOAD_WAVE_FORM; // Use individual duty cycle for each PWM channel
        pwm_config.base_clock   = NRF_PWM_CLK_1MHz;
        
        // Pass config structure into driver init() function 
        err_code = nrfx_pwm_init(&m_pwm0, &pwm_config, NULL);
        APP_ERROR_CHECK(err_code);
    }
    
    
    
    // Structure holding duty cycle and counter top values
    static nrf_pwm_values_wave_form_t pwm_wave_form_values[WAVE_FORM_LENGTH];
    
    // Genreate PWM values 
    static void generate_pwm_wave_form(void)
    {
        uint16_t counter_top = 100;
        
        for(int i=0; i<WAVE_FORM_LENGTH; i++)
        {
            pwm_wave_form_values[i].counter_top = counter_top;
            pwm_wave_form_values[i].channel_0 = counter_top / 2;
            counter_top = counter_top * 2;
        }
    }
    
    // Structure for defining a sequence of PWM duty cycles
    static nrf_pwm_sequence_t pwm_sequence =
    {
        .values.p_wave_form = pwm_wave_form_values,
        .length          = (sizeof(pwm_wave_form_values) / sizeof(uint16_t)),
        .repeats         = 1,
        .end_delay       = 0
    };
    
    int main(void)
    {
        APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
        NRF_LOG_DEFAULT_BACKENDS_INIT();
        NRF_LOG_INFO("PWM example started.");
        
        // Start HF Crystal to generate accurate clock frequency for PWM
        NRF_CLOCK->TASKS_HFCLKSTART = 1;
        while(NRF_CLOCK->EVENTS_HFCLKSTARTED == 0)
        {
            ;
        }
    
        init_pwm();
        generate_pwm_wave_form();
        nrfx_pwm_simple_playback(&m_pwm0, &pwm_sequence, 1, NRFX_PWM_FLAG_LOOP);
    
        for (;;)
        {
            // Wait for an event.
            __WFE();
        }
    }
    
    

Related