핀 변경 인터럽트의 올바른 사용법


10

누른 버튼을 감지하기 위해 핀 변경 인터럽트를 사용하려고합니다. 지금까지 나는 이런 종류의 인터럽트로 작업 한 적이 없으며 몇 가지 문제가 있으므로 이것이 올바른 사용법인지 확인하고 싶습니다.

데이터 시트를 올바르게 얻은 경우 핀 변경 인터럽트를 사용하려면 다음을 수행해야합니다.

  1. PCMSK 레지스터에서 제어하려는 PIN 설정
  2. 핀 변경 인터럽트 제어 (PCICR)를 위해 PIN 레지스터 활성화
  3. 인터럽트 활성화
  4. 해당 인터럽트 벡터를 사용하십시오

프로젝트 : Simple Moodlamp, 4 개의 버튼으로 제어되는 색상.

설정:

  • Atmega168A-PU
  • 미니 푸시 버튼 스위치 4 개
  • 3W RGB LED를 제어하는 ​​MOSFET

다음은 예상대로 작동하지 않는 사용중인 코드입니다.

#include <avr/io.h>
#include <stdint.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#define BUTTON1 (1<<PC5) 
#define BUTTON2 (1<<PC4) 
#define BUTTON3 (1<<PC3) 
#define BUTTON4 (1<<PC2) 

#define GREEN   (1<<PB1) 
#define BLUE    (1<<PB2) 
#define RED     (1<<PB3) 

void init() {

        // enable LED
        DDRB |= GREEN;
        DDRB |= BLUE;
        DDRB |= RED;

        // button pullups
        PORTC |= BUTTON1;
        PORTC |= BUTTON2;
        PORTC |= BUTTON3;
        PORTC |= BUTTON4;

        // pin change interrupts for buttons
        PCMSK1 |= PCINT13;
        PCMSK1 |= PCINT12;
        PCMSK1 |= PCINT11;
        PCMSK1 |= PCINT10;

        // enable pin change for buttons
        PCICR |= PCIE2;

        sei();

}

ISR(PCINT2_vect) {

                PORTB = BLUE;
}


void ledTest() {

                PORTB ^= RED;
                _delay_ms(250);
                PORTB ^= RED;
                _delay_ms(250);
                PORTB ^= RED;
                _delay_ms(250);
                PORTB ^= RED;


                PORTB ^= BLUE;
                _delay_ms(250);
                PORTB ^= BLUE;
                _delay_ms(250);
                PORTB ^= BLUE;
                _delay_ms(250);
                PORTB ^= BLUE;

                PORTB ^= GREEN;
                _delay_ms(250);
                PORTB ^= GREEN;
                _delay_ms(250);
                PORTB ^= GREEN;
                _delay_ms(250);
                PORTB ^= GREEN;
}

int main() {

        init();
        ledTest();

        _delay_ms(500);
        PORTB |= GREEN;

        while(1) {
                _delay_ms(100);
        }
}

참고 : 버튼을 디 바운스해야합니다. 이 단계별로 시도하고 있으며 LED를 켜는 데 문제가되지 않기 때문에 여기서 무시했습니다.

질문 : 인터럽트를 사용하려는 방식이 올바른가요?

내 설정 문제 :

  • 버튼 1 ~ 3은 완전히 무시됩니다.
  • Button4가 atmega의 재설정을 트리거합니다

내가 확인한 것들 :

  • 버튼이 재설정 PIN에 연결되지 않습니다
  • 누르면 버튼이 GND에 올바르게 연결됩니다
  • 누르지 않으면 버튼이 GND에 연결되지 않습니다
  • 인터럽트없이 버튼을 사용하면 버튼이 잘 작동합니다.

    if (! (PINC & BUTTON4)) {PORTB ^ = 파랑; }

  • 16MHZ 외부 결정 / 내부 결정
  • 라우팅 오류
  • atmega에서 PWR과 GND 사이에 100nF 커패시터를 사용하고 있습니다.
  • VCC (7), GND (8), GND (22), AVCC (20)가 연결됨 (AREF가 필요하지 않기 때문에 연결되지 않음)

당신은 PCIE1 플래그 (안 PCIE2)와 PCINT1_vect (안 PCINT2)가 필요합니다
microtherion

