I'm trying to use pin change interrupts with the ATtiny because I have to use the INT0 pin for SCK in I2C.
I can trigger the interrupt, but after it fires, it runs through setup() again. It may be just rebooting - with no bootloader it's hard to tell.
The attachInterrupt() with INT0 works fine.
I've tried a few different techniques and vectors [ISR (PCINT1_vect), SIGNAL (SIG_PCINT)] with similar results.
I may be doing something basically wrong here, but I can't figure out what.
Here is a sample that demonstrates the problem . . .
/* IR Receiver Test 2 GOES BACK TO MAIN WHEN I USE ISR
* SETUP:
* ATtiny Pin 1 = (RESET) N/U ATtiny Pin 2 = (D3) IR OUTPUT PIN
* ATtiny Pin 3 = (D4) to LED1 ATtiny Pin 4 = GND
* ATtiny Pin 5 = D0/SDA on GPIO ATtiny Pin 6 = (D1) Piezo
* ATtiny Pin 7 = D2/INT0/SCK on GPIO ATtiny Pin 8 = VCC (2.7-5.5V)
*/
#define LED1_PIN 4 // ATtiny Pin 3
#define PIEZO_PIN 1 // ATtiny Pin 6
#define IR_PIN 3 // IR sensor - ATtiny Pin 2
void setup(){
pinMode(LED1_PIN,OUTPUT); // for general DEBUG use
pinMode(IR_PIN,INPUT); // IR on pin 3
digitalWrite(IR_PIN, HIGH); // turn on pullup resistor
Blink(LED1_PIN,3); // shows it's in setup
//tone(PIEZO_PIN,2500,1000); // shows it's in setup (not supported with MIT core)
delay(1500);
// attachInterrupt(0,IR_ISR,FALLING); // THIS METHOD OF ISR WORKS
// Setup pin change interrupt . . .
// METHOD 4: *************
PCMSK |= (1<<PCINT3); // tell pin change mask to listen to D3
GIMSK |= (1<<PCIE); // enable PCINT interrupt in the general interrupt mask
sei(); // Enable all interrupts
}
void loop(){
//tone(PIEZO_PIN,2000,200); // when used makes different pitch than in setup()
delay (1500);
digitalWrite(LED1_PIN,LOW);
}
SIGNAL (SIG_PCINT) { // METHOD 4: *************
//void IR_ISR(){ // STANDARD METHOD WORKS
digitalWrite(LED1_PIN,HIGH);
}
void Blink(byte led, byte times){ // poor man's GUI
for (byte i=0; i< times; i++){
digitalWrite(led,HIGH);
delay (400);
digitalWrite(led,LOW);
delay (175);
}
}
Rather than help troubleshoot, can I interest you in a working library? It uses less memory and should be a bit faster than the "stock" pin-change interrupt code.
Sure! A working library for what? Interrupts? IR?
If it's for IR, I'd prefer an int only when it receives a signal, rather than a timer int that going off every x ms.
As I said, I need pin 7 free for I2c.
I'm afraid you'll be a bit disappointed. It's just for pin-change interrupts on tiny processors. But it is well debugged.
The Tiny Core includes a write-only software Serial (Tiny Debug Serial). All you need is a TTL serial converter (an Arduino will work). It beats the heck out of LED debugging.
Just read the datasheet....
Pin change interrupts are sort of global to each port, after having a PCINT you must write some code to determine wich pin as changed and then react accordind, its all in the datasheet...
That and google avr pin change interrupt..
My understanding of what your saying is that I need some code in the ISR - correct?
Only if there are two or more possible interrupt sources. If you only have one then by definition the interrupt comes form that pin and there's no need to figure anything out.
Good luck and please let me know how you make out!
I had good luck and this is how I made out . . .
The PinChangeInterrupt lib worked great! It even perfectly handled the FALLING mode.
I'm reading the IR remote every time and it had no effect on ISR0 so my I2C is working fine for the LCD display.
Major thanks CB. Thats a fine little lib.
Here's the code FWIW. (The IR_ISR() may be worth taking a look at.)
/* ATtiny85 IR Receiver Test BroHogan 2/11/11
* Tests IR receiver (using pin change interrupt) while I2C LC display is used.
* SETUP:
* ATtiny Pin 1 = (RESET) N/U ATtiny Pin 2 = (PB3) IR OUTPUT PIN
* ATtiny Pin 3 = (PB4) to LED1 ATtiny Pin 4 = GND
* ATtiny Pin 5 = PB0/SDA on GPIO ATtiny Pin 6 = (PB1) N/U
* ATtiny Pin 7 = PB2/INT0/SCK on GPIO ATtiny Pin 8 = VCC (2.7-5.5V)
*/
#include <PinChangeInterrupt.h> // mimics attachInterrupt() but a PCI for ATtiny
#include "TinyWireM.h" // uses TinyWireM lib low level functions
#include <LiquidCrystal_I2C.h> // for LCD w/ GPIO MODIFIED for the ATtiny85
#define LED1_PIN 4 // ATtiny Pin 3
#define IR_PIN 3 // IR sensor - ATtiny Pin 2
#define GPIO_ADDR 0x3F // (PCA8574A A0-A2 @5V) typ. A0-A3 Gnd 0x20 / 0x38 for A
// IR receiver defines
#define ENTER 12
#define UP 17
#define DOWN 18
#define RIGHT 19
#define LEFT 20
#define OK 21
#define POWER 22
#define SLEEP 55
#define INFO 59
// vars for IR_ISR() (must be global)
volatile boolean IR_Avail; // flag set if IR has been read
volatile unsigned int IR_Return; // returns IR code received
volatile unsigned long ir_mask; // Loads variable ir_string
volatile unsigned int ir_bit_seq; // Records bit sequence in ir string
volatile unsigned int ir_string; // Stores ir string
LiquidCrystal_I2C lcd(GPIO_ADDR,16,2); // set address & 16 chars / 2 lines
void setup(){
pinMode(LED1_PIN,OUTPUT); // for general DEBUG use
pinMode(IR_PIN,INPUT); // IR on pin 3
digitalWrite(IR_PIN, HIGH); // turn on pullup resistor
lcd.init(); // initialize the lcd & TinyWireM
lcd.backlight(); // Print a message to the LCD.
lcd.print("Hello, IR!");
attachPcInterrupt(3,IR_ISR,FALLING); // Catch IR sensor on PB3 with pin change interrupt
}
void loop(){
Check_IR();
}
void Check_IR(){ // check if remote used and process
int remoteInput; // normalized input from remote
if(IR_Avail){ // check if key on IR has been pressed
remoteInput = IR_Return + 1; // convert raw return to actual digit values
if (remoteInput == 10) remoteInput = 0; // special case for zero key
lcd.clear();
switch (remoteInput) { // a case for each key
case 0 ... 9: // digits 0-9
lcd.print("Digit-");
lcd.print(remoteInput,DEC);
break;
case ENTER:
lcd.print("Enter");
break;
case UP:
lcd.print("Up");
break;
case DOWN:
lcd.print("Down");
break;
case RIGHT:
lcd.print("Right");
break;
case LEFT:
lcd.print("Left");
break;
case OK:
lcd.print("OK");
break;
case POWER:
lcd.print("Power");
break;
case SLEEP:
lcd.print("Sleep");
break;
case INFO:
lcd.print("Info");
break;
default:
lcd.print("????-");
lcd.print(remoteInput,DEC);
}
IR_Avail = false; // allow IR again
digitalWrite(LED1_PIN, LOW); // turn off LED
}
}
void IR_ISR(){ // This ISR is called for EACH pulse of the IR sensor
if(ir_bit_seq == 0){ // it is the long start pulse
for(int i = 0; i<20; i++){ // see if it lasts at least 2 ms
delayMicroseconds(100); // 100
if(digitalRead(IR_PIN)) return; // it's doesn't so get out (low active)
}
ir_bit_seq = 1; // mark that the start pulse was received
ir_mask = 1; // set up a mask for the next bits
return;
}
delayMicroseconds(900); // wait 900 us and test
if(!digitalRead(IR_PIN)) ir_string = ir_string | ir_mask; // Stores 1 in bit
ir_mask = ir_mask << 1; // shifts ir_mask by one to the left
ir_bit_seq++; // ir_bit_seq is incrimented by one
if(ir_bit_seq == 12){ // after remote sends 12 bits it's done
ir_mask = 63; // only want the last 6 bits - the command
ir_string = ir_string & ir_mask; // only keep last 6 bits
IR_Return = ir_string; // final result
IR_Avail = true; // indicate new command received
digitalWrite(LED1_PIN, HIGH); // turn on LED to show you got something
ir_bit_seq = 0; // clean up
ir_string = 0;
ir_mask = 0;
for(int i = 0; i<25; i++){ // delay to stop repeat 10ms / loop ~250ms about right
delayMicroseconds(10000); // 16383 is maximum value so need to repeat
}
}
}