Confused why analogWrite blocks Bounce2::button from being responsive any longer

Meshtron

Member
I put up a gist here: https://gist.github.com/MikahB/67d3e00dac024b8a83895423a229e1be

I'm working my way (relatively slowly) towards a user-configurable input (in the form of mechanical buttons and voltage-generating things) to output (in the form of PWM DC output drivers) control system. I've made myself a list of bits of functionality to tick off one by one, so the overall structure is a bit of a mess, but still very much in the "make this step work" phase.

I've run into something that I can't quite get my head around. In the gist, I've commented out (lines 46, 50, and 54) three `analogWrite` instructions that simply drive the onboard LED at different duty cycles. Because Pin13 is an available PWM pin, I expect that should be fine. But, if I run any `analogWrite` on Pin13, I never get another response from my Bounce2 button objects - they never see any state switching events after that point. But with those 3 lines cancelled out, things work exactly like I'd expect.

I'm struggling to understand what could be the possible relationship between PWM on Pin13 and never getting high/low on Pin2 (my button pin for now) again. Is the program just stopping as soon as I do an analog write? If so, why? Is the program NOT stopping but somehow the button object becomes decoupled from the pin it's attached to?

Appreciate any thoughts and guidance!

Edit1: Added inline code also
Edit2: It seems that the problem, more specifically, is mixing `digitalWrite` and `analogWrite` on the same Pin13. If I convert the "off" state to `analogWrite(13,0)`, then the other analog writes work fine, but none of the Blinks work (which are all `digitalWrite`)

Code:
#include <Arduino.h>
#include <Bounce2.h>
#include <EEPROM.h>

// input states
enum inputState{off, low, medium, high, reset};

// input actions
enum inputAction {shortPress, longPress};

// Pin 13 has an onboard LED on the Teensy 4.1
int outputPins[] = {13};
int inputPins[] = {2};
constexpr int bounceTime = 20;
constexpr u_int16_t INPUT0_LASTVAL = 1000;

Button buttons[1];
inputState CurrentInputState;

// this indicates what mode we're starting in
// todo: non-blocking
void Blink(int numBlinks, int timeVal = 100) {
  if (numBlinks <= 0) {return;}
  for (int i = 0; i < numBlinks; i++) {
    digitalWrite(13, LOW);
    delay(10);
    digitalWrite(13, HIGH);
    delay(timeVal);
    digitalWrite(13, LOW);
    delay(timeVal);
  }
}

// this actually sets the output
// note: need to fix terminology between input and output states
void SetOutputState(inputState newState, int outputNumber = 0) {
  CurrentInputState = newState;
  switch (newState)
  {
    case off:
      digitalWrite(outputPins[outputNumber], LOW);
      return; // explicitly, without changing last state
      break;
    case low:
      Blink(1);
      //analogWrite(outputPins[outputNumber], 128);
      break;
    case medium:
      Blink(2);
      analogWrite(outputPins[outputNumber], 192);
      break;
    case high:
      Blink(3);
      //analogWrite(outputPins[outputNumber], 256);
      break;
  }
  // if we didn't turn it off, store this value for later
  EEPROM.write(INPUT0_LASTVAL, newState);
}

// this runs the state machine
void UpdateInputState(inputAction action) {
  // if it's off, just turn on to previous setting
  if (CurrentInputState == off) {
    inputState lastSetting = static_cast<inputState>(EEPROM.read(INPUT0_LASTVAL));
    SetOutputState(lastSetting);
  }
  // if it's NOT off, figure out what to do based on action
  else {
    if (action == shortPress) {
      SetOutputState(off);
    }
    else {
      // yuck - cast both ways to increment
      inputState nextState = static_cast<inputState>(static_cast<int>(CurrentInputState) + 1);
      if (nextState == reset) nextState = low;  // if we're cycling off off high, go back to low
      SetOutputState(nextState);
    }
  }
}

void setup() {            

  // set pwm frequency
  analogWriteFrequency(13, 1000);

  // initialize the output pins
  for (int oPin: outputPins)
    pinMode(oPin, OUTPUT);

  // little different iterate since we need pin# and location
  for (uint16_t i = 0; i < (sizeof(inputPins) / sizeof(inputPins[0])); i++) {
    int pinNumber = inputPins[i];
    pinMode(pinNumber, INPUT_PULLDOWN); // pull low to not float
    buttons[i].attach(pinNumber);
    buttons[i].interval(bounceTime);
    buttons[i].setPressedState(HIGH);   // when pin goes high, btn is pressed
    }

  SetOutputState(off);
}

void loop() {
  buttons[0].update();
  if (buttons[0].fell())
    {
      //Blink(4);
      if (buttons[0].previousDuration() > 1000)
        UpdateInputState(longPress);
      else
        UpdateInputState(shortPress);
    }
}
 
Last edited:
Edit2: It seems that the problem, more specifically, is mixing `digitalWrite` and `analogWrite` on the same Pin13. If I convert the "off" state to `analogWrite(13,0)`, then the other analog writes work fine, but none of the Blinks work (which are all `digitalWrite`)

When you use analogWrite(), you are configuring the pin for PWM. Once you do that, if you want to switch back to digitalWrite(), you can probably do that if you call pinMode(13,OUTPUT). Don't think of analogWrite(13,0) as equivalent to digitalWrite(13,0). Those are not the same thing.
 
When you use analogWrite(), you are configuring the pin for PWM. Once you do that, if you want to switch back to digitalWrite(), you can probably do that if you call pinMode(13,OUTPUT). Don't think of analogWrite(13,0) as equivalent to digitalWrite(13,0). Those are not the same thing.

Yeah, I'm aware they're not the same but found somewhere that simply running a digitalWrite again effectively stops the PWM mode. Only reference I can find here is from 2014 and suggests, as you mention, that once PWM is enabled (via an analogWrite) that the pin is in a 'special mode' and needs to be set up again to switch back out of that.

Is that something that's documented somewhere I just can't find? I switched everything to analogWrite for now and it seems to work as expected.
 
Yeah, I'm aware they're not the same but found somewhere that simply running a digitalWrite again effectively stops the PWM mode. Only reference I can find here is from 2014 and suggests, as you mention, that once PWM is enabled (via an analogWrite) that the pin is in a 'special mode' and needs to be set up again to switch back out of that.

Is that something that's documented somewhere I just can't find? I switched everything to analogWrite for now and it seems to work as expected.

The Arduino reference may help.

https://www.arduino.cc/reference/en/language/functions/digital-io/digitalwrite/
https://www.arduino.cc/reference/en/language/functions/analog-io/analogwrite/
 

Thanks for those. Somewhere in all my searching I found something that explicitly said you didn't need to redo pin setup switching between analog and digital, seems that's the core of my misunderstanding. I saw some comments on other PWM threads from this forum about how each pin is effectively MUXd and PWM and digital GPIO are different "sources" controlling the pin. So, I think I can make sense of this. Appreciate your help @joepasquariello!
 
Back
Top