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

pwm for speaker

Hi

I'm using this simple_pwm code to drive my speaker, I use this as a buzzer but I want to change the frequency.

In this code ".base_clock   = NRF_PWM_CLK_125kHz", the clock can only go as low as 125kHz, I want to have like 2kHz frequency. 

I don't know how I can achieve it, please correct me if I was wrong.

thanks,

Cindy

/**
 * Copyright (c) 2015 - 2017, Nordic Semiconductor ASA
 * 
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form, except as embedded into a Nordic
 *    Semiconductor ASA integrated circuit in a product or a software update for
 *    such product, must reproduce the above copyright notice, this list of
 *    conditions and the following disclaimer in the documentation and/or other
 *    materials provided with the distribution.
 * 
 * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 * 
 * 4. This software, with or without modification, must only be used with a
 *    Nordic Semiconductor ASA integrated circuit.
 * 
 * 5. Any software provided in binary form under this license must not be reverse
 *    engineered, decompiled, modified and/or disassembled.
 * 
 * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 */
/** @file
 * @defgroup pwm_example_main main.c
 * @{
 * @ingroup pwm_example
 *
 * @brief PWM Example Application main file.
 *
 * This file contains the source code for a sample application using PWM.
 */

#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 "nrf_drv_clock.h"
#include "nrf_delay.h"


#define OUTPUT_PIN 8

static nrf_drv_pwm_t m_pwm0 = NRF_DRV_PWM_INSTANCE(0);

// Declare variables holding PWM sequence values. In this example only one channel is used 
nrf_pwm_values_individual_t seq_values[] = {0, 0, 0, 0};
nrf_pwm_sequence_t const seq =
{
    .values.p_individual = seq_values,
    .length          = NRF_PWM_VALUES_LENGTH(seq_values),
    .repeats         = 0,
    .end_delay       = 0
};


// Set duty cycle between 0 and 100%
void pwm_update_duty_cycle(uint8_t duty_cycle)
{
    duty_cycle = 70;
    // Check if value is outside of range. If so, set to 100%
    if(duty_cycle >= 100)
    {
        seq_values->channel_0 = 100;
    }
    else
    {
        seq_values->channel_0 = duty_cycle;
    }
    
    nrf_drv_pwm_simple_playback(&m_pwm0, &seq, 1, NRF_DRV_PWM_FLAG_LOOP);
}

static void pwm_init(void)
{
    nrf_drv_pwm_config_t const config0 =
    {
        .output_pins =
        {
            OUTPUT_PIN, // channel 0
            NRF_DRV_PWM_PIN_NOT_USED,             // channel 1
            NRF_DRV_PWM_PIN_NOT_USED,             // channel 2
            NRF_DRV_PWM_PIN_NOT_USED,             // channel 3
        },
        .irq_priority = APP_IRQ_PRIORITY_LOWEST,
        .base_clock   = NRF_PWM_CLK_125kHz,
        .count_mode   = NRF_PWM_MODE_UP,
        .top_value    = 100,
        .load_mode    = NRF_PWM_LOAD_INDIVIDUAL,
        .step_mode    = NRF_PWM_STEP_AUTO
    };
    // Init PWM without error handler
    APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0, &config0, NULL));
    
}


int main(void)
{

    // Start clock for accurate frequencies
    NRF_CLOCK->TASKS_HFCLKSTART = 1; 
    // Wait for clock to start
    while(NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) 
        ;
    
    pwm_init();

    for (;;)
    {
        for(int i = 0; i <= 100; i++)
        {
            nrf_delay_ms(10);
            pwm_update_duty_cycle(i);
        }
    }
}


/** @} */

  • Hello Cindy,

    So you want to control a buzzer with different frequencies, is that so? Please note that PWM is originally made for changing the pulse width, and not the frequency (Pulse Width Modulation). If you want to use PWM and change the frequency, I believe you need to uninit and initialize the PWM to change the frequency. 

    However, on the thingy:52 we use PWM to generate sound, but please note that this is used a bit differently. In the Thingy FW the PWM is used to generate the output voltage in a sound wave. 

    I don't know the complexity of the sound you want to play. Is it simple notes? Or are you going to play a recording? If it is simple notes, I believe you can just uninit and init the PWM to change the frequency, and always use 50% duty cycle. If you are going to play recorded sound you probably have to dig deeper into the PWM library. 

    Either way, I suggest you start by looking at the pwm_library (not driver) example. Actually, if you want to change the frequency, you may be better off using a PPI + TIMER + GPIOTE solution. Please check out the project I uploaded in this post. If you start there, you can change the reload register instead of the "PWM toggle" register to change the number of ticks required for the timer to wrap around (reload). If that is interesting, please check out the project I uploaded in the last reply in this ticket.

  • Hi Cindy,

    in your example, you can set load_mode = NRF_PWM_LOAD_WAVE_FORM. Then the last value of each sequence step (channel_3) will update COUNTERTOP register, changing PWM frequency (Fout = base_clock / COUNTERTOP)

  • Hi, does this mean I need to use channel_3 instead? how can I set COUNTERTOP, would you mind explain it a bit more?

  • I just to make simple notes, because 125kHz is a really high frequency, it's a very uncomfortable sound. I want to lower it. pwm_library doesn't have any function I can change the frequency. I'm not familiar with PPI, is there any simple solution for my problem?

    Thanks,

    Cindy

  • when mode is set to NRF_PWM_LOAD_WAVE_FORM, the value from channel_3 goes to COUNTERTOP, and .top_value is ignored. Just set channel_3 to (base_clock/Fout), channel_0 to (channel_3*duty_cycle)/100.

Related