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

gpio_lcd

by sj0020 2021. 1. 8.

 

#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 함수

#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) // high
#define GPIO_CLR(g)     (*(gpio+10) = 1<<g) // low
#define GPIO_GET(g)     (*(gpio+13) & (1<<g)) //read

#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 RS			   9	
#define EN			   11	
#define D4		       5
#define D5			   6
#define D6	 		   13 
#define D7			   19 

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;

//lcd prototype
void lcdInit(void);
void lcdComm(unsigned char cmd);
void lcdChar(unsigned char data);
void lcdStr(char *str);
void lcdStrPos(int row, int pos, char *str);
void lcdClear(void);
unsigned char cmd;

void lcdInit(void){
	mdelay(30); //Wait for more than 30ms after VDD rises to 4.5V
	lcdComm(0x02); // init 4bit mode로 세팅 (전원 인가 후 기본 8비트 모드로 동작)
	lcdComm(0x28); // function set
	lcdComm(0x0F); // display on/off
	lcdComm(0x01); // display clear
	lcdComm(0x06); // entry mode set
}

void lcdComm(unsigned char cmd){
	//상위 4비트 처리
	
	if ((cmd & 0x10) != 0){
		GPIO_SET(D4);
	}
	else{ 
		GPIO_CLR(D4);
	}
	
	if ((cmd & 0x20) != 0) GPIO_SET(D5);
	else GPIO_CLR(D5);
	
	if ((cmd & 0x40) != 0) GPIO_SET(D6);
	else GPIO_CLR(D6);
	
	if ((cmd & 0x80) != 0) GPIO_SET(D7);
	else GPIO_CLR(D7);
	
	GPIO_CLR(RS);
	GPIO_SET(EN);
	udelay(1);
	GPIO_CLR(EN);
	
	//하위 4비트 처리
	if ((cmd & 0x01) != 0) GPIO_SET(D4);
	else GPIO_CLR(D4);
	
	if ((cmd & 0x02) != 0) GPIO_SET(D5);
	else GPIO_CLR(D5);
	
	if ((cmd & 0x04) != 0) GPIO_SET(D6);
	else GPIO_CLR(D6);
	
	if ((cmd & 0x08) != 0) GPIO_SET(D7);
	else GPIO_CLR(D7);
	
	GPIO_SET(EN);
	udelay(1);
	GPIO_CLR(EN);
	udelay(1530);
	
}


void lcdChar(unsigned char data){
	
	//상위 4비트 처리
	if ((cmd & 0x10) != 0) GPIO_SET(D4);
	else GPIO_CLR(D4);
	
	if ((cmd & 0x20) != 0) GPIO_SET(D5);
	else GPIO_CLR(D5);
	
	if ((cmd & 0x40) != 0) GPIO_SET(D6);
	else GPIO_CLR(D6);
	
	if ((cmd & 0x80) != 0) GPIO_SET(D7);
	else GPIO_CLR(D7);
	
	GPIO_SET(RS);
	GPIO_SET(EN);
	udelay(1);
	GPIO_CLR(EN);
	
	//하위 4비트 처리
	if ((cmd & 0x01) != 0) GPIO_SET(D4);
	else GPIO_CLR(D4);
	
	if ((cmd & 0x02) != 0) GPIO_SET(D5);
	else GPIO_CLR(D5);
	
	if ((cmd & 0x04) != 0) GPIO_SET(D6);
	else GPIO_CLR(D6);
	
	if ((cmd & 0x08) != 0) GPIO_SET(D7);
	else GPIO_CLR(D7);
	
	GPIO_SET(EN);
	udelay(1);
	GPIO_CLR(EN);
	udelay(1530);
	
	
}

void lcdStr(char *str){
	int i;
	for(i=0; str[i]!=0; i++){
		lcdChar(str[i]);
	}
}

void lcdStrPos(int row, int pos, char *str){
#if 0
	if (row == 0 && pos<16){
		lcdComm((pos & 0x0F) |0x80);
	}
	else if (row == 1 && pos<16){
		lcdComm((pos & 0x0F)| 0xC0);
	}
#else
	lcdComm(0x80 | (pos + row * 0x40));
#endif
	lcdStr(str);
}

void lcdClear(void){
	lcdComm(0x01);
}


//장치파일 열기
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;
}
      

// 파일 동작 함수 연결
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);
	GPIO_OUT(RS);
	GPIO_OUT(EN);
	GPIO_OUT(D4);
	GPIO_OUT(D5);
	GPIO_OUT(D6);
	GPIO_OUT(D7);
	
	lcdInit();
	lcdStrPos(0, 1, "delay1530");
	lcdStrPos(1, 5, "2NE LINE");
    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("GPL V2");

 

 

 

 

우분투에서 make 하면 
C99 어쩌구 오류남

void lcdStr(char *str){ 
for(int i=0; str[i]!=0; i++){ 
lcdChar(str[i]); 
} 
} 

----------------에서 i 타입 선언을 for문 밖에 해줘야 함--------
void lcdStr(char *str){ 
int i; 
for(i=0; str[i]!=0; i++){ 
lcdChar(str[i]); 
} 
}