라즈베리파이 4 (BCM2711)에서 메모리 맵 I/O로 GPIO를 직접 제어해보는 예시 일부를 요약해서 필기해보았다.

메모리 맵 I/O (Memory-Mapped I/O)

개념:
CPU의 주소 공간에 메모리뿐 아니라 I/O 장치 레지스터(GPIO, UART, SPI 등)를 같은 주소 공간에 배치하는 방식.
→ CPU는 메모리에 접근하는 것과 동일한 방식으로 장치를 제어할 수 있음.
전체 메모리 공간을 메모리와 I/O 장치로 나눠서 사용

버스 요약

Address bus: CPU는 값을 가져오기 위해 접근할 메모리/장치 주소를 어드레스 버스를 통해 설정

Data bus: 데이터버스를 통해 값을 읽어옴.

Control bus: 읽기/쓰기 동작 제어 (입출력 방향 설정)

어드레스 버스를 통해 GPIO와 같은 H/W에 접근, 메모리 특정 주소를 사용해 GPIO에 값을 보내거나, 들어오는 값을 읽는다.

mmap()으로 라즈베리파이 led 제어해보기

bcm2711의 메모리 주소 맵핑

  1. BCM2711의 GPIO주소

GPIO base 주소: 0x7E200000 (I/O base + 0x200000)

  1. GPIO 주요 레지스터


GPFSELn (GPIO Function Select) : 각 GPIO 핀의 입출력 모드 설정 (핀당 3비트)

GPSETn : GPIO 출력 High (1로 설정)

GPCLRn : GPIO 출력 Low (0으로 클리어)

GPLEVn : 현재 입력 레벨 확인

  1. GPIO 방향 설정 (GPFSEL)

핀 하나당 3비트씩 할당하여 I/O 방향을 설정.

입력 : 000, 출력 : 001
특정 핀 번호를 GPFSEL 레지스터에서 찾는 방법:

총 0~5번으로 6개의 GPFSEL레지스터. gpio핀은 58개,
핀번호 / 10 → 사용할 GPFSEL 레지스터 선택

핀번호 % 10 → 그 레지스터 내에서의 비트 위치

mmap()

Memory-mapped I/O: I/O 장치를 메모리 주소 공간에 포함시켜 접근
Raspberry Pi 4 (BCM2711): GPIO base는 0xFE200000
직접 제어: /dev/mem → mmap()으로 매핑 → GPIO 레지스터 접근 → 토글
이제 mmap()을 알아보고 직접 제어를 해볼 것이다.

#include <sys/mman.h>

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
int munmap(void *addr, size_t length);

(사물인터넷을 위한 리눅스 프로그래밍 with 라즈베리파이)

mmap() : fd가 가리키는 객체를 offset의 값을 기준으로 length 만큼 메모리에 매핑하도록 커널에 요청하면 매핑된 시작 주소가 반환된다.

munmap() : mmap()함수와 연결된 메모리 해제.

주요 코드 흐름

/dev/mem → mmap()으로 매핑 → GPIO 레지스터 접근 → 토글

  1. /dev/mem 열기커널이 관리하는 물리 메모리 공간에 접근하기 위해 /dev/mem 사용
  2. mem_fd = open("/dev/mem", O_RDWR | O_SYNC);
  3. mmap()으로 물리 주소를 가상 주소에 매핑GPIO 레지스터 주소 영역을 프로세스의 가상주소에 매핑
  4. gpio_map = mmap(NULL, GPIO_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, GPIO_BASE);
  5. GPIO 포인터 설정이제 gpio[]배열처럼 접근하면 실제 레지스터에 읽기, 쓰기 가능하다.
  6. gpio= (volatile unsigned *)gpio_map;
  1. GPIO를 출력으로 설정 하는예
GPIO\_OUT(gno); 

매크로로 해당 핀을 출력 모드(001)로 설정.

  1. LED 토글

    GPIO_SET(gno) //high
    sleep(1);
    GPIO_CLR(gno) //Low
    sleep(1);

    1초 간격으로 GPIO핀을 on/off하는 예.

  2. 리소스 해제!

    munmap(gpio_map, GPIO_SIZE);
    close(mem_fd);

+ Recent posts