Hi
I am trying to have two push buttons generate PCI to wake up ATtiny84 from Power Down sleep mode.
I think I understand the principle, I’ve done it before on other MCU but I can’t see the error in my code below - I stripped out most of the code to focus on my PCI issue.
The ISR routines capture the interrupts generated - without these I get a reset on PCI interrupt.
My mistake must be in how I set up the PCMSK0 PCMSK1 and then the GIMSK to enable the PCI for the pins in question.
I appreciate my buttons, SW1 and SW2 are on PCINT5 and PCINT6 respectively and hence in the MCMSK0.
The buttons may be read for a brief period from start up before the inactivity timeout causes sleep to be invoked.
Periodically the WDT is successfully able to wake the device from POWER DOWN sleep.
I have a natty LED for debug to indicate when the unit is awake.
What I want is to have the buttons generate a PCI and for this to wake up the unit from POWER DOWN sleep.
Can you see what I have done wrong as I can’t?
I’ve tried setting PCMSK0 and GIMSK just before I dive into sleep just in case some Arduino code is changing things but this has no affect.
My code snippet:
// ATtiny84_PCI_Help
// cSpell:includeRegExp CStyleComment
// written for ATtiny84
// Peter Charles-Jones
#ifdef F_CPU
#undef F_CPU
#define F_CPU 8000000UL // Main clock is internal 32kHz oscillator / 8- required for delay function
#endif
#include <Arduino.h>
#include <avr/sleep.h> // useful macros for putting the unit into sleep mode
#include <avr/wdt.h> // used to wake up MCU periodically from Sleep
#define SW1_bp PA5 // PCINT5
#define SW2_bp PA6 // PCINT6
#define LED1_bp PB1
#define LED2_bp PB0
#define LED3_bp PA7
#define ADC_bp PA0
// PA1 Serial TXD
// PA2 Serial RXD // PCINT2
// PA3 Software Serial TXD - Tx
// PB2 Software Serial RXD - Rx // PCINT10
/* Pin definitions and use
// ATtiny84
// VCC 1| u |14 GND
// PCINT8/XTAL1/CLKI PB0 2| |13 PA0 ADC0/AREF/PCINT0
// PCINT9/XTAL2 PB1 3| |12 PA1 ADC1/AIN0/PCINT1
// PCINT11/~RESET~/dW PB3 4| |11 PA2 ADC2/AIN1/PCINT2
// PCINT10/INT0/OC0A/CKOUT PB2 5| |10 PA3 ADC3/T0/PCINT3
// PCINT7/ICP/OC0B/ADC7 PA7 6| |9 PA4 ADC4/USCK/SCL/T1/PCINT4
// PCINT6/OC1A/SDA/MOSI/DI/ADC6 PA6 7| |8 PA5 ADC5/DO/MISO/OC1B/PCINT5
This core uses a simple scheme for assigning the Arduino pin numbers:
Pins are numbered starting from the the I/O pin closest to Vcc as pin 0
and proceeding counterclockwise, skipping the (mostly) non-usable RESET pin. `
See Dr Azzy link <https://europe1.discourse-cdn.com/arduino/original/4X/3/a/5/3a50e2c98f4f87265f182d2e8e1adf5fcaa2376d.jpeg>
// ARDUINO digital pin numbers
// VCC 1| u |14 GND
// (D 0) PB0 2| |13 AREF (D 10)
// (D 1) PB1 3| |12 PA1 (D 9)
// PB3 4| |11 PA2 (D 8)
// PWM INT0 (D 2) PB2 5| |10 PA3 (D 7)
// PWM (D 3) PA7 6| |9 PA4 (D 6)
// PWM (D 4) PA6 7| |8 PA5 (D 5) PWM
//
// PA1 TXD Arduino pin 9
// PA2 RXD Arduino pin 8
// ISP header
// MISO 1 2 VCC
// SCK 3 4 MOSI
// ~RST 5 6 GND
//
*/
#define buttonDebounce 10 // button debounce time (ms)
#define BAUD 9600 // define the serial baud rate
#define Prtln(x) Serial.println(x)
#define Prt(x) Serial.print(x)
volatile uint8_t INT_FLAG; // ISR flags
#define WDT_INT_bp 0
#define WDT_Prescaler (1<<WDP3)|(0<<WDP2)|(0<<WDP1)|(1<<WDP0) // every 8 seconds
// put function declarations here:
void goToSleep();
void enableWDT();
void sysInit();
int pinChange (unsigned long* last_time, int pinID, char tPIN, char hPIN, char* pins);
int pinHIGH (int pinID, char* pins);
void setup() {
// put your setup code here, to run once:
sysInit();
Serial.begin(BAUD);
Prtln(F("\n\r\n\rStarting serial"));
enableWDT();
//goToSleep();
}
void loop() {
// put your main code here, to run repeatedly:
static char PINS = PINA; // debounced copy of the PIND register
static unsigned long inactivity_time = millis(); // inactivity timer
static unsigned long lastSW1_time = millis(); // debounce timer
static unsigned long lastSW2_time = millis(); // debounce timer
static char tempPINA; // used as snapshot for PIND status if it changes mid process
static char pinAHistory; // the last read PIND status
// check buttons
tempPINA = PINA; // get latest PINA setting
if (pinChange(&lastSW1_time, SW1_bp, tempPINA, pinAHistory, &PINS)){
inactivity_time = millis();
if (pinHIGH(SW1_bp,&PINS)){ // SW1 is HIGH - OC - button released
}
else{ // SW1 is LOW - CC - button pressed
Prt (F("\n\rSW1 presed "));
}
}
if (pinChange(&lastSW2_time, SW2_bp, tempPINA, pinAHistory, &PINS)){
inactivity_time = millis();
if (pinHIGH(SW2_bp,&PINS)){ // SW1 is HIGH - OC - button released
}
else{ // SW1 is LOW - CC - button pressed
Prt (F("\n\rSW2 presed "));
}
}
pinAHistory = tempPINA;
if (INT_FLAG & (1<<WDT_INT_bp)){
INT_FLAG &= ~(1<<WDT_INT_bp);
WDTCSR |= (1<<WDIE); // required to prevent a reset on next timeout
Prtln(F("WDT"));
inactivity_time = millis(); // keeps teh unit awake a bit on each WDT
}
if ((millis() - inactivity_time) > (1000)){ // to to POWER DOWN sleep mode after inactivity
PORTB &= ~(1<<LED1_bp);
goToSleep();
}
else{
PORTB |= (1<<LED1_bp);
}
//goToSleep();
}
// interrupt service routines
ISR (PCINT0_vect){ // pin change interrupt - used to wake up unit from PowerDown sleep mode
} // end of pin change interrupt
ISR (PCINT1_vect){ // pin change interrupt - used to wake up unit from PowerDown sleep mode
} // end of pin change interrupt
ISR (WDT_vect){ // WDT interrupt
INT_FLAG |= (1<<WDT_INT_bp); // set flag so that main knows to execute some code
}
// put function definitions here:
void goToSleep(void){
//PCMSK0 |= (1<<PCINT5)|(1<<PCINT6)|(1<<PCINT2); // set PCI on SW1, SW2 and Serial RXD
//GIMSK |= (1<<PCIE1)|(1<<PCIE0); // enable PCI
char oldSREG = SREG; // grab a copy of the current value of the system register
cli(); // turn off interrupts
sleep_enable();
SREG = oldSREG; // enable interrupts if they were enabled before
sleep_cpu(); // this is the point where the MCU shuts down
// zzZZzz
sleep_disable(); // code restarts here on wake up
}
void enableWDT(void){ // turn on WDT
wdt_reset(); // reset watchdog - don't want it timing out mid change
unsigned char oldSREG = SREG; // store SREG state specially for global interrupt enable
cli(); // turn off interrupts for timed sequence
MCUSR &= ~(1<<WDRF); // clear the WDT restarted flag
WDTCSR |= (1<<WDCE)|(1<<WDE); // start timed sequence enabling it to be edited
WDTCSR = (1<<WDIE)|(1<<WDE)|WDT_Prescaler; // Interrupts enabled, WDT enabled, prescaler set
SREG = oldSREG; // restore SREG (will also re-enable interrupts if they were enabled before)
}
int pinChange (unsigned long* last_time, int pinID, char tPIN, char hPIN, char* pins) { // has the pin changed state?
if ((tPIN & (1<<pinID))!= (hPIN & (1<<pinID) )){ // last pin state different
*last_time = millis();
}
if ((millis() - *last_time > buttonDebounce)){ // debounce time has elapsed
if ((tPIN & (1<<pinID))!=(*pins & (1<<pinID))){ // is the current pin state different from the current one
*pins &= ~(1<<pinID); // mask out the bit position
*pins |= (tPIN & (1<<pinID)); // get the bit value at bit location and OR into PINS
return 1; // pin has changed
}
}
return 0; // pin has not changed state
}
int pinHIGH (int pinID, char* pins){
if (*pins & (1<<pinID)){ // pin value is 1.. switch / button is Open Circuit
return 1; // HIGH
}
else{ // pin value is 0 .. switch / button is Closed to ground
return 0; //LOW
}
}
// initialization functions here:
void sysInit(){
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
//PCMSK0 |= (1<<SW1_bp)|(1<<SW2_bp)|(1<<PA2); // set PCI on SW1, SW2 and Serial RXD
PCMSK0 |= (1<<PCINT5)|(1<<PCINT6)|(1<<PCINT2); // set PCI on SW1, SW2 and Serial RXD
//PCMSK1 |= (1<<PB2); // set PCI for Software Serial RXD
PCMSK1 |= (1<<PCINT10); // set PCI for Software Serial RXD
GIMSK |= (1<<PCIE1)|(1<<PCIE0); // enable PCI
DDRB |= (1<<LED1_bp)|(1<<LED2_bp); // define output ports
PORTB &= ~((1<<LED1_bp)|(1<<LED2_bp)); // set outputs LOW
DDRA |= (1<<LED3_bp); // define output ports
DDRA &= ~((1<<SW1_bp)|(1<<SW2_bp)); // make sure thse are inputs
PORTA &= ~((1<<LED3_bp)); // set outputs LOW
PORTA |= (1<<SW1_bp)|(1<<SW2_bp); // define internal pull up on digital inputs
sei ();
}