라즈베리파이 5 GPIO LED 제어를 위한 리눅스 디바이스 드라이버 만들어보기
클로드를 사용하여 라즈베리파이 5에서 LED 켜고 끄는 디바이스 드라이버 만드는 방법을 조사후 진행해봤습니다.
2025. 1. 16 최초작성
원래는 다음처럼 연결해야 하지만, 편의상 저항은 빼고 연결했습니다.
GPIO 18 (PIN 12) → 220Ω 저항 → LED 긴 다리 → LED 짧은 다리 → GND
이미지 출처 - https://www.reddit.com/r/raspberry_pi/comments/18y24yr/powering_raspberry_pi_5_with_gpio/
필요한 패키지를 설치합니다.
$ sudo apt update
$ sudo apt install raspberrypi-kernel-headers build-essential
코드를 저장할 디렉토리를 만들고 그 안에 소스 코드를 저장합니다.
pi@raspberrypi:~ $ mkdir led_driver
pi@raspberrypi:~ $ cd led_driver/
pi@raspberrypi:~/led_driver $ nano led_driver.c
다음 코드를 led_driver.c에 붙여넣고 Ctrl + O눌러 저장하고 Ctrl + X를 눌러 nano에서 빠져나옵니다.
GPIO 18번 핀을 사용하도록 코드가 작성되어 있습니다. 주의할점은 GPIO 18번에 대해 내부 핀 번호 589를 사용해야 합니다.
참고 https://blaxsior-repository.tistory.com/294
다음 명령으로 내부 핀번호 확인이 가능합니다.
$ cat /sys/kernel/debug/gpio
#include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/gpio.h> #include <linux/kobject.h> #include <linux/sysfs.h> #define LED_GPIO 589 static struct kobject *led_kobj; static int led_value = 0; // sysfs에서 LED 상태 읽기 static ssize_t led_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { return sprintf(buf, "%d\n", led_value); } // sysfs를 통해 LED 제어 static ssize_t led_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { int ret; long temp; ret = kstrtol(buf, 10, &temp); if (ret) { printk(KERN_ERR "LED Driver: Invalid input value\n"); return ret; } if (temp != 0 && temp != 1) { printk(KERN_ERR "LED Driver: Value must be 0 or 1\n"); return -EINVAL; } led_value = temp; gpio_set_value(LED_GPIO, led_value); printk(KERN_INFO "LED Driver: LED set to %d\n", led_value); return count; } static struct kobj_attribute led_attr = __ATTR(value, 0660, led_show, led_store); static int __init led_init(void) { int ret; printk(KERN_INFO "LED Driver: 초기화 시작\n"); // GPIO 요청 ret = gpio_request(LED_GPIO, "LED"); if (ret) { printk(KERN_ERR "LED Driver: GPIO request failed\n"); return ret; } // GPIO 출력으로 설정 ret = gpio_direction_output(LED_GPIO, 0); if (ret) { printk(KERN_ERR "LED Driver: Failed to set GPIO direction\n"); gpio_free(LED_GPIO); return ret; } // sysfs 엔트리 생성 led_kobj = kobject_create_and_add("led_driver", kernel_kobj); if (!led_kobj) { printk(KERN_ERR "LED Driver: Failed to create sysfs entry\n"); gpio_free(LED_GPIO); return -ENOMEM; } // sysfs 파일 생성 ret = sysfs_create_file(led_kobj, &led_attr.attr); if (ret) { printk(KERN_ERR "LED Driver: Failed to create sysfs file\n"); kobject_put(led_kobj); gpio_free(LED_GPIO); return ret; } printk(KERN_INFO "LED Driver: 초기화 완료\n"); return 0; } static void __exit led_exit(void) { printk(KERN_INFO "LED Driver: 제거 시작\n"); if (led_kobj) { sysfs_remove_file(led_kobj, &led_attr.attr); kobject_put(led_kobj); } gpio_set_value(LED_GPIO, 0); // LED 끄기 gpio_free(LED_GPIO); printk(KERN_INFO "LED Driver: 제거 완료\n"); } module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("A simple LED driver for Raspberry Pi 5"); |
Makefile을 생성하여 다음 코드를 붙여넣고 Ctrl + O눌러 저장하고 Ctrl + X를 눌러 nano에서 빠져나옵니다.
pi@raspberrypi:~/led_driver $ nano Makefile
obj-m := led_driver.o KERNELDIR ?= /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules clean: $(MAKE) -C $(KERNELDIR) M=$(PWD) clean |
이제 모듈을 빌드합니다. 코드 수정해도 절대 make clean을 하지 마세요. 커널 헤더를 지워버려서 문제가 생깁니다.
pi@raspberrypi:~/led_driver $ make
make -C /lib/modules/6.6.62+rpt-rpi-2712/build M=/home/pi/led_driver modules
make[1]: Entering directory '/usr/src/linux-headers-6.6.62+rpt-rpi-2712'
CC [M] /home/pi/led_driver/led_driver.o
MODPOST /home/pi/led_driver/Module.symvers
CC [M] /home/pi/led_driver/led_driver.mod.o
LD [M] /home/pi/led_driver/led_driver.ko
make[1]: Leaving directory '/usr/src/linux-headers-6.6.62+rpt-rpi-2712'
모듈을 로드합니다.
pi@raspberrypi:~/led_driver $ sudo insmod led_driver.ko
커널 로그를 확인할 수 있습니다.
pi@raspberrypi:~/led_driver $ dmesg | tail
[ 4.208238] macb 1f00100000.ethernet: gem-ptp-timer ptp clock registered.
[ 4.216415] brcmfmac: brcmf_cfg80211_set_power_mgmt: power save enabled
[ 8.217902] Bluetooth: RFCOMM TTY layer initialized
[ 8.217912] Bluetooth: RFCOMM socket layer initialized
[ 8.217922] Bluetooth: RFCOMM ver 1.11
[ 332.746253] led_driver: loading out-of-tree module taints kernel.
[ 391.111387] LED Driver: 초기화 시작
[ 391.111416] LED Driver: 초기화 완료
권한을 설정합니다.
pi@raspberrypi:~/led_driver $ sudo chmod 666 /sys/kernel/led_driver/value
LED가 켜집니다. 상태확인도 가능합니다.
pi@raspberrypi:~/led_driver $ echo 1 > /sys/kernel/led_driver/value
pi@raspberrypi:~/led_driver $ cat /sys/kernel/led_driver/value
1
LED가 꺼집니다. 상태확인도 가능합니다.
pi@raspberrypi:~/led_driver $ echo 0 > /sys/kernel/led_driver/value
pi@raspberrypi:~/led_driver $ cat /sys/kernel/led_driver/value
0
pi@raspberrypi:~/led_driver $
커널 로그를 확인합니다.
pi@raspberrypi:~/led_driver $ dmesg | tail
[ 391.111387] LED Driver: 초기화 시작
[ 391.111416] LED Driver: 초기화 완료
[ 553.606858] LED Driver: LED set to 1
[ 606.958806] LED Driver: LED set to 0
드라이버를 제거합니다.
pi@raspberrypi:~/led_driver $ sudo rmmod led_driver.ko
커널 로그를 확인합니다.
pi@raspberrypi:~/led_driver $ dmesg | tail
[ 391.111387] LED Driver: 초기화 시작
[ 391.111416] LED Driver: 초기화 완료
[ 553.606858] LED Driver: LED set to 1
[ 606.958806] LED Driver: LED set to 0
[ 635.966072] LED Driver: 제거 시작
[ 635.966092] LED Driver: 제거 완료