Journey to CS/임베디드

[ATmega328P] 레지스터 직접 제어 방식으로 프로그래밍 해보기

Cordilog 2025. 9. 29. 00:42

이번 포스팅에서는 ATmega328P 마이크로컨트롤러를 레지스터 직접 제어 방식으로 프로그래밍하는 방법에 대해서 알아본다. 

레지스터 방식은 Arduino IDE에서 사용하는 digitalWrite() 같은 함수 호출 방식보다 더 효율적이고 하드웨어에 가깝게 제어할 수 있다.

 

💡 레지스터란?

레지스터는 마이크로컨트롤러 내부에 있는 매우 빠른 임시 저장 공간이다.

ATmega328P에서는 특정 기능을 제어하기 위해 여러 레지스터를 사용한다.

아래 예시 프로그램에서는 포트의 입출력 방향을 설정하는 DDRB 레지스터와, 포트의 출력 상태를 제어하는 PORTB 레지스터를 사용한다.

 

예시: ATmega328P의 PB5 핀에 연결된 LED를 1초 간격으로 깜빡이는 프로그램

#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>

int main(void) {
    // 1) PB5(=D13)를 출력으로 설정
    DDRB |= (1 << DDB5);

    while (1) {
        // 2) LED ON (PB5 = HIGH)
        PORTB |=  (1 << PORTB5);
        _delay_ms(1000);

        // 3) LED OFF (PB5 = LOW)
        PORTB &= ~(1 << PORTB5);
        _delay_ms(1000);
    }
}

 

헤더 파일: 

 

  • #define F_CPU 16000000UL: 마이크로컨트롤러의 클럭 주파수를 16MHz로 설정하는 부분. 이는 _delay_ms() 함수가 정확한 지연 시간을 계산하는 데 필요함.
  • #include <avr/io.h>: ATmega328P의 레지스터 정의를 포함하는 헤더 파일. 이 파일을 포함해야 DDRB나 PORTB 같은 레지스터 이름을 사용할 수 있음.
  • #include <util/delay.h>: _delay_ms() 함수를 사용하기 위해 필요한 헤더 파일.

🛠️ 레지스터 제어 방식

1) 핀 모드 설정: PB5 핀을 출력 모드로 설정

DDRB |= (1 << DDB5);

 

 

DDRB: Data Direction Register for Port B의 약자로, 포트 B의 각 핀을 입력 또는 출력으로 설정하는 레지스터

 

  • (1 << DDB5): **비트 이동(shift) 연산. DDB5는 PB5 핀에 해당하는 비트 위치를 나타낸다. 이 연산은 5번째 비트를 1로 만들어준다.
  • |=: 비트 OR 연산. DDRB 레지스터의 기존 값에 (1 << DDB5) 값을 OR 연산함. 이렇게 하면 다른 핀의 설정은 그대로 둔 채, PB5 핀에 해당하는 비트만 1로 바꿔준다. DDRB 레지스터의 5번 비트가 1이 되면, PB5 핀은 출력 모드가 된다.

**비트 이동 연산 원리

1 << DDB5 연산은 1이라는 값의 비트를 DDB5만큼 왼쪽으로 옮기는 것을 의미한다.

DDB5는 ATmega328P 마이크로컨트롤러에서 PB5 핀의 위치를 나타내는 미리 정의된 상수로, 이 상수의 값은 5이다.

즉, DDB5는 5번 비트를 의미한다.

따라서 1 << DDB51 << 5와 같다.

 

1이라는 숫자를 8비트 이진수로 표현하면 0000 0001이 된다.

 

여기서 1을 왼쪽으로 5칸 옮겨보면,

  1. 0000 0001 (10진수 1)
  2. 0000 0010 (1칸 이동)
  3. 0000 0100 (2칸 이동)
  4. 0000 1000 (3칸 이동)
  5. 0001 0000 (4칸 이동)
  6. 0010 0000 (5칸 이동)

 

 

2) LED ON: PB5 핀에 HIGH 신호 내보내기

PORTB |= (1 << PORTB5);

 

PORTB: Port B Data Register의 약자로, 포트 B의 출력 상태를 제어하는 레지스터

 

  • (1 << PORTB5): PORTB5는 PB5 핀에 해당하는 비트 위치. 이 연산은 5번째 비트를 1로 만들어.
  • |=: 위와 마찬가지로 비트 OR 연산. PORTB 레지스터의 기존 값에 (1 << PORTB5)를 OR 연산하여 PB5 핀만 HIGH 상태로 만든다. 다른 핀의 상태는 변하지 않는다.

 

3) LED OFF: PB5 핀에 LOW 신호 내보내기

PORTB &= ~(1 << PORTB5);

 

  • (1 << PORTB5): 위와 마찬가지로 5번째 비트를 1로 만든다.
  • ~: NOT 연산. ~(1 << PORTB5)는 모든 비트를 반전시킨다. (ex. 00100000가 11011111로 바뀜)
  • &=: 비트 AND 연산. PORTB 레지스터의 기존 값에 ~(1 << PORTB5)를 AND 연산함. 이렇게 하면 다른 비트는 그대로 두고, PB5 핀에 해당하는 5번 비트만 0으로 만든다. 5번 비트가 0이 되면, PB5 핀은 LOW 상태가 된다.

이처럼 레지스터를 직접 제어하는 방식은 비트 연산을 통해 핀 단위로 정교하게 제어할 수 있다.