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.

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

  • 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

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

Reply Children
No Data
Related