본문 바로가기
Atmega328p

{Atmega328p}-페리페럴 제어 GPIO , 레지스터 구조체

by syyy1 2025. 10. 22.

GPIO 정의 General Purpose Input/Output “범용 입출력 핀
— MCU(마이크로컨트롤러)가 외부 장치와 데이터를 주고받기 위해 쓰는 다목적 핀

 

 

ATmega328P(아두이노 UNO의 MCU)

3개의 8비트 포트를 가짐
PORTB (8bit), PORTC (8bit), PORTD (8bit)

PORTB Port B — I/O 포트 그룹 8비트 (PB0~PB7) SPI, PWM, Digital 핀 8~13
PORTC Port C — I/O 포트 그룹 7비트 (PC0~PC6) 아날로그 입력 A0~A5
PORTD Port D — I/O 포트 그룹 8비트 (PD0~PD7) Digital 핀 0~7, UART

 

 

I/O Switch  입력의 상태

I

아날로그 > 디지털로 변환해야한다 

여기서 주변 전기장에 의해 플로팅 발생으로 인해서 신호가 잘못 인식됨

따라서 풀업 풀다운으로 붙잡아준다. 

사진에서는 0.8~2 V 가 플로팅 구간

 

Pull up 저항

저항이 VCC와 입력 사이에 존재

Pull Down

저항이 GND와 INPUT 사이에 존재

 

 

DataSheet IO Atmega328p 내부 구조

VCC와 입력 사이에 저항 존재 > 풀업 저항 존재

표에 나와있는 레지스터 조건으로 동작시키면 Pull up 을 사용하게된다.

이름   레지스터   역할
DDxn DDRx (Data Direction Register) 0이면 Input, 1이면 Output
PORTxn PORTx Output일 땐 출력값 제어, Input일 땐 풀업 제어
PUD MCUCR(SFIOR)에 있음 Pull-up Disable (1이면 풀업 전체 비활성화)

 

표에 따르면

레지스터 설정값  의미
DDRx = 0 입력 모드 핀을 Input으로 설정
PORTx = 1 풀업 ON 내부 풀업 저항 연결
PUD = 0 풀업 기능 사용 가능 MCU 전체의 풀업 기능이 켜져 있음

 

 

레지스터 초기화

Clock은 MCU의 속도를 조절하고,
Register는 Peripheral(주변장치)을 제어한다.

 

  • Clock → “MCU가 얼마나 빠르게 동작할지”를 결정하는 타이밍 신호
  • Register → “MCU 안의 장치(주변장치)”들을 실제로 제어하는 조작

Register 의 3가지

Control : 초기 설정 역할을 하는 레지스터 (설정 메뉴)

Status : 변화에 따른 상태 확인 레지스터 (전송 상태 , 동작 상태 등)

Data : 읽거나 쓸 수 있는 데이터 레지스 (우편함 같이 값)

 

 

Register 예시

 

ex) R/W 인 PUD를 1로 바꾸면 Pull up 안쓰겠다 라고 설정 가능


GPIO 는 status 존재하지 않음

UART 같은 경우 송수신 완료 상태 플래그 레지스터 존재

 

⚙️ Peripheral(주변장치)의 역할 # 참고

분야 주변장치 예시 설명
단순 입출력 (I/O) 🔸 GPIO LED ON/OFF, 스위치 입력 등 “단순 전압 입출력”
통신 (Communication) 🔸 UART, SPI, I2C 센서, 다른 MCU, PC 등과 “데이터 통신”
시간 제어 (Timing) 🔸 TIMER, PWM 주기적 인터럽트, 모터 제어, 신호 생성
아날로그 처리 (Analog) 🔸 ADC, DAC 센서 값(전압)을 디지털 값으로 변환
전원/인터럽트 제어 🔸 WDT, INT 예외 상황 대응, 슬립모드 제어

 


Data Register

 

 

PORTx = 데이터 출력 레지스터(Data Register) -> Data register

Data Register 예시

 

DDRx = Data Direction Register (데이터 방향 레지스터) -> Control register 

1로 두면 출력으로 쓰고 0으로 두면 입력

 

PINx = Input Pins Register (입력 핀 레지스터) -> Data register

PIN address

