본문 바로가기
임베디드/KERNEL

라즈베리파이 test_gpio4_irq (button+led) 인터럽트

by sj0020 2021. 1. 7.
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/major.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/version.h>
#include <linux/uaccess.h>
#include <linux/delay.h> //mdelay(10) ->ms, udelay(1) ->us
#include <linux/gpio.h> //커널에서 제공하는 gpio 함수
#include <linux/interrupt.h>

#define __BCM_2711__
/* BCM2837의 I/O Peripherals 주소*/
#if defined(__BCM_2837__)
#define BCM_IO_BASE     0x3F000000
#elif defined (__BCM_2711__)
#define BCM_IO_BASE     0xFE000000
#elif defined(__BCM_2835__)
#define BCM_IO_BASE     0x20000000
#endif

// GPIO 컨트롤러의 주소
#define GPIO_BASE      (BCM_IO_BASE + 0x200000)  
// 0x7E2000B0 - 0x7E200000 +4 = 176 + 4 = 180
#if defined (__BCM_2837__) || defined(__BCM_2835__)
#define GPIO_SIZE       (180)
#elif defined (__BCM_2711__)
#define GPIO_SIZE       (240)
#endif

// GPIO 설정 매크로
#define GPIO_IN(g)      ( *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3)) )
#define GPIO_OUT(g)     ( *(gpio+((g)/10)) |=  (1<<(((g)%10)*3)) )
#define GPIO_SET(g)     (*(gpio+7) = 1<<g)
#define GPIO_CLR(g)     (*(gpio+10) = 1<<g)
#define GPIO_GET(g)     (*(gpio+13) & (1<<g))

#define IOCTRL_LED_ON   1000
#define IOCTRL_LED_OFF   1001
#define IOCTRL_EXIT      1002

#define BUFF_SIZE      1024
#define MAJOR_NUMBER   256
#define DEVICE_NAME      "gpio_led"
#define GPIO_LED       18 
#define GPIO_SW		   24 // 스위치 gpio 번호

static char *buffer = NULL;   //커널에서 사용 버퍼
static int sz_data = 0;      // data size

// I/O 접근을 위한 volatile 변수
volatile unsigned *gpio; // volatile unsigned int 로 써도 됨. int는 생략가능
struct cdev gpio_cdev;
static int switch_irq;

// gpio 인터럽트 서비스 루틴(ISR)
static irqreturn_t isr_func(int irq, void *data){
	if (irq == switch_irq && !gpio_get_value(GPIO_LED)){
		gpio_set_value(GPIO_LED, 1);
	}
	else if (irq == switch_irq && gpio_get_value(GPIO_LED)){
		gpio_set_value(GPIO_LED, 0);
	}
	return IRQ_HANDLED;
}

//장치파일 열기
static int my_open(struct inode *inode, struct file *filp){
   printk("device driver open! \n");
   try_module_get(THIS_MODULE);
   return 0;
}

//장치 파일 닫기
static int my_release(struct inode *inode, struct file *filp)
{
   printk("<Ko> device driver release! \n");
   module_put(THIS_MODULE);

   return 0;
}

//장치파일에 데이터 쓰기
static ssize_t my_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
   printk("<Ko> write to buffer! %s\n", buf);
   if(BUFF_SIZE < count){
      sz_data = BUFF_SIZE;
   }
   sz_data = count;
   strncpy(buffer, buf, sz_data);
   printk("<Ko> buffer : %s, %d\n",buffer, sz_data);
   return count;
}

//장치 파일에서 데이터 읽어 옴.
static ssize_t my_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
   printk(KERN_DEBUG "<Ko> from, buffer %s\n", buffer);
   // App의 read()로 보내는 buffer (사용자 값)
   if(!strcmp(buffer, "1")){
      copy_to_user(buf, "LED ON", 7);
   }
   else if(!strcmp(buffer,"2")){
      copy_to_user(buf,"LED OFF",8);
   }
   return sz_data;
}

static long my_ioctl(struct file *flip, unsigned int cmd, unsigned long arg){
   printk(KERN_DEBUG "<Ko> cmd : %d \n" ,cmd);
   printk(KERN_DEBUG "IOCTRL_LED_ON : %d \n" ,IOCTRL_LED_ON);
   
   switch(cmd){
   case IOCTRL_LED_ON:
      printk(KERN_INFO "LED ON in kernel\n");
      gpio_set_value(GPIO_LED, 1);
      break;
   case IOCTRL_LED_OFF:
      printk(KERN_INFO "LED OFF in kernel\n");
      gpio_set_value(GPIO_LED, 0);
      break;
   case IOCTRL_EXIT:
      printk(KERN_INFO "App exit\n");
      break;
   default:
      printk(KERN_INFO "unknown menu!\n");
      break;
   }
   return 0;
}
      

// 파일 동작 함수 연결
static struct file_operations gpio_fops = {
   .owner = THIS_MODULE,
   .read = my_read,
   .write = my_write,
   .open = my_open,
   .release = my_release,
   .unlocked_ioctl = my_ioctl
};

// cat//proc//devices -> 디바이스 드라이버 MAJOR_NUMBER 확인
// $sudo mknod /dev/gpio_led 256 0
// $sudo chmod 666 /dev/gpio_led
int __init my_init(void) {
    dev_t devno;
    unsigned int count;
    static void* map = NULL; //I/O 접근을 위한 변수
    int err;

    printk(KERN_INFO "Hello module!\n");

    //문자 디바이스를 등록
    devno = MKDEV(MAJOR_NUMBER, 0);
    register_chrdev_region(devno, 1, DEVICE_NAME);

    //문자 디바이스를 위한 구조체를 초기화
    cdev_init(&gpio_cdev, &gpio_fops);

    gpio_cdev.owner = THIS_MODULE;
    count = 1;
    // 문자 디바이스를 추가
    err = cdev_add(&gpio_cdev, devno, count);
    if (err < 0) {
        printk(KERN_ERR "ERROR : Device Add\n");
        return -1;
    }
	
	//gpio 사용 요청
	gpio_request(GPIO_LED, "LED");
	gpio_direction_output(GPIO_LED, 0);
	
	//인터럽트 handler 등록
	gpio_request(GPIO_SW, "SWITCH");
	switch_irq = gpio_to_irq(GPIO_SW);
	request_irq(switch_irq, isr_func, IRQF_TRIGGER_RISING | IRQF_TRIGGER_NONE, "switch", NULL);
	return 0;
}

void __exit my_exit(void){
    dev_t devno = MKDEV(MAJOR_NUMBER, 0);
    //문자 디바이스의 등록을 해제
    unregister_chrdev_region(devno, 1);
    //문자 디바이스의 구조체를 해제
    cdev_del(&gpio_cdev);
	
	// 인터럽트 해제
	free_irq(switch_irq, NULL);
	gpio_free(GPIO_SW);
	gpio_free(GPIO_LED);
    printk(KERN_INFO "Bye module!\n");
}

module_init(my_init);
module_exit(my_exit);
MODULE_AUTHOR("Ko");
MODULE_LICENSE("GPL v2");

'임베디드 > KERNEL' 카테고리의 다른 글

insmod mknod chmod 명령어  (0) 2021.01.14
gpio_lcd  (0) 2021.01.08
라즈베리파이 test_gpio3_func.c 인터럽트  (0) 2021.01.07
FATAL: modpost: GPL-incompatible module 에러  (0) 2021.01.07
라즈베리파이 gpio _led2 (led + buzzer)  (0) 2021.01.04