행복한 하루

Raspberry Pi – Linux Device Driver 만들어 보기-11 (인터럽트를 이용한 버튼 - interrupt button) 본문

RaspberryPi/DeviceDriver

Raspberry Pi – Linux Device Driver 만들어 보기-11 (인터럽트를 이용한 버튼 - interrupt button)

변화의 물결 2023. 2. 5. 00:03

 

안녕하세요.

 

 이전에 내용에서는 단순 GPIO를 이용한 버튼을 확인해 보았습니다. 이번에는 좀 더 정확하게 처리할 수 있는 Interrupt 신호를 가지고 버튼신호를 확인해 보겠습니다.


1. 사전 준비

1) 하드웨어 준비

- 간단한 Tact Switch를 한 개 준비합니다. 그리고 Raspberry Pi 핀 17번에 한쪽을 연결하고 나머지 한쪽은 3.3V(+) 출력 단자를 연결합니다.

2) 소프트웨어 준비

  - 이전 내용과 유사하게 드라이버 틀이 만들어진 day1 폴더를 복사해서 수정합니다. 

pi@raspberrypi:~/DriverStudy $ cp day1 day11_irq_button -r
pi@raspberrypi:~/DriverStudy $ cd day11_irq_button
pi@raspberrypi:~/DriverStudy $ mv day1_module.c day11_gpio_irq_button.c

  - Makefile을 수정합니다.

 

pi@raspberrypi:~/DriverStudy/day11_irq_button $ vim Makefile

obj-m += day11_gpio_irq_button.o

2. 소스 수정

 - day11_gpio_irq_button.c 파일을 수정합니다. 

pi@raspberrypi:~/DriverStudy/day11_irq_button $ vim day11_gpio_irq_button.c

 

 

 - 인터럽트 처리에 필요한 해더파일과 인터럽트 요청 번호(irq) 등을 추가합니다. 

//(생략)
#include <linux/gpio.h>
#include <linux/interrupt.h>

/* Meta Information */
//(생략)
MODULE_DESCRIPTION("A simple Linux Kernel Module for gpio interrupt");

 

  - 인터럽트가 발생했을 때 동작하는 인터럽트 서비스 함수를 구현합니다. 여기서 함수의 타입은 interrupt.h에 정의되어 있습니다. 여기서는 단순하게 문자열만 출력하도록 하였습니다. 그리고 IRQ_HANDLED로 인터럽트가 처리가 잘 되었다고 리턴해줍니다. 

/**
* @brief Interrupt service routine is called, when interrupt is triggered
*/
static irq_handler_t gpio_irq_handler(unsigned int irq, void *dev_id, struct pt_regs *regs) {
        printk("gpio_irq: Interrupt was triggered and ISR was called!\n");
        return (irq_handler_t) IRQ_HANDLED;
}

  - 모듈 초기화 함수에서 GPIO 핀 할당과 입력 방향으로 설정합니다. 

static int __init ModuleInit(void) {
        printk("qpio_irq: Loading module... ");

        /* Setup the gpio */
        if(gpio_request(GPIO_PIN, "rpi-gpio-17")) {
                printk("Error!\nCan not allocate GPIO %d\n",GPIO_PIN);
                return -1;
        }

        /* Set GPIO PIN direction */
        if(gpio_direction_input(GPIO_PIN)) {
                printk("Error!\nCan not set GPIO %d to input!\n",GPIO_PIN);
                gpio_free(GPIO_PIN);
                return -1;
        }

 

- gpio_to_irq함수를 통해 GPIO 번호에 해당하는 interrupt Address를 얻어옵니다.

  - request_irq 함수를 통해서 interrupt service 함수를 커널에 등록합니다. 이 함수의 첫 번째 인자는 interrupt address, 두 번째는 interrupt 발생 시 실행할 함수 포인터를 넣어줍니다. 위에서 구현 함수 포인터를 넣어줍니다. 세 번째는 interrupt 관련 옵션인데, 여기서는 RISING, 즉 신호가 LOW에서 HIGH로 변경될 때 발생하도록 설정합니다. 네 번째는 소유자 이름을 나타내며 /proc/interrupts에 나타납니다. 다섯 번째는 인터럽트 공유 시에 인터럽트의 구분 ID로 사용되거나 인터럽트 핸들러에 전달될 데이터의 주소 지정에 사용됩니다. 

        /* Setup the interrupt */
        irq_number = gpio_to_irq(GPIO_PIN);

        if(request_irq(irq_number, (irq_handler_t) gpio_irq_handler, IRQF_TRIGGER_RISING, "gpio_irq_button", NULL) != 0){
                printk("Error!\nCan not request interrupt nr.: %d\n", irq_number);
                gpio_free(GPIO_PIN);
                return -1;
        }

        printk("Done!\n");
        printk("GPIO %d is mapped to IRQ Nr.: %d\n",GPIO_PIN, irq_number);
        return 0;
}

  

  - 모듈에서 제거될 때 irq와 gpio핀을 해제합니다. 

