Tone(): different behaviour between debug and release

Hi all,

I have a bizarre behaviour for a program running on a STM32F103: I’m using tone() to generate a notification on a buzzer and it works fine when I run the program in debug mode, but fails to generate any sounds in release.

The platformio.ini is this one:

[env:genericSTM32F103CB]
platform = ststm32
board = genericSTM32F103CB
framework = arduino
board_build.core = maple
lib_deps = robotis-git/Dynamixel2Arduino
upload_protocol = stlink
debug_tool = stlink
upload_flags = -c set CPUTAPID 0x2ba01477
debug_server = 
	/Users/Alex/.platformio/packages/tool-openocd/bin/openocd
	-s /Users/Alex/.platformio/packages/tool-openocd/scripts
	-f interface/stlink.cfg
	-c "transport select hla_swd"
	-c "set CPUTAPID 0x2ba01477"
	-f target/stm32f1x.cfg
	-c "reset_config none"

(I’m using a breadboarded blue pill with a Chinese clone for prototyping, hence the CPUTAPID usage.)

The code is basically a battery voltage indicator: reads a voltage on PA6 and compares it with a number of thresholds to light 4 LEDs indicating the degree of charge. Under a certain threshold the LED 1 will blink and a buzzer will sound with a 0.5s cycle (0.5s on, 0.5s off).

the loop() looks like this:

void loop()
{
  ...
  // compute state
  raw = analogRead(PA6);
  volt_sum += raw;
  num_samples++;
  if (num_samples == 10) {
    compute_state(volt_sum/10);
    display_state();
    volt_sum = 0;
    num_samples = 0;
  }
  ...
}

The compute_state() is not that interesting, it just computes a state based on the raw voltage and the thresholds, taking into account a hysteresis effect and puts a global variable v_state into indicating 4 LEDs, 3 LEDs, 2 LEDs, 1 LED or 1 LED blinking.

The display_state() looks like this:

void display_state()
{
  switch (v_state)
  {
  case STATE4:
    digitalWrite(PB5, LOW);
    digitalWrite(PB6, LOW);
    digitalWrite(PB7, LOW);
    digitalWrite(PB8, LOW);
    noTone(PB9);
    break;
  
  case STATE3:
    digitalWrite(PB5, HIGH);
    digitalWrite(PB6, LOW);
    digitalWrite(PB7, LOW);
    digitalWrite(PB8, LOW);
    noTone(PB9);
    break;

  case STATE2:
    digitalWrite(PB5, HIGH);
    digitalWrite(PB6, HIGH);
    digitalWrite(PB7, LOW);
    digitalWrite(PB8, LOW);
    noTone(PB9);
    break;

  case STATE1:
    digitalWrite(PB5, HIGH);
    digitalWrite(PB6, HIGH);
    digitalWrite(PB7, HIGH);
    digitalWrite(PB8, LOW);
    noTone(PB9);
    break;

  case STATE1B:
    digitalWrite(PB5, HIGH);
    digitalWrite(PB6, HIGH);
    digitalWrite(PB7, HIGH);
    if ((millis() % 1000) < 500) {
      digitalWrite(PB8, LOW);
      tone(PB9, 1000);
    }
    else {
      digitalWrite(PB8, HIGH);
      noTone(PB9);
    }
    break;

  default:
    break;
  }
}

As I mentioned at the beginning of the note, if the program is loaded and runs in release mode the LEDs behaviour is fine, including the blinking, but no sound is heard in the buzzer. If I run the code in debug over the stlink I can hear the buzzer working properly.

Any idea where this might be coming from, or how it can be fixed?

Thanks.

Does the release mode work when you add

build_unflags = -Os
build_flags = -Og

to the platformio.ini?

Yes, if I add those two lines in the platformio.ini and re-flash the release version, I get the buzzer working.

Somehow the optimization level is killing the logic responsible for the buzzer…

When you replace this with -O0, -O1, -O2 and -O3, which of these work and which don’t?

