ATtiny85 Input/Pin Change Interrupt problem

I originally had this code for an ESP8266 project and I've adapted it to my current project. Originally I had 3 Pin Change Interrupt inputs for 3 sensors that would send a code to the ESP8266 and they worked as expected.

Now, the only pin responding to any input is pin 7 (PB2). My output data goes to PB3 when pin 7 (PB2) goes high and it should do the same for the other input pins 3,5,6, but nothing happens (verified with Oscope).

The interrupt library I'm using is
GitHub - GreyGnome/EnableInterrupt: New Arduino interrupt library, designed for Arduino Uno/Mega 2560/Leonardo/Due which doesn't require an ISR routine.

I'm using the Sparkfun Tiny AVR Programmer

I've gotten to the point where I've confused myself about bit shifting
assignments to registers as you'll see in comments to myself, however even using pinmode() hasn't helped. The enableINTs() function sets the register bits. setup() is where I attempt to set Inputs.

Currently there's no circuit, just the chip in the programmer and a jumper from Vcc I touch to the pins while watching the output on pin 3 with my scope.

What am I missing/doing wrong? (other than forgetting to attach my code)

/**
   ATtiny85 (2MHz) (APR 2022)
**/

// TIP -- Enable CKOUT and then use the scope to view the clock frequency on pin 5/6
// Use AVRDudess 2.13 GUI to set/chg these.
// Default Fuses  L=0x62 H=0xDF E=0xFF
// CKOUT Enabled  L=0x22 H=0xDF E=0xFF

//                 ATMEL ATTINY 25/45/85
//
//                               +-\/-+
//            <>         RESET  1|    |8  Vcc
//   DATA OUT <> Ain3 (D3) PB3  2|    |7  PB2 (D2) Ain1 <> pirSensorA
// pirSensorD <> Ain2 (D4) PB4  3|    |6  PB1 (D1) pwm1 <> pirSensorB
//                         GND  4|    |5  PB0 (D0) pwm0 <> pirSensorC
//                               +----+

#include <avr/io.h>
#include <avr/sleep.h>              // Sleep Modes
#include <avr/power.h>              // Power management
#include <avr/delay.h>              // Delay for < 8MHz  _delay_us / _delay_ms
#include <avr/interrupt.h>          // Conflict with EnableInterrupt.h??
#include <EnableInterrupt.h>        // Easy to use interrupts for ATtiny (GreyGnome)
#include <avr/wdt.h>

#define BODS              7         // BOD Sleep bit in MCUCR
#define BODSE             2         // BOD Sleep enable bit in MCUCR

#define PIRsensorD        3         // pin 3 / PCINT4 / PB4
#define PIRsensorC        5         // pin 5 / PCINT0 / PB0
#define PIRsensorB        6         // pin 6 / PCINT1 / PB1
#define PIRsensorA        7         // pin 7 / PCINT2 / PB2
#define DATA              PB3       // pin 2 Output pulses to TX module

#define F_CPU             2000000UL // 2 MHz (Set in code below to 2Mhz)

volatile int                   state = LOW; // Used in PinChgInt ISR
volatile int                Awakened = LOW;
volatile uint8_t             seconds = 0;   // Used in Timer ISR
volatile uint8_t DelayBeforeSleeping = 45;  // Time in seconds to delay before turning off ESP01

uint8_t                   mcucr1, mcucr2;
int                         delaycnt = 0;     // Interval counter
int                              bit = 0;
int                          runonce = 0;

// code = 0001 0001 0101 0000 0101 0101 0;
bool code[25]={0,0,0,1, 0,0,0,1, 0,1,0,1, 0,0,0,0, 0,1,0,1, 0,1,0,1, 0};  // Harborfreight motion sensor code

//###########################################################################################################################################//
void setup () {

  // Change CPU Freq. Page 33, Table 6-15
  cli();
  CLKPR = (1<<CLKPCE);
  //CLKPR = ((0<<CLKPS3) |(0<<CLKPS2) |(1<<CLKPS1) | (1<<CLKPS0));  // 1Mhz
  //CLKPR = ((0<<CLKPS3) |(0<<CLKPS2) |(0<<CLKPS1) | (1<<CLKPS0));  // 4Mhz
  CLKPR = ((0<<CLKPS3) |(0<<CLKPS2) |(1<<CLKPS1) | (0<<CLKPS0));    // 2Mhz
  sei();

  DDRB |= (1<<DATA); // set PB1 as Output for data pulses

  // Set pins as inputs
  DDRB |= ((0 << PIRsensorA) | (0 << PIRsensorB) | (0 << PIRsensorC) | (0 << PIRsensorD));

  // Which is correct? Want Input/should be ZERO.
  /*  
  DDRB &= ~(1 << PIRsensorA);    //_INPUT  PB2 port (pin 7)
  DDRB &= ~(1 << PIRsensorB);    //_INPUT  PB1 port (pin 6)
  DDRB &= ~(1 << PIRsensorC);    //_INPUT  PB0 port (pin 5)
  DDRB &= ~(1 << PIRsensorD);    //_INPUT  PB4 port (pin 3)

  DDRB |= (1 << PIRsensorA);    //_INPUT  PB2 port (pin 7)
  DDRB |= (1 << PIRsensorB);    //_INPUT  PB1 port (pin 6)
  DDRB |= (1 << PIRsensorC);    //_INPUT  PB0 port (pin 5)
  DDRB |= (1 << PIRsensorD);    //_INPUT  PB4 port (pin 3)
  */

  goToSleep();

}