static void __exit ModuleExit(void) {
        printk("gpio_irq: Unloading module... ");
        free_irq(irq_number, NULL);
        gpio_free(GPIO_PIN);
}

module_init(ModuleInit);
module_exit(ModuleExit);

 

<참고 – vim에서 수직으로 화면을 두 개로 나눠서 작업>

:vsplit ../day4_gpio/gpio_driver.c 파일을 불러와서 필요한 부분을 복사할 수 있게 활용합니다.

3. 컴파일 및 동작 확인

 - 컴파일하면 드라이버 파일이 생성됩니다. 

pi@raspberrypi:~/DriverStudy/day11_irq_button $ make
pi@raspberrypi:~/DriverStudy/day11_irq_button $ ls
pi@raspberrypi:~/DriverStudy/day11_irq_button $ sudo insmod day11_gpio_irq_button.ko
pi@raspberrypi:~/DriverStudy/day11_irq_button $ dmesg | tail -3

  - 등록된 interrupt를 확인합니다. 

pi@raspberrypi:~/DriverStudy/day11_irq_button $ cat /proc/interrupts

 

 

 - 버튼을 누르면 누르면 커널 메시지가 발생하지만 누른 횟수와 다르게 나타난다는 것을 알 수 있습니다. 이것은 하드웨어적으로 소프트웨어적으로 채터링 현상에 대해 보완하지 않았기 때문입니다. 우리가 한 번만 살짝 누른다고 하지만 전압 차원에서는 LOW, HIGH 상태가 여러 번 발생하기 때문입니다.

 

 - 캐패시터를 추가하거나, 평균, 누르는 시점과 띄는 시점을 파악해서 처리하는 방법 등이 있습니다. 참고할 수 있는 자료를 하단, 참고 사이트 링크에 추가해 두었습니다.   

pi@raspberrypi:~/DriverStudy/day11_irq_button $ dmesg | tail -5

 

- 커널에서 모듈을 제거합니다. 

pi@raspberrypi:~/DriverStudy/day11_irq_button $ sudo rmmod day11_gpio_irq_button

   

감사합니다.

 

 

<참고 사이트>

1. Let's code a Linux Driver - 11: Using GPIO Interrupts in a Linux Kernel Module

https://www.youtube.com/watch?v=oCTNuwO9_FA&list=PLCGpd0Do5-I3b5TtyqeF1UdyD4C-S-dMa&index=12

2. 아두이노 코딩 스위치 채터링과 디바운스 디바운싱 학습

https://blog.naver.com/PostView.naver?blogId=lymcall&logNo=222518641242

3. interrupt.h File Reference

https://docs.huihoo.com/doxygen/linux/kernel/3.7/include_2linux_2interrupt_8h.html#a33561b7274066c2fe0a180a2ae633f5c

4. [BBB] GPIO Interrupt

https://m.blog.naver.com/moony6463/10176934157

5. request_irq() 함수, free_irq() 함수 [출처] request_irq() 함수, free_irq() 함수|작성자 aLONEly

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=manhwamani&logNo=10186246231

day11_irq_button.zip
0.00MB

 

Comments