1

I'm replacing a busted fan remote with a new one built from scratch and I've got it working but need to improve the interrupts that are being used to detect button presses.

The MCU is an ATtiny84A and the switches are SPST momentary switches.

The issue is that the current program uses simple Pin Change Interrupts, but I get double interrupts if I hold the button too long, because it's detecting the falling edge Change first, then the falling edge Change second. It only happens if I hold it too long, because the buttons are software debounced. So what I want to do is act on falling edge only.

INT0 is not available for hardware falling edge detection as it's being used for PWM, so I think it has to be a software solution, but don't know how to implement it. Can anyone help, please?

Relevant parts of the program are below.

//Setup
void setup(){
  //Set pin modes.
  pinMode (buttonFanHigh, INPUT_PULLUP);
  pinMode (buttonFanMed, INPUT_PULLUP);
  pinMode (buttonFanLow, INPUT_PULLUP);
  pinMode (buttonFanOff, INPUT_PULLUP);
  pinMode (buttonLight, INPUT_PULLUP);

  //Set unused pins to INPUT_PULLUP as recommended by the datasheet.
  pinMode (mosi, INPUT_PULLUP);
  pinMode (miso, INPUT_PULLUP);
  pinMode (reset, INPUT_PULLUP);
  pinMode (usck, INPUT_PULLUP);

  //Set PWM pin modes.
  pinMode(chargePumpA, OUTPUT);
  pinMode(chargePumpB, OUTPUT);

  chargePumpOff();                              //Set initial charge pump state.

  set_sleep_mode(SLEEP_MODE_PWR_DOWN);          //Set power management mode.  Power-down is the lowest power state available while using the internal oscillator.
  
  //Set up the transmitter.
  transmitter.enableTransmit(radioTx);          //Sets the transmitter pin to OUTPUT via the RCSwitch library.
  transmitter.setProtocol(11);                  //Transmission protocol.  Determined using an RF Sniffer program.

  //Set up pin change interrupts, which are required to wake from sleep and used to trigger radio transmissions.
  //Note there are two PC registers (0 & 1) on the ATtiny84A that handle different sets of pins.
  GIFR   |= bit (PCIF0);                        //Setting bit to 1 clears any outstanding interrupts.
  GIMSK  |= bit (PCIE0);                        //Setting bit to 1 enables pin change interrupts.
  GIFR   |= bit (PCIF1);
  GIMSK  |= bit (PCIE1);
  PCMSK0 |= bit (PCINT0);                       //Setting bit to 1 assigns this pin to be minotored for interupts.
  PCMSK0 |= bit (PCINT1);
  PCMSK0 |= bit (PCINT2);
  PCMSK0 |= bit (PCINT3);
  PCMSK1 |= bit (PCINT9);
}
//Main loop
void loop() {
  loopTime = millis();

  if (interruptFlag) {
    powerSaveModeTime = loopTime;               //Restart the power-down timer if a button is pressed.

    checkVoltage();                             //Set low voltage flag when low voltage detected.  Will remain on until the MCU is reset (ie. batteries changed).
    
    if (whichSwitch != lastSwitch) {            //Reset the debounce timer if a new button press is registered.
      lastSwitch = whichSwitch;
      lastDebounceTime = millis();
    }

    if ((loopTime - lastDebounceTime) > debounceDelay) {
      chargePumpOn();
      lightOnTime = millis();
      
      switch(whichSwitch) {
        case 4:
          setFanHigh();
          break;
        case 3:
          setFanMed();
          break;
        case 2:
          setFanLow();
          break;
        case 1:
          setFanOff();
          break;
        case 0:
          setLightOnOff();
          break;
        default:
          break;
      }
      interruptFlag = false;                      //Clear interrupt flag.  The button press was successful, so don't need to come back into this code again until next button press.
    }
  }

  if (lowVoltageFlag) {
    if (lightStatus) {
      if ((loopTime - lightOnTime) > lightDuration) {
        chargePumpOff();
      }
    } else if ((loopTime - lightOffTime) > lightDuration) {
      chargePumpOn();
    }
  } else if ((loopTime - lightOnTime) > lightDuration) {
    chargePumpOff();
  }

  if ((loopTime - powerSaveModeTime) > powerDownTime) {
    powerDown();
  }
}
//Interrupt service routines

ISR (PCINT0_vect) {
  interruptFlag = true;

  if(digitalRead(buttonFanMed) == LOW) {
    whichSwitch = 3;
  }
  if(digitalRead(buttonFanLow) == LOW) {
    whichSwitch = 2;
  }
  if(digitalRead(buttonFanOff) == LOW) {
    whichSwitch = 1;
  }
  if(digitalRead(buttonLight) == LOW) {
    whichSwitch = 0;
  }
}

ISR (PCINT1_vect) {
  interruptFlag = true;

  if(digitalRead(buttonFanHigh) == LOW) {
    whichSwitch = 4;
  }
}
Nexmo16
  • 137
  • 1
  • 11

1 Answers1

0

Since you have a separate button for each mode selection and each mode is independent, I do not think you need any debouncing at all.

Instead, try a much simpler approach.


unsigned long powerDownTimeMillis =0;
const unsigned long POWERDOWN_TIMEOUT_MS = 5 * 100  // Sleep after 5 seconds of no button presses

//Interrupt service routines

ISR (PCINT0_vect,PCINT1_vect) {

  if(digitalRead(buttonFanMed) == LOW) {
    setFanMed();
  } else if(digitalRead(buttonFanLow) == LOW) {
     setFanLow();
  } else if(digitalRead(buttonFanOff) == LOW) {
    setFanOff();
  } else if(digitalRead(buttonLight) == LOW) {
    setLightOnOff();
  } else {
    // No buttons currently down so interrupt was probably from 
    // a button lifting, so ignore.
    return;
  }

  powerDownTimeMillis = millis() + POWERDOWN_TIMEOUT_MS;

}


It is not clear what the charge pump stuff is about from just this code, but I bet you can easily include whatever it needs to do in the above logic.

Note that if two buttons are pressed at the same time then it is possible that you might switch between modes, but that seems to be OK in your code. If not, you might want to consider adding variables called something like TargetFanPWM and CurrentFanPWM and then have the ISR change the target based on the last button press. Then the foreground code can slowly adjust the current PWM towards the target PWM. This will both make it so flickering simultaneous button pressed don't have any practical effect, and it also adds a nice slow slew to speed adjustments.

bigjosh
  • 1,273
  • 13
  • 19