Raspberry Pi/Raspberry Pi 활용

라즈베리파이 5 GPIO LED 제어를 위한 리눅스 디바이스 드라이버 만들어보기

webnautes 2025. 1. 16. 23:14
반응형

클로드를 사용하여 라즈베리파이 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: 제거 완료




반응형