Main Site Documentation

RETRO PWM/Piezo and PwmOut.period


#1

Playing around with making some modifications to the default RETRO firmware to add some more sounds, and I’m seeing some odd behavior.

If I set the period of the PWM object using either period_ms or period_us, it seems to work fine. If I try to set the period using just period (which takes a float argument), I get an initial click sound, then nothing. I’ve tried using the same frequency across all three methods, and only period(float) isn’t working.

For example, I modified the checkLives function to play a sound when the game is over:

void Game::checkLives() {
    if (this->lives == 0) {
        this->disp.clear();
                
        this->drawString(Game::LOSE_1, DisplayN18::HEIGHT / 2 - DisplayN18::CHAR_HEIGHT); 
        this->drawString(Game::LOSE_2, DisplayN18::HEIGHT / 2);  
        
        this->pwm.period_us(4545);
        this->pwm.write(0.5);
        wait(1);
        this->pwm.write(0.0);
        
        while (this->circle.read())
            wait_ms(1);
            
        this->initialize();
    }
    else {
        this->disp.drawCharacter(0, 0, static_cast<char>(this->lives + '0'), DisplayN18::WHITE, DisplayN18::BLACK);   
    }
}

The code above works fine. But if I change this->pwm.period_us(4545) to this->pwm.period(1/220), no sound. The two should be equivalent, however.

Stranger still, if I use the following program (from one of the mbed PDF docs on PWM) as a standalone program, period(float) works fine:

// Oranges and Lemons program 

 #include "mbed.h"   
PwmOut buzzer(P0_18); 
float frequency[]={659,554,659,554,550,494,554,587,494,659,554,440}; 
//frequency array 
float beat[]={1,1,1,1,1,0.5,0.5,1,1,1,1,2}; 
//beat array 

int main() {     
    while (1) {
        for (int i=0; i<=11; i++) {
            buzzer.period(1/(frequency[i]));    
            // set PWM period             
            buzzer=0.5;
            // set duty cycle
            wait(0.5*beat[i]);
            // hold for beat period
        }
    }
}

My assumption is that I must be doing something wrong, but I’m not sure what. Am I missing something obvious here?


#2

Did you try
this->pwm.period(0.00455);

or even
this->pwm.period(1.0/220);

1/220 is taken as being (int) / (int) by the compiler and so returns an integer. This is then converted to a float of 0.00000 when passed to the period function.

1.0/220 is (float)/(int) which will return a float and so should work correctly.


#3

@ AndyA - OK, that makes sense, since I’m pretty sure I only tried multiples of 440. Will try the operation using 1.0.


#4

@ AndyA - Thanks, that did the trick.


#5

I’ve been bitten with this before.


#6

The only reason I spotted it is the large number of hours I’ve lost tracking down bugs caused by this in my own code.

One minor performance issue, in the c standard 1.0 is technically a double so 1.0/220 would return a double that is then converted to a float when passed to the function. In this situation where everything is constant that shouldn’t have any performance impact since the value should be calculated at compile time.

However if you had:

setPeriod(float frequency) {
pwm.period(1.0/frequency);
}

It would calculate 1.0/frequency as a double before converting to a float.

In that situation if you were being paranoid about performance you would want to do 1/frequency or 1.0f/frequency in order to ensure that the calculation was done as a float rather than a double.