I apologize upfront if this is going to be a lengthy post.
I started on a project where I wanted my ATtiny45 to wake from deep sleep, so I played with the pin change interrupt. I sampled a switch connected to PB0 and triggered a PCI on each pin change.
My impression was that sometimes switch presses were missed. I spent a lot of time on this and now think there is some 'issue' with the change detection logic.
I stripped down the software to the bare minimum which still reproduces the effect. Here it is, it's only 13 instructions long (it's a disassembly with a few comments):
0: rjmp main ; reset vector, start main() 2: rjmp main ; vec_1, not used vec2: 4: sbi 0x16, 1 ; vec_2, pin change ISR; toggle PB1... 6: reti ; ...and that's it main: 8: ldi r24, 0x1E ; DDRB = 1 << PB4 | 1 << PB3 | 1 << PB2 | 1 << PB1; a: out 0x17, r24 c: sbi 0x18, 0 ; activate pullup on PB0 e: ldi r24, 0x20 ; GIMSK = 1 << PCIE; 10: out 0x3b, r24 12: ldi r24, 0x01 ; PCMSK = 1 << PCINT0; 14: out 0x15, r24 16: sei ; enable interrupts .L4: 18: rjmp .L4 ; spin
So, it just sets up the pins plus the PCI and then spins. The ISR will toggle PB1, then return. I expect to see one (or several, if the switch bounces) toggles on PB1 for each press and release.
I'm watching the pins with a fast scope.
Oh, and I should note that the low fuse is set to 0x24, so CLK is output at pin 3, and the chip is running at 128/8 = 16kHz.
Also with this minimum program I could see switch presses/releases being lost; i.e. I could see the switch transitioning from LO to HI, with a bouncing transition period, but NO PCI ISR triggered.
Note that I could clearly see if the ISR was triggered twice. I'd say that with my setup I had a single ISR call in about 70% of cases, two ISR calls in 20%, and ZERO ISR calls in 10% of switch presses/releases.
I also noted that, for my switch, the time between bouncing peaks was irregular but in the range of about 100usecs, which is quite close to the chip's cycle time at 16kHz.
After a while all this switch pressing got annoying, so I hooked up my scope's signal generator and tried to simulate the switch. I found a pattern that reliably causes the PCI change detection logic to fail.
It is quite simple: say, the input is LOW. Then, go HIGH for half a clock period; LOW for half a clock period; then HIGH and remain HIGH.
If this signal is applied to a pin and the initial rising edge happens while the clock signal is HIGH, the whole pin change is lost; PCIF won't be set, and the ISR won't be triggered.
This is consistent with the circuit given in 9.2.2, "Pin change interrupt timing", of the ATtinyx5 datasheet. I'm not an expert here, but I tried to apply my synthetic signal to this circuit, as shown in the attached timing diagram, and would expect the pin change to be lost.
As I understand it,
- the initial latch is transparent while CLK is HIGH, and keeps the state it had on the falling edge of CLK while CLK is low
- the signal that finally ends up in PCIF is generated from the XOR of the input and pin_sync, which is a delayed version of pin_lat
- with the given timing, this XOR is always low on rising edge of CLK, so PCIF is never set.
Looking forward to all comments. However, please note that this post is not about debouncing, so an answer like 'use a timer interrupt and poll your switch, that's better' won't really help. This is about the pin change interrupt :)