왜 PCIE1인가? C 레지스터를 사용하고 있는데 카운트하면 A (PCIE0), B (PCIE1), C (PCIE2)입니까? 어쨌든 PCIE1 nad PCINT1_vect로 시도했지만 버튼을 눌러도 반응이 없습니다.
echox

1
그러한 과제에서 직교성을 가정하는 것은 약간 위험 할 수 있습니다. 이 특별한 경우에, 당신은 ATmega168에 포트 A가 없다는 것을 제외하고는 거의 정확할 것입니다. 어쨌든, 나는 데이터 시트와 핀아웃을 갔다. 또 다른 팁은 PCIE2를 사용하고 있지만 PCMSK1에서 비트를 설정한다는 것입니다. (아쉽게도 수정 한 스케치가 여전히 작동하지 않는 이유를 모르겠습니다)
microtherion

고맙게도, 자체 빌드 하드웨어에 의존하는 디버깅 소프트웨어의 조합이 쉽지 않다는 것을 이해합니다 ;-)
echox

답변:


14

핀 변경 인터럽트는 일반적으로 버튼 동작을 감지하는 좋은 방법이 아닙니다. 기계식 버튼이 튀기 때문에 의미없는 인터럽트가 많이 발생하기 때문에 어쨌든 수신 거부해야합니다.

더 좋은 방법은 1ms (1kHz 속도)마다 주기적으로 인터럽트하는 것입니다. 대부분의 프로세서에서 오랜 시간이 걸리므로 인터럽트에 소요되는 시간은 아주 짧습니다. 인터럽트 할 때마다 버튼 상태를 간단히 샘플링하십시오. 새 상태를 50ms 연속으로 본 경우 새 단추 상태를 선언하십시오. 50ms는 대부분의 버튼 바운스보다 길지만 여전히 짧아서 인간이 시차에 대해 신경 쓰거나 신경 쓰지 않습니다.

이 방법으로 동일한주기 1ms 인터럽트에서 여러 버튼을 처리 할 수도 있습니다. 각 버튼마다 하나의 카운터 만 있으면됩니다.

디 바운스 시간에 대한 추가 정보 :

때때로,이 경우와 같이 누군가 누군가 50ms가 너무 긴 디 바운스 시간이라고 말합니다. 사람이 누르는 일반 버튼에는 해당되지 않습니다. 스톱워치와 같이 타이밍이 매우 중요한 응용 프로그램에서는 문제가 될 수 있지만 지금까지는 실행되지 않았습니다. 나는 1980 년대 초에 이것을 테스트했으며 다른 많은 사람들도 있습니다.

일반적인 푸시 버튼 바운스 시간은 약 10ms이며, 거의 모두 25ms 정도 안정됩니다. 디 바운스 시간에 대한 제한 요소는 인간의 인식입니다. 50ms는 사람들이 그것을 찾지 않을 때 지연을 느끼기 시작하는 것보다 약간 짧습니다. 그럼에도 불구하고 성가신 시간이 훨씬 오래 걸립니다. 인간 이 구체적으로 찾고 있다면 50ms와 0ms 지연 사이의 차이를 감지하는 것이 가능할 수도 있지만, 버튼을 누르고 무언가가 발생하고 지연에 대해 생각하지 않는 것과는 상당히 다릅니다.

따라서 지연이 일반 애플리케이션에서 인식 한계 미만, 성가신 한계 미만, 대부분의 스위치 반송 시간보다 훨씬 길기 때문에 50ms는 좋은 디 바운스 시간입니다. 나는 거의 오랫동안 바운스 된 스위치를 발견 했으므로 느슨 할 것이 없기 때문에 인식 한계로 넘어갈 수 있습니다.

50ms 디 바운스 시간을 사용하여 펌웨어 디 바운스 버튼으로 많은 제품을 수행했습니다. 한 번도 고객이 지연에 대해 언급 한 적이 없었습니다. 그들은 모두 버튼을 문제없이 잘 작동한다고 받아 들였습니다.


1
어떤 경우에는 50ms가 너무 길 수 있지만 (보통 10-20ms는 인간의 인식의 한계이며, 수신 거부에 충분해야 함) 여기에 설명 된 방법은 갈 길입니다.
Laszlo Valko

1
@Laszlo : 아니요, 보통의 경우 50ms가 너무 길지 않습니다. 내 답변 이외에도 참조하십시오.
Olin Lathrop