//###########################################################################################################################################//
void loop() {
  
  // This condition is for repeat triggers while awake.
  if ((state == HIGH) && (Awakened == HIGH)) {

    TIMSK &= ~(1 << OCIE1A);      // DISABLE 1-second TIMER

    // Disable interrupts to avoid a loop
    // Interrupts re-enabled below if condition goes LOW.
    disableInterrupt(PIRsensorA);
    disableInterrupt(PIRsensorB);
    disableInterrupt(PIRsensorC);
    disableInterrupt(PIRsensorD);

    if(PINB & ((1 << PB4) | (1 << PB2) | (1 << PB1) | (1 << PB0))) {
      while(PINB & ((1 << PB4) | (1 << PB2) | (1 << PB1) | (1 << PB0))) {
        sendcode();
      }

      state = LOW;
      seconds = 0;
    }
  }
  
  // This is condition after waking from Sleep.
  if ((state == HIGH) && (Awakened == LOW)){

    TIMSK &= ~(1 << OCIE1A);      // DISABLE 1-second TIMER, sensors could be on longer than 30 seconds

    Awakened = HIGH;
    seconds = 0;
  }

  // This condition happens when all sensors have gone LOW
  if(state == LOW){

    enableInterrupt(PIRsensorA|PINCHANGEINTERRUPT, trigger, RISING);
    enableInterrupt(PIRsensorB|PINCHANGEINTERRUPT, trigger, RISING);
    enableInterrupt(PIRsensorC|PINCHANGEINTERRUPT, trigger, RISING);
    enableInterrupt(PIRsensorD|PINCHANGEINTERRUPT, trigger, RISING);
    
    TIMSK |= (1 << OCIE1A); //Re-enable 1 second timer interrupt.
    sei();
  }
}

//###########################################################################################################################################//
void sendcode(void){

  if (runonce == 0) { // Was for testing in main loop
    runonce = 0;

    // Send bitsream 6 times
    for(int cnt=1;cnt<=6;cnt++){
      for(bit=0;bit<=24;bit++) {
        if (code[bit] == 1) {
            PORTB |= (1 << DATA);   //_HIGH_
            _delay_us(4790);
            PORTB &= ~(1 << DATA);   //_LOW_
            _delay_us(1600);
        }
        else {
            PORTB |= (1 << DATA);   //_HIGH_
            _delay_us(1600);
            PORTB &= ~(1 << DATA);   //_LOW_
            _delay_us(4790);
        }
      }
      _delay_us(24000); // Delay between pulse streams, if too short fails to get decoded.
    }
  }
}

//###########################################################################################################################################//
void enableINTs(void){

  PCMSK |= (1 << PCINT0);  //Set pin 5(PB0/D0) to be interrupt pin
  PCMSK |= (1 << PCINT1);  //Set pin 6(PB1/D1) to be interrupt pin
  PCMSK |= (1 << PCINT2);  //Set pin 7(PB2/D2) to be interrupt pin
  PCMSK |= (1 << PCINT4);  //Set pin 3(PB4/D4) to be interrupt pin
  
  GIFR  |= (1 << PCIF);    //Clear any outstanding interrupts
  GIMSK |= (1 << PCIE);    //Enable Pin Change interrupts
  
  enableInterrupt(PIRsensorA|PINCHANGEINTERRUPT, trigger, RISING);
  enableInterrupt(PIRsensorB|PINCHANGEINTERRUPT, trigger, RISING);
  enableInterrupt(PIRsensorC|PINCHANGEINTERRUPT, trigger, RISING);
  enableInterrupt(PIRsensorD|PINCHANGEINTERRUPT, trigger, RISING);
}

//###########################################################################################################################################//
void trigger(void) {
  
  //state = !state;             // Toggle "state"
  state = HIGH;                 // For Clarity
  seconds = 0;
}

