Tôi nghĩ rằng sự trùng lặp đặt tên của vectơ và chân là khó hiểu
Nó là!
Lý do có 8 chân ngoài khác nhau cho một vectơ ngắt là để giúp bố trí PCB dễ dàng hơn hoặc sử dụng một chân khác nếu có xung đột với chức năng ghim khác.
Tôi có đúng không khi nghĩ ... cách duy nhất để xác định (các) pin nào gây ra ngắt là ghi lại trạng thái của chúng sau mỗi lần ngắt và so sánh giá trị hiện tại và trước đó của tất cả các chân được bật trong PCMSKn?
Khá nhiều, giả sử bạn chỉ quan tâm đến PB0 (PCINT0) và PB1 (PCINT1). Vì vậy, thay đổi pin cho phép mặt nạ PCMSK0 sẽ được đặt thành 0x03.
// External Interrupt Setup
...
volatile u_int8 previousPins = 0;
volatile u_int8 pins = 0;
ISR(SIG_PIN_CHANGE0)
{
previousPins = pins; // Save the previous state so you can tell what changed
pins = (PINB & 0x03); // set pins to the value of PB0 and PB1
...
}
Vì vậy, nếu pins
là 0x01, bạn biết đó là PB0 ... Và nếu bạn cần biết những gì đã thay đổi, bạn cần so sánh nó với previousPins
, chính xác như những gì bạn nghĩ.
Hãy ghi nhớ trong một số trường hợp, pins
có thể không chính xác nếu pin có trạng thái thay đổi kể từ khi ngắt nhưng trước đó pins = (PINB & 0x03)
.
Một tùy chọn khác là sử dụng các vectơ ngắt riêng biệt với một chân từ mỗi vectơ để bạn biết cái nào được thay đổi. Một lần nữa, điều này cũng có một số vấn đề, như ưu tiên ngắt và khi CPU đi vào ISR, toàn cầu gián đoạn cho phép chút I-bit
trong SREG
sẽ bị xóa để tất cả các ngắt khác bị vô hiệu hóa, mặc dù bạn có thể đặt nó bên trong ngắt nếu bạn muốn, mà có thể là một ngắt lồng nhau.
Để biết thêm thông tin, hãy xem ghi chú ứng dụng của Atmel bằng cách sử dụng Ngắt bên ngoài cho các thiết bị megaAVR.
Cập nhật
Đây là một ví dụ mã hoàn chỉnh tôi vừa tìm thấy ở đây .
#include <avr/io.h>
#include <stdint.h> // has to be added to use uint8_t
#include <avr/interrupt.h> // Needed to use interrupts
volatile uint8_t portbhistory = 0xFF; // default is high because the pull-up
int main(void)
{
DDRB &= ~((1 << DDB0) | (1 << DDB1) | (1 << DDB2)); // Clear the PB0, PB1, PB2 pin
// PB0,PB1,PB2 (PCINT0, PCINT1, PCINT2 pin) are now inputs
PORTB |= ((1 << PORTB0) | (1 << PORTB1) | (1 << PORTB2)); // turn On the Pull-up
// PB0, PB1 and PB2 are now inputs with pull-up enabled
PCICR |= (1 << PCIE0); // set PCIE0 to enable PCMSK0 scan
PCMSK0 |= (1 << PCINT0); // set PCINT0 to trigger an interrupt on state change
sei(); // turn on interrupts
while(1)
{
/*main program loop here */
}
}
ISR (PCINT0_vect)
{
uint8_t changedbits;
changedbits = PINB ^ portbhistory;
portbhistory = PINB;
if(changedbits & (1 << PB0))
{
/* PCINT0 changed */
}
if(changedbits & (1 << PB1))
{
/* PCINT1 changed */
}
if(changedbits & (1 << PB2))
{
/* PCINT2 changed */
}
}