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.

  • I don't quite understand your problem, but I could find someone else reporting something similiar, so I am pointing you to this case and hopefully that will also help you:  Pwm Stays on even when proper stop functions are called 

    Kenneth

  • Hi, I'm sorry I wasn't clear.

    I also don't understand how the forwarded ticket relates to my problem. I'm not using .values.p_individual = &m_demo1_seq_values but .p_common, so my sequence has a length > 1.

    Let me try to simplify the explanation of my problem:

    My aim is to implement the the pwm_play() function like this:

    static void pwm_play(void){
    
    //..
    
        for(int i=0;i<100;i++){
          (void)nrfx_pwm_simple_playback(&m_pwm0, &seq0, 3, NRFX_PWM_FLAG_STOP);
          nrf_delay_ms(500);
          }
    
    }

    Or, even better:

    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,
         };
    
        while(1){
            (void)nrfx_pwm_simple_playback(&m_pwm0, &seq0, 3, NRFX_PWM_FLAG_STOP);
            nrf_delay_ms(500);
        }
    }

    When I write to my buzzer characteristic, the buzzer_write_handler() function works properly by beeping and having pauses in between beeps due to the nrf_delay_ms() function. However, when I write 0x00 to the characteristic, the buzzer_write_handler() function doesn't work as it doesn't stop the beeping.

    In other words, this part of the handler function code:

    //..
        else if (new_status.p_data[0] == 0x00)
        {
            pwm_stop();
            NRF_LOG_INFO("Buzzer is quiet.");
        }

    is not executed, or is executed but doesn't stop the beeping. Somehow, the sever application gets stuck in the for or while loop. I write 0x00 to the characteristic, and even read it back as well, and it says is 0x00. So, I, at least, know that the characteristic is being properly written to.

    Is there a proper way to implement the pwm_play() function such that it can be properly shut off by the buzzer_write_handler() when I write 0x00 to it.

    As I said earlier, the only way I could make it work (i.e. beeping stops when writing 0x00 to the char.) is by taking out the ..._simple_playback() function out of any loop, and using the NRFX_PWM_FLAG_LOOP flag. Unfortunately, this is not desired because the LOOP flag will keep playing the tone without any pauses, as I do them using the nrf_delay_ms() function.

    Let me know if anything is still not clear.

    Best,

    Ernesto

  • Hi Ernesto,

    It's not clear to me where the code is hanging when you write "Somehow, the sever application gets stuck in the for or while loop. I write 0x00 to the characteristic", it should be possible to find where the code is hanging by debugging.

    If you just want to forcefully stop the PWM you can always disable it? E.g. by nrfx_pwm_uninit(). When disabled the pin used by PWM will fall back to the PIN_CNF[] for the pin in question.

    Alterntively you may look into disabling and enabling the PWM directly without using the nrfx_pwm driver. E.g. if you want to disable PWM, but still allow calling nrfx_pwm_simple_playback(), but calling it should have no effect, then you may simply consider calling:

    NRF_PWM0->ENABLE = 0; // stop PWM (calling nrfx_pwm_simple_playback() will have no effect)
    NRF_PWM1->ENABLE = 1; // start PWM (calling nrfx_pwm_simple_playback() will have an effect)

    Best regards,
    Kenneth

  • HI Kenneth,

    What I mean with "it's stuck in the for- or while-loop" is that it's stuck in the loop I implemented within the pwm_play() function. In other wors, the server remains beeping forever according to this while loop and it'll never stop, not even if I write 0x00 to the characteristic.

    Right now, I have it implemented like this:

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

    From debugging, all I see is that, after writing 0x01, the server starts buzzing, but and it goes to the breakpoint I set at the NRF_LOG_INFO() call. However, it never goes to the pwm_stop() function I implemented when I write 0x00.

    What I also find interesting is that the NRF_LOG_INFO doesn't print when running the debugging mode. Could it be because it needs a delay after the NRF_LOG_INFO() call so that it's able to print the text? I've tried 100ms and it still woudn't print.

    Best regards,

    Ernesto

  • It may be that you should change NRF_LOG_DEFERRED to 0 in sdk_config.h to ensure that it will print log statement directly when calling NRF_LOG() api instead of waiting for idle. Hopefully you should then be able to enable better debugging for you to find the problem.

    Kenneth

Related