How to have a beep after another in a loop with PWM?

Hi,

I've implemented a buzzer service with the following code:

static nrf_pwm_values_common_t sequence_values[(int)(m_top/m_step)];

uint16_t value = 0;

//using the 0th PWM module
static nrfx_pwm_t m_pwm0 = NRFX_PWM_INSTANCE(0);

static void pwm_common_init(void)
{
 
  int max_steps = m_top/m_step;
  for(int i=0; i<max_steps; i++){
    
    //1st it: 0+100
    value += m_step;
    sequence_values[i] = value;
  }

  //configuration
  nrfx_pwm_config_t const config0 =
  {
    .output_pins =
    {
      BSP_LED_0 | NRFX_PWM_PIN_INVERTED,
      buzzer_external,
      NRFX_PWM_PIN_NOT_USED,
      NRFX_PWM_PIN_NOT_USED
    },

    .irq_priority = APP_IRQ_PRIORITY_LOWEST,
    .base_clock   = NRF_PWM_CLK_1MHz,
    .count_mode   = NRF_PWM_MODE_UP,
    .top_value    = m_top,
    .load_mode    = NRF_PWM_LOAD_COMMON,
    .step_mode    = NRF_PWM_STEP_AUTO
  };

  // clock = 1MHz
  // Counter is 15-bit
  // T_pwm(up) = T_pwm_clock*COUNTERTOP
  // Do = 532.251Hz
  // 1/532 = 1/1e6*COUNTERTOP
  // Countertop ~= 1880
  
  APP_ERROR_CHECK(nrfx_pwm_init(&m_pwm0, &config0, NULL));
}

static void pwm_play(void)
{
  nrf_pwm_sequence_t const seq0 = 
  {
    .values.p_common = sequence_values,
    .length          = NRF_PWM_VALUES_LENGTH(sequence_values),
    .repeats         = 0x00,
    .end_delay       = 0,
  };

    for(int i=0;i<20;i++){
      (void)nrfx_pwm_simple_playback(&m_pwm0, &seq0, 3, NRFX_PWM_FLAG_STOP);
      nrf_delay_ms(500);
      }
}


static void pwm_stop(void)
{
  (void)nrfx_pwm_stop(&m_pwm0, false);
}

m_top is 1880 such that I get a C note/tone at the buzzer.

I also use the following evt handler function:

static void buzzer_write_handler(ble_buzzer_service_t * p_buzzer_service, ble_buzzer_new_status_t * new_buzzer_status)
{
    
    buzzer_new_status_t new_status = new_buzzer_status->params_command.status_command_data;
   
    if (new_status.p_data[0])
    {
        pwm_play();
        NRF_LOG_INFO("Buzzing.");
    }
    else if (new_status.p_data[0] == 0x00)
    {
        pwm_stop();
        NRF_LOG_INFO("Buzzer is quiet.");
    }
    
}

The service is very simple, if I write a 0x01 to buzzer characteristic, then buzz. If I write a 0, then stop buzzing.

My problem is that, with this current implementation, the buzzing will not finish until the for-loop has gone through. The program doesn't care that I wrote a 0x00 to the characteristic, it still continues buzzing.

The only way I've been able to make it work (i.e., writing 0 to the characteristic turns off the buzzing) is to just place the ...simple_playback() function standalone, without any for-loop enclosing it, and using the NRFX_PWM_FLAG_LOOP flag.

However, in this way, I can only get a constant tone, a not beeps separated by silences like I do now.

My question is:

How can I implement a function that will make the buzzer sound forever (with spaced tones) that I will be able to stop by writing 0x00 to the characteristic?

Thanks in advance for the reply.

  • Hi Kenneth,

    Thanks for your answer.

    Setting the NRF_LOG_DEFERRED to 0 made the NRF_LOG_INFO() call work.

    But also, as I expected, it's basically printing the NRF_LOG_INFO("Buzzing! within pwm_play") calls within the while-loop. If I write 0x00 to the characteristic, the function pwm_stop() is not called, because the application is stuck in the while-loop of the pwm_play() function.

    It sounds to me that disabling the PWM won't work as the application insists on staying within the pwm_play() function.

    Perhaps I'm misunderstanding something fundamental and this is the actual expected behavior.

    If that's the case, do you care to suggest an alternative implementation to my code within the pwm_play function?

    //..
      while(1){
        NRF_LOG_INFO("Buzzing! within pwm_play");
        (void)nrfx_pwm_simple_playback(&m_pwm0, &seq0, 3, NRFX_PWM_FLAG_STOP);
        nrf_delay_ms(500);
      }
    //..

    As I said, I know I can work around it by removing everything but the ..._simple_playback() function and use the NRFX_PWM_FLAG_LOOP, but that plays a constant tone (no silences in between beeps).

    Best,

    Ernesto

  • I don't see how your while(1) will ever exit? You need some logic to exit that while loop when you want to stop the pwm.

    Kenneth

  • Hi,

    Based on your suggestion I attempted the following:

    static void buzzer_write_handler(ble_buzzer_service_t * p_buzzer_service, ble_buzzer_new_status_t * new_buzzer_status)
    {
        
        buzzer_new_status_t new_status = new_buzzer_status->params_command.status_command_data;
        //ble_buzzer_service_t * p_buzz_serv = p_buzzer_service;
        //uint8_t const * p_data
    
        nrf_pwm_sequence_t const seq0 = 
        {
          .values.p_common = sequence_values,
          .length          = NRF_PWM_VALUES_LENGTH(sequence_values),
          .repeats         = 0x00,
          .end_delay       = 0,
        };
    
        NRF_LOG_INFO("write handler");
        while(new_status.p_data[0] == 0x01)
        {
          NRF_LOG_INFO("Buzzing!");
          (void)nrfx_pwm_simple_playback(&m_pwm0, &seq0, 3, NRFX_PWM_FLAG_STOP);
          nrf_delay_ms(500);
          
          //read p_data gain
          //new_status = new_buzzer_status->params_command.status_command_data;
        }
        
        (void)nrfx_pwm_stop(&m_pwm0, false);
        NRF_LOG_INFO("Quiet!");
        
    }

    However, it doesn't work because, at least within the scope of the while-loop, the new_status.pdata[0] will not change.

    The question is: how can I make the application pass the new value I've written to the characteristic when it is inside this while-loop playing the PWM tone?

    I tried calling the buzzer_write_handler() again within the while-loop, but that didn't work either.

Related