51 단편기 외부 인터럽트를 활용한 이벤트 카운터 설계 및 구현

51 단편기(8051 계열 MCU)에서 외부 인터럽트는 실시간으로 발생하는 외부 하드웨어 이벤트를 감지하고 처리하는 데 필수적인 기능입니다. 특히 버튼 입력이나 센서 신호를 계수하는 시스템에서 인터럽트를 사용하면 CPU가 다른 작업을 수행하는 도중에도 신호를 놓치지 않고 정확하게 포착할 수 있습니다.

1. 51 단편기 인터럽트 시스템 구조

51 단편기는 기본적으로 5개의 인터럽트 소스를 제공합니다. 외부 인터럽트 0(INT0), 타이머 0, 외부 인터럽트 1(INT1), 타이머 1, 그리고 직렬 포트(UART) 인터럽트입니다. 각 인터럽트는 고유의 벡터 주소를 가지며, 인터럽트 발생 시 하드웨어적으로 해당 주소로 점프하여 서비스 루틴(ISR)을 실행합니다.
인터럽트 소스 벡터 주소 인터럽트 번호
외부 인터럽트 0 (INT0) 0003H 0
타이머 0 인터럽트 000BH 1
외부 인터럽트 1 (INT1) 0013H 2
타이머 1 인터럽트 001BH 3

2. 외부 인터럽트 트리거 모드 설정

외부 인터럽트 0(P3.2)과 1(P3.3)은 두 가지 트리거 방식을 지원하며, 이는 TCON 레지스터의 IT0 및 IT1 비트를 통해 설정합니다.
  • 저레벨 트리거(Level-triggered, ITx=0): 핀의 전위가 낮은 상태(Low)를 유지하는 동안 계속 인터럽트를 요청합니다. 신호가 길게 유지될 경우 중복 실행될 위험이 있습니다.
  • 하강 에지 트리거(Edge-triggered, ITx=1): 핀의 전위가 고전위(High)에서 저전위(Low)로 변하는 순간 한 번만 인식합니다. 버튼 카운팅과 같은 이벤트 캡처에 가장 적합합니다.
// INT0을 하강 에지 트리거로 설정하는 예시
TCON |= 0x01; // IT0 = 1 설정

3. 인터럽트 허용 및 우선순위 제어

인터럽트 시스템을 활성화하려면 IE(Interrupt Enable) 레지스터를 설정해야 합니다. 또한, 여러 인터럽트가 동시에 발생할 경우를 대비해 IP(Interrupt Priority) 레지스터로 우선순위를 결정할 수 있습니다.

IE 레지스터 주요 비트

  • EA (EA.7): 전역 인터럽트 허용 비트. 이 비트가 1이어야 모든 인터럽트가 작동합니다.
  • EX0 (IE.0): 외부 인터럽트 0 허용 비트.
  • EX1 (IE.2): 외부 인터럽트 1 허용 비트.

IP 레지스터 설정

PX0(IP.0) 또는 PX1(IP.2) 비트를 1로 설정하면 해당 외부 인터럽트가 고우선순위(High Priority)를 갖게 되어 다른 저우선순위 인터럽트 수행 중에도 즉시 실행될 수 있습니다.

4. C언어 기반 인터럽트 서비스 루틴(ISR) 작성

Keil C51 컴파일러에서는 `interrupt` 키워드를 사용하여 ISR을 정의합니다. 인터럽트 함수는 반환값이 없어야 하며 매개변수를 가질 수 없습니다.
unsigned int g_event_total = 0; // 전역 카운터 변수

void External_INT0_Handler(void) interrupt 0
{
    // 인터럽트 발생 시 실행될 로직
    g_event_total++; 
    if (g_event_total > 99) {
        g_event_total = 0; // 2자리수 제한 시 초기화
    }
}
이때 변수 `g_event_total`은 메인 루프와 ISR에서 공유되므로, 컴파일러 최적화로 인한 오류를 방지하기 위해 `volatile` 키워드를 붙여 선언하는 것이 권장됩니다.

5. 하드웨어 채터링 방지(Debouncing)

기계식 버튼을 사용하여 인터럽트를 발생시킬 때 발생하는 전기적 노이즈(채터링)는 다중 인터럽트 호출의 원인이 됩니다. 이를 해결하기 위해 하드웨어적으로 RC 필터를 구성하거나, 인터럽트 발생 직후 소프트웨어적인 짧은 지연(Delay)을 주어 오작동을 막아야 합니다.

6. 실전 프로젝트 코드: 7세그먼트 카운터

아래 코드는 INT0 인터럽트가 발생할 때마다 카운트를 증가시키고 이를 7세그먼트에 표시하는 전체적인 로직 구조입니다.
#include <reg52.h>

typedef unsigned char u8;
typedef unsigned int u16;

// FND 공통 음극(Common Cathode) 코드 표
u8 code FND_TABLE[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};

volatile u16 v_push_count = 0;

void Init_System()
{
    IT0 = 1;  // 하강 에지 트리거 설정
    EX0 = 1;  // 외부 인터럽트 0 활성화
    EA = 1;   // 전체 인터럽트 허용
}

void main()
{
    Init_System();
    
    while(1)
    {
        // 메인 루프에서는 디스플레이 스캔 로직 수행
        P0 = FND_TABLE[v_push_count % 10]; // 1의 자리 표시
        // (멀티플렉싱 제어 코드 생략)
    }
}

void External0_ISR(void) interrupt 0
{
    // 버튼 눌림 감지 시 카운트 증가
    v_push_count++;
}
해당 코드를 실행하면 하드웨어적으로 P3.2 핀에 연결된 버튼이 눌릴 때마다 `External0_ISR`이 즉시 실행되어 `v_push_count` 값이 안정적으로 업데이트됩니다. 메인 루프는 디스플레이 갱신에 집중할 수 있어 시스템 자원을 효율적으로 배분할 수 있습니다.

태그: 8051 Embedded-C Interrupts Microcontroller Firmware

6월 13일 21:12에 게시됨