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;
}
}