PORTB는 “입력용”으로 직접 값을 읽는 게 아니라, “입력 시 풀업저항 설정용”으로만 쓸 수 있고,
값을 읽을 때는 반드시 PINB를 사용해야 합니다.

※ PIN은 입력용? PORT 는 출력용? 이건 이후에 코드를 보면서 알아나가자 (확답 x 맞는 거 같음)

사용 예시

 

예제 Led 불켜기

 

/*
 * Blink_D7_328P.c
 *
 * Created: 2025-10-19 오후 7:14:12
 * Author : 김동하
 */ 

#define F_CPU 16000000UL // CPU 클럭을 16Mhz 선언
#include <avr/io.h> // 래지스터 라이브러리
#include <util/delay.h> // delay 함수

int main(void)
{
	// D5 = PD5 를 출력으로 설정
	DDRB |= (1 << DDB5); // DDRB 포트 페리페럴을 (8비트)
	
	while (1)
	{
		// LED ON (PD7 High)
		PORTB |= (1 << PORTB5);
		_delay_ms(1100);

		// LED OFF (PD7 Low)
		PORTB &= ~(1 << PORTB5);
		_delay_ms(1100);
	}
}

 

DDRB 

DDRB

PORTB

PORTB

# 궁금점 PB5랑 PORTB5 랑 뭐가 다름

결론 : avr/io.h 에 정의되어 있는 읽는 사람 입장에서 의미를 명확히 표현한 정수

이 숫자가 어느 레지스터에서 사용되는 비트인지 명확하게 구별하기 위해

정수 물리 핀 DDRB PORTB PINB
0 PB0 DDB0 PORTB0 PINB0
1 PB1 DDB1 PORTB1 PINB1
2 PB2 DDB2 PORTB2 PINB2
3 PB3 DDB3 PORTB3 PINB3
4 PB4 DDB4 PORTB4 PINB4
5 PB5 DDB5 PORTB5 PINB5

 

 

※구조체로 직접 Register 만들기

 

매크로 처리된 변수를 사용하지 않고 직접 레지스터 주소를 지정하는 구조체를 만들어서 구현해보자

 

목적 : 사진의 레지스터 부분만을 접근하기 위한 레지스터 구조체를 직접 구성해보자

데이터 시트 레지스터

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

#define PORTB_REG 0x23   // PINB 레지스터 시작 주소

struct io_port
{
    unsigned char pin;   // 0x23 (PINB)
    uint8_t ddr;         // 0x24 (DDRB)
    uint8_t port;        // 0x25 (PORTB)
};

int main(void)
{
    struct io_port *portB = (void *)PORTB_REG;

    portB->ddr = 0x20;   // DDRB = 0x20 → PB5 출력 설정
    // 0x20 => 0010 0000

    while (1)
    {
        portB->port = 0x20;  // PORTB = 0x20 → PB5 HIGH (LED ON)
        _delay_ms(1000);

        portB->port = 0x00;  // PORTB = 0x00 → PB5 LOW (LED OFF)
        _delay_ms(1000);
    }
}

직접 구조체를 만들어서 그 구조체의 메모리값을 데이터 시트에 맞게 지정해서 조작해도 된다

 

C언어 문법 #궁금점 : 특정 메모리 주소를 키리키게 만들기 위함

(void*)의 의미 — “주소로 인식시켜라”

(void*)는 “아무 타입도 없는 포인터(주소)” 이건 어떤 타입인지 모르겠지만 주소임”이라고 컴파일러에게 알려주는 것

즉 그 구조체가 0x23에 존재한다는 걸 알려주는 코드

struct io_port *portB = (void*)0x23;

 

코드 의미
struct io_port portB; 구조체 변수 하나를 RAM에 새로 만든다. (일반적인 변수 생성)
struct io_port *portB = (void*)0x23; 구조체 포인터를 만들고, 그 포인터가 특정 ‘메모리 주소(0x23)’를 가리키게 한다. (기존 하드웨어 주소에 직접 접근)

'Atmega328p' 카테고리의 다른 글

{Atmega328p}-Timer , Counter  (0) 2025.10.29
{Atmega328p}-ADC  (0) 2025.10.26
{Atmega328p}-ISR  (0) 2025.10.26
{Atmega328p}-UART 통신  (0) 2025.10.24
{임베디드}-임베디드 시스템  (0) 2025.10.21