행복한 하루
Raspberry Pi – Linux Device Driver 만들어 보기-11 (인터럽트를 이용한 버튼 - interrupt button) 본문
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
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