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);
        }
    }
}


/** @} */

Parents
  • 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.

  • 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

Reply Children
  • The pwm_library set the period in:

    app_pwm_config_t pwm1_cfg = APP_PWM_DEFAULT_CONFIG_2CH(5000L, BSP_LED_0, BSP_LED_1);

    5000L (L for long integer) is the period in µs. If this is set to 5000 this equals a frequency of 200hz.

    So if you want a certain frequency, you must set it to:

    APP_PWM_DEFAULT_CONFIG_2CH(XL, BSP_LED_0, BSP_LED1);

    where X = 1/desired frequency

  • thanks, I tried this. now I'm kind of confused. I thought the buzzer isn't loud is because the frequency.

    now, it's still not very loud even after I change the frequency.   

    https://www.mouser.ca/datasheet/2/670/cui%20inc._ccv-084b16-smt-1216976.pdf

    this the buzzer I'm currently using.

    /** @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 <stdbool.h>
    #include <stdint.h>
    #include "nrf.h"
    #include "app_error.h"
    #include "bsp.h"
    #include "nrf_delay.h"
    #include "app_pwm.h"
    
    APP_PWM_INSTANCE(PWM1,1);                   // Create the instance "PWM1" using TIMER1.
    
    static volatile bool ready_flag;            // A flag indicating PWM status.
    
    void pwm_ready_callback(uint32_t pwm_id)    // PWM callback function
    {
        ready_flag = true;
    }
    
    int main(void)
    {
        ret_code_t err_code;
    
        /* 1-channel PWM, 100000 microseconds period = 0.1 second , output on pin 28. */
        app_pwm_config_t pwm1_cfg = APP_PWM_DEFAULT_CONFIG_1CH(1000L, 28);
    
        /* Switch the polarity of the second channel. */
        pwm1_cfg.pin_polarity[1] = APP_PWM_POLARITY_ACTIVE_HIGH;
    
        /* Initialize and enable PWM. */
        err_code = app_pwm_init(&PWM1,&pwm1_cfg,pwm_ready_callback);
        APP_ERROR_CHECK(err_code);
        app_pwm_enable(&PWM1);
        
        //DUTY CYCLE SET TO 50%
        while (app_pwm_channel_duty_set(&PWM1, 0, 50) == NRF_ERROR_BUSY);
    
    
        while (true)
        {
    
        // Configure pin 5 as output
        nrf_gpio_cfg_output(4);
        // Set pin 5 high
        nrf_gpio_pin_set(4);
        nrf_delay_ms(1000);
        // Set pin 5 low
        nrf_gpio_pin_clear(4);
        nrf_delay_ms(1000);
    
        }
    
    }
    
    
    /** @} */
    this is the code I'm currently trying. 

    I also tested this sensor on Arduino, it can be very loud with 1kHz, I'm not sure what went wrong. 

    this is a very simple code I tried on Arduino.

    const int buzzer = 9; //buzzer to arduino pin 9
    
    
    void setup(){
     
      pinMode(buzzer, OUTPUT); // Set buzzer - pin 9 as an output
    
    }
    
    void loop(){
     
      tone(buzzer, 1000); // Send 1KHz sound signal...
      delay(1000);        // ...for 1 sec
      noTone(buzzer);     // Stop sound...
      delay(1000);        // ...for 1sec
       
    }

    will it because of the current?

Related