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

라즈베리파이 gpio _led

by sj0020 2020. 12. 24.

1. test_gpio1.c (우분투)

#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

#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 

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 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(GPIO_LED);
      break;
   case IOCTRL_LED_OFF:
      printk(KERN_INFO "LED OFF in kernel\n");
      GPIO_CLR(GPIO_LED);
      break;
   case IOCTRL_EXIT:
      printk(KERN_INFO "App exit\n");
      break;
   default:
      printk(KERN_INFO "unknown menu!\n");
      break;
   }
   return 0;
}
      

// 파일 동작 함수 연결.  system call
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;
    }

    //물리 주소를 가상 주소로 맵핑
    map = ioremap(GPIO_BASE, GPIO_SIZE);
    if (!map) {
        printk(KERN_ERR "ERROR : mapping gpio memory\n");
        iounmap(map);
        return -1;
    }
    gpio = (volatile unsigned int *)map;

    //LED 사용을 위한 초기화
    GPIO_OUT(GPIO_LED);
    return 0;
}

void __exit my_exit(void){
    dev_t devno = MKDEV(MAJOR_NUMBER, 0);
    //문자 디바이스의 등록을 해제
    unregister_chrdev_region(devno, 1);
    //문자 디바이스의 구조체를 해제
    cdev_del(&gpio_cdev);

    if (gpio) {
        //매핑된 메모리를 삭제
        iounmap(gpio);
    }
    
    printk(KERN_INFO "Bye module!\n");
}

module_init(my_init);
module_exit(my_exit);
MODULE_AUTHOR("Ko");
MODULE_LICENSE("Rack");

 

makefile

 

#linux kernel source
KDIR = /home/sss/linux
obj-m := test_gpio1.o

default:
	$(MAKE) ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -C $(KDIR) M=$$PWD modules
	
clean:
	$(MAKE) -C $(KDIR) M=$$PWD clean

 

2. test_gpio1_app1.c

 

#include <unistd.h>
//#include <fcntl.h>
#include <stdio.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <string.h>
#include <stdlib.h>


typedef enum {
	IOCTRL_LED_ON = 1000,
	IOCTRL_LED_OFF,
	PROGRAM_EXIT
} ioctrlCmd_t;

int main(int argc, char **argv) {
   int dev = 0;
   //char buff[1024] = { 0,};
   int menu = 0;
   dev = open("/dev/gpio_led", O_RDWR); //open은 한번만 열면 됨
   while(1){
	   printf("--------------------\nKERNEL Module Control 1.0\n--------------------\n");
	   printf("1. LED ON\n2. LED OFF\n3. EXIT\n");
	   printf("-------------------\n");
	   printf("menu : ");
	   scanf("%d", &menu);
	   switch(menu +999){
			case IOCTRL_LED_ON:
				ioctl(dev, IOCTRL_LED_ON);
				break;
			case IOCTRL_LED_OFF:
				ioctl(dev, IOCTRL_LED_OFF);
				break;
			case PROGRAM_EXIT:
				printf("Exit BYE\n");
				ioctl(dev, PROGRAM_EXIT);
				close(dev);
				exit(0);
	   }
   }
   
   return 0;
}

 

makefile

 

# app Makefile
CC = gcc
OBJECT = test_gpio1_app1.o
TARGET = test_gpio1_app1
CFLAGS = -g -Wall

$(TARGET) : $(OBJECT)
	$(CC) -o $(TARGET) $(OBJECT)

clean:
	rm -f $(OBJECT) $(TARGET)

 

 

 

 

 

 

 

 

 

1 선택하면 led on
2 선택하면 led off
3 선택하면 종료