//###########################################################################################################################################//
void goToSleep()
{
  
  cli();                     //Disable Interrupts
  TIMSK &= ~(1 << OCIE1A);   //Disable Timer

  GIFR  |= (1 << PCIF);      //Clear any outstanding interrupts
  
  enableINTs();              //Set Pin Change interrupts before we sleep, or coma!

  ACSR |= _BV(ACD);          //disable the analog comparator
  ADCSRA &= ~_BV(ADEN);      //disable ADC

  Awakened  = LOW;
  
  mcucr1 = MCUCR | _BV(BODS) | _BV(BODSE);  //turn off the brown-out detector
  mcucr2 = mcucr1 & ~_BV(BODSE);
  MCUCR = mcucr1; // WTF?
  MCUCR = mcucr2; // WTF? This overwrites previous line!

  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  power_all_disable();    // power off ADC, Timer 0 and 1, serial interface
  
  GIFR  |= (1 << PCIF);           //Clear any outstanding interrupts
  
  sleep_enable();
  sei();                  //Enable Interrupts
  sleep_cpu();            //Interrupt during sleep runs trigger() from here FIRST, then returns here.

  GIMSK = 0x00;           //disable INT0
  sleep_disable();
  power_all_enable();     //Power everything back on

  state    = HIGH; 
  Awakened = LOW;
  seconds  = 0;
}

//###########################################################################################################################################//
ISR(TIM1_COMPA_vect)
{
  delaycnt++;
  
  if(seconds <= DelayBeforeSleeping) seconds++;
  else {

    TIMSK &= ~(1 << OCIE1A);      // DISABLE 1-second TIMER
  }
}

That library isn't compatible with the ATtiny.

Not sure why you're saying that. The documentation in the link I provided says it is, and I used it in my previous project and it worked perfectly.

Do you use pull down resistors?
Which core do you use?
You should probably make a simple demo sketch that would be easier to troubleshoot.
You could also try this library
https://github.com/NicoHood/PinChangeInterrupt

Thanks for taking a look and replying. I actually just did a demo sketch by stripping a ton of stuff from the code I posted and it works, so its definitely not the library.

No core I'm aware of, I pop the chip in the programmer, hit upload and my program is loaded, no core has ever been loaded unless its automagically without my knowledge. I'm not using any pulldown resistors either.

I guess with the demo sketch I just did working, it points to some coding problem or register conflict.

Ok, I can't point to any specific thing that caused this not to work, but the problem was in the main loop(). Let's just say I don't know what I was thinking when I wrote this, but there was definitely some 'excessive logic' and some duplicate/unnecessary code. I also want to make it clear for anyone in the future, the pin change interrupt library is definitely compatible with the ATtiny85 and works fine for me. I'll try to repost the new code when I'm finished polishing it up a little more.

You're right, in the intro it shows support the the ATtinys but the documentation below doesn't have a section for the ATtinys.

My guess is that the first call of enableInterrupt() actually registers the pinchange mask and the rest is ignored but finding the actual code that is compiled for the ATtiny is like finding a needle in the haystack.

That code doesn't do what you expect it to do. Or-ing with 0 won't change the value. But as the default value of the register at startup is 0 this might work, nevertheless.
The next version (in the comment) is the correct on to be used but you must use the port bit numbers and not the pin numbers. So the value for sensor D is 4, for sensor C it's 0, for sensor B it's 1 and for sensor A it's 2.

documentation below doesn't have a section for the ATtinys

I agree, the documentation isn't clear, if you look at the EnableInterrupt.h file though you can find a section for the ATtinys.

Thanks for clearing up the part about OR-ing. I was grasping at straws doubting what I had in my original code even though that worked. I changed my #defines from using pin numbers to port names defined in some other include.h

#define PIRsensorD        PB4       // pin 3 / PCINT4 / PB4
#define PIRsensorC        PB0       // pin 5 / PCINT0 / PB0
#define PIRsensorB        PB1       // pin 6 / PCINT1 / PB1
#define PIRsensorA        PB2       // pin 7 / PCINT2 / PB2
#define DATA              PB3       // pin 2 Data Output pulses to TX module

Fixed port initialization back to this:

  DDRB &= ~(1 << PIRsensorA);    //_INPUT  PB2 port (pin 7)
  DDRB &= ~(1 << PIRsensorB);    //_INPUT  PB1 port (pin 6)
  DDRB &= ~(1 << PIRsensorC);    //_INPUT  PB0 port (pin 5)
  DDRB &= ~(1 << PIRsensorD);    //_INPUT  PB4 port (pin 3)

Appreciate the clarification, thank you.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.