나는 나를 위해 잘 작동하는 50ms를 시도했다 :-) 왜 핀 변경 인터럽트가 (튀는 것 옆에) 작동하지 않는지 여전히 궁금하지만, 이것이 작동합니다 :-) 감사합니다.
echox

1

핀 변경 인터럽트는 폴링보다 더 좋은 방법입니다. 인터럽트는 일반적으로 D-Flip Flop 또는 D-Latch와 같은 일부 로직을 거치게됩니다. 이것이 사실이지만 더 높은 수준의 컴파일러로이 디 바운스 루틴을 구현하는 것이 더 어렵습니다. 인터럽트가 발생하면 인터럽트 플래그가 지워지지 않고 지연이 발생할 때까지 인터럽트 활성화가 해제됩니다. 지연이 발생하면 핀의 상태가 점검되고 여전히 인터럽트를 트리거 한 주어진 상태에있는 경우 버튼의 상태가 변경되고 인터럽트 플래그가 지워지고 인터럽트 활성화가 설정됩니다. 시작을 유발 한 상태가 아닌 경우 인터럽트 활성화가 설정되고 상태는 동일하게 유지됩니다. 이것은 다른 작업을 위해 프로세서를 비 웁니다. 프로그램에서 주기적으로 낭비 시간이 중단됩니다.


-1

"핀 변경 인터럽트는 일반적으로 버튼 동작을 감지하는 좋은 방법이 아닙니다."

잘못된. PC INT가 가장 좋은 옵션입니다. 폴링을 사용하여 버튼의 상태를 확인하면 대부분의 시간 동안 아무것도 수행되지 않습니다. 귀중한 CPU 시간을 낭비합니다. PC INT를 사용하면 요청시에만 작업을 수행 할 수 있습니다.

"기계식 버튼이 튀기 때문에 의미없는 인터럽트가 많이 발생하기 때문에 어쨌든 수신 거부해야합니다."

수신 거부에 대한 정보입니다. 그러나 인터럽트 루틴 내부에서 버튼 / 스위치를 절대로 디 바운스해서는 안됩니다 (동일한 이유 : CPU 시간 낭비). ISR은 코드 단위로 짧고 효율적입니다. 하드웨어 디 바운싱을 사용하십시오. 소프트웨어를 깨끗하게 유지하십시오!

하드웨어 디 바운싱이 더 편리합니다. 참조 / RC 디 바운싱 + 슈미트 트리거 를 참조하십시오. 나는 PC INT와 함께 무수히 많은 시간 동안 그것을 사용했지만 결코 실패하지 않았습니다.

예, PC INT를 사용하여 버튼 상태를 얻을 수 있습니다. 그러나 적절한 하드웨어 디 바운싱을 사용해야합니다.


2
소프트웨어 디 바운싱은 유효한 접근 방식이며 대부분의 추가 CPU 오버 헤드는 중요하지 않습니다. 일반적으로 하드웨어에서 디 바운스해야한다고 말하는 것이 가장 의심 스럽다. 모든 경우에 하드웨어 디 바운싱을 사용해야한다고 말하는 것은 잘못되었습니다.
Olin Lathrop

대부분의 응용 프로그램에서 컨트롤러는 대부분의 시간 동안 유휴 상태로 실행되며 메인 루프를 실행합니다. 또한 IO 상태 확인을 수행하고 잠재적으로 변수를 증가시키는 데 필요한 CPU 시간은 최소입니다. 소프트웨어에서 간단한 디 바운싱을 구현하면 하드웨어 비용이 "무료"가됩니다. 그리고 몇 센트 만 웃으면 서 조립 비용도 들지 않으며, 중간에서 많은 양의 제품을 운영한다면 무시할 수 없습니다. ISR 시간이 짧아야한다는 것은 사실이지만,이 경우에는 거의 논쟁의 여지가 없습니다. 수신 거부로 인해 PC INT ISR이 연속으로 50 번 발사되면 더 중요합니다.
Rev1.0

@Nelson, 'CPU 시간 낭비'는 일부 응용 프로그램에서는 중요하지만 다른 응용 프로그램에서는 중요하지 않습니다. CPU 시간이 중요한 상황에 대한 답변을 받아야합니다.
user1139880
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.