You should also open a CLI, execute pio platform update ststm32 and re-check if it works now without the addition of any build_unflags and build_flags.

  • -O0 - works
  • -O1 - works
  • -O2 - doesn’t work
  • -O3 - doesn’t work

Also interesting is that -Og and -O1 produce a higher pitch sound that the -O0.

That doesn’t seem to change the behaviour (if the build flags are removed the buzzer stops working).

Is your sketch compatible with the regular STM32Duino core too (remove this line to change to STM32Duino)?

Can you create a minimal sketch that only does tone(PB9, 1000); and check its behavior on the Maple and STM32Duino core?

This is not good, that shows that the tone is dependent on the execution speed / optimization level of the tone() function. tone() should be using a timer to generate its PWM output, the timers should be setup in the same way and then operate only based on the system’s clock frequency, which is the same for both optimized and non-optimized code. Unless you’re aborting tone() with notone() extremely often per second and restart it.

I’m using maple because of differences in the EEPROM library. The code uses that to store persistent configuration parameters for the device. I would need to change the code to use the EEPROM from core library, but that is something that I might have to do at one point.

This is a good idea and I will try to do this today.

Could it be that under debug tone() does not use TIM, but bit-bangs the pins?

No, it all goes through a timer in Maple (source).

There is indeed a difference between the two frameworks:

platformio.ini:

[env:genericSTM32F103CB]
platform = ststm32
board = genericSTM32F103CB
framework = arduino
; board_build.core = maple
upload_flags = -c set CPUTAPID 0x2ba01477

code:

#include <Arduino.h>

void setup() {
    // put your setup code here, to run once:
    pinMode(PB8, OUTPUT); // LED 4 LSB
    pinMode(PB9, OUTPUT); // buzzer
    delay(1000);
}

void loop() {
    // put your main code here, to run repeatedly:
    if ((millis() % 1000) < 500) {
        digitalWrite(PB8, LOW);
        tone(PB9, 500);
    }
    else {
        digitalWrite(PB8, HIGH);
        noTone(PB9);
    }
}

If I load the core build the buzzer is working (although in all fairness there is a little bit of “gurgling” in the sense that the sound seems to be interrupted randomly from time to time).

If I use the maple build there is no sound.

I will try to revist the code and change to support the EEPROM from the core framework and see how it works.

The “gurgling” is due to my poor code. I’ve changed the code and now the sound is clear:

#include <Arduino.h>

bool blink = false;

void setup() {
    // put your setup code here, to run once:
    pinMode(PB8, OUTPUT); // LED 4 LSB
    pinMode(PB9, OUTPUT); // buzzer
    delay(1000);
}

void loop() {
    // put your main code here, to run repeatedly:
    if ((millis() % 1000) < 500 && !blink) {
        digitalWrite(PB8, LOW);
        tone(PB9, 500);
        blink = true;
    }
    if ((millis() % 1000) >= 500 && blink) {
        digitalWrite(PB8, HIGH);
        noTone(PB9);
        blink = false;
    }
}

Try your Maple configuration again but add

platform_packages = 
   framework-arduinoststm32-maple@https://github.com/maxgerhardt/Arduino_STM32.git

into the platformio.ini to select an updated version of the framework. Does it work now under Maple?

That seemed to work. I have removed the build_flags in the platformio.ini and rebuild and flashed and now I can hear the buzzer.

Thanks for the help.

Then PlatformIO needs to update its Maple core package, I will open an issue for that. General problem being though that GitHub - rogerclarkmelbourne/Arduino_STM32: Arduino STM32. Hardware files to support STM32 boards, on Arduino IDE 1.8.x including LeafLabs Maple and other generic STM32F103 boards has not had a realease for 3 years although plenty of commits have happened since then…

You’d be right and more future proof to adapt your firmware to work with the now standard GitHub - stm32duino/Arduino_Core_STM32: STM32 core support for Arduino core though.

This is now tracked in Update Maple core package · Issue #592 · platformio/platform-ststm32 · GitHub.