메뉴 건너뛰기

제어 - RaspberryPi.NCLab

1. 목표

Linux Device Driver 기본 형식 제작

* Device Driver 이름은 morse이지만 실제로 morse  signal을 만들어 내는 Device Driver는 gpio-ok05 이므로 유의



2. gpio-morse의 기능

가. Linux Device Driver의 기본 예제

나. 등록 시 morse_init()함수 호출

다. 해제 시 morse_exit()함수 호출

라. open()함수로 열 때 morse_open()함수 호출

마. close()함수로 닫을 때 morse_release()함수 호출



3. 실습 순서

가. linux/driver/gpio/gpio-morse.c  추가

나. linux/driver/gpio/Makefile 수정

다. gpio-morse 모듈 컴파일

라. RaspberryPi로 linux/driver/gpio/gpio-morse.ko 전송

마. gpio-morse 모듈 삽입

바. gpio-morse 장치 파일 생성

사. gpio_morse_app.c 추가

아. gpio_morse_app.c 컴파일

자. gpio_morse_app 실행

차. gpio-morse 모듈 제거



4. linux/driver/gpio/gpio-morse.c  추가

가. WinSCP 프로그램을 이용하면 Windows에서 Ubuntu로 파일을 쉽게 옮길 수 있음

* http://winscp.net/download/winscp570.zip 에서 다운로드

그림1.png

1). 호스트 이름(H): Ubuntu에서 사용중인 IP 주소(NAT 사용)

2). 사용자 이름(U): Ubuntu의 사용자 이름

3). 비밀번호(P): 사용자 이름에 해당하는 비밀번호


그림2.png

4). 왼쪽 창이 Host를 의미

5). 오른쪽 창이 Guest를 의미

6). 오른쪽 창에서 linux/driver/gpio로 이동

7). 왼쪽 창에서 소스 코드가 있는 경로로 이동

8). 왼쪽의 Windows에서 gpio-morse.c를 오른쪽의 Ubuntu로 복사


나. 또는 VMware의 공유 폴더 기능을 이용하여 복사 할 수 있음

* 개발환경 구축하기 - 04 Ubuntu 설치 참고


다. 또는 Ubuntu에서 vim 유틸리티를 통해 직접 작성

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>

#include <linux/fs.h>

/* 주 번호가 될 숫자 */
#define DEV_MORSE_MAJOR_NUMBER 	230
/* lsmod 실행 시 보여질 모듈의 이름 */
#define DEV_MORSE_NAME			"gpio-morse"

/* 장치 파일을 open() 하면 호출 될 함수 */
static int morse_open(struct inode *inode, struct file *filp){ 
	/* /proc/kmsg 파일에 기록, demsg 명령어로 다음 메시지를 볼 수 있음 */
	printk("[gpio-morse] morse_open()\n");
	return 0;
}

/* 장치 파일을 close() 하면 호출 될 함수 */
static int morse_release(struct inode *inode, struct file *filp){
	printk("[gpio-morse] morse_close()\n");
	return 0;
}

/* 이 구조체에 등록함으로써 응용프로그램에서 다음의 함수를 호출 할 수 있게 함 */
static struct file_operations morse_fops = {
	.owner		= THIS_MODULE,
	/* 장치 파일을 open()함수로 열면 morse_open() 이 호출됨 */
	.open		= morse_open,
	/* 장치 파일을 close()함수로 열면 morse_release() 이 호출됨 */
	.release	= morse_release,
};

/* 모듈을 insmod로 삽입할 때 호출되는 함수 */
static int morse_init(void){
	printk("[gpio-morse] morse_init()\n");
	/* 모듈을 주번호와 이름, 파일 오퍼레이션을 가지고 커널에 등록하는 함수 */
	register_chrdev(DEV_MORSE_MAJOR_NUMBER, DEV_MORSE_NAME, &morse_fops);
	return 0;
}

/* 모듈을 rmmod로 제거할 때 호출되는 함수 */
static void morse_exit(void){
	printk("[gpio-morse] morse_exit()\n");
	/* 주번호와 이름이 일치하는 모듈을 커널에서 등록 해제시키는 함수 */
	unregister_chrdev(DEV_MORSE_MAJOR_NUMBER, DEV_MORSE_NAME);

}

/* 모듈이 삽입될 때 morse_init()이 호출되도록 함 */
module_init(morse_init);
/* 모듈이 제거될 때 morse_exit()이 호출되도록 함 */
module_exit(morse_exit);

MODULE_LICENSE("Dual BSD/GPL");


5. linux/driver/gpio/Makefile 수정

* Ubuntu에서 진행

가. 해당 디렉토리에 있는 Device Driver를 Kernel에 어떤 방식으로 포함할 것인지에 대한 설정들이 포함되어 있음

1). obj-m : 모듈 형태로 제작 

2). obj-y : Kernel에 포함하여 제작

3). obj-  : 모듈을 생성하지 않음


나. 다음과 같이 마지막 부분에 추가

그림3.png

* 앞으로 새로운 모듈을 생성할 때마다 같은 방법으로 추가할 것

# generic gpio support: platform drivers, dedicated expander chips, etc

ccflags-$(CONFIG_DEBUG_GPIO)	+= -DDEBUG

obj-$(CONFIG_GPIO_DEVRES)	+= devres.o
obj-$(CONFIG_GPIOLIB)		+= gpiolib.o
obj-$(CONFIG_GPIOLIB)		+= gpiolib-legacy.o
obj-$(CONFIG_OF_GPIO)		+= gpiolib-of.o
obj-$(CONFIG_GPIO_SYSFS)	+= gpiolib-sysfs.o
obj-$(CONFIG_GPIO_ACPI)		+= gpiolib-acpi.o

# Device drivers. Generally keep list sorted alphabetically
obj-$(CONFIG_GPIO_GENERIC)	+= gpio-generic.o

obj-$(CONFIG_GPIO_74X164)	+= gpio-74x164.o
obj-$(CONFIG_GPIO_ADNP)		+= gpio-adnp.o
obj-$(CONFIG_GPIO_ADP5520)	+= gpio-adp5520.o
obj-$(CONFIG_GPIO_ADP5588)	+= gpio-adp5588.o
obj-$(CONFIG_GPIO_AMD8111)	+= gpio-amd8111.o
obj-$(CONFIG_GPIO_ARIZONA)	+= gpio-arizona.o
obj-$(CONFIG_GPIO_BCM_KONA)	+= gpio-bcm-kona.o
obj-$(CONFIG_GPIO_BT8XX)	+= gpio-bt8xx.o
obj-$(CONFIG_GPIO_CLPS711X)	+= gpio-clps711x.o
obj-$(CONFIG_GPIO_CS5535)	+= gpio-cs5535.o
obj-$(CONFIG_GPIO_CRYSTAL_COVE)	+= gpio-crystalcove.o
obj-$(CONFIG_GPIO_DA9052)	+= gpio-da9052.o
obj-$(CONFIG_GPIO_DA9055)	+= gpio-da9055.o
obj-$(CONFIG_GPIO_DAVINCI)	+= gpio-davinci.o
obj-$(CONFIG_GPIO_DWAPB)	+= gpio-dwapb.o
obj-$(CONFIG_GPIO_EM)		+= gpio-em.o
obj-$(CONFIG_GPIO_EP93XX)	+= gpio-ep93xx.o
obj-$(CONFIG_GPIO_F7188X)	+= gpio-f7188x.o
obj-$(CONFIG_GPIO_GE_FPGA)	+= gpio-ge.o
obj-$(CONFIG_GPIO_GRGPIO)	+= gpio-grgpio.o
obj-$(CONFIG_GPIO_ICH)		+= gpio-ich.o
obj-$(CONFIG_GPIO_IOP)		+= gpio-iop.o
obj-$(CONFIG_GPIO_IT8761E)	+= gpio-it8761e.o
obj-$(CONFIG_GPIO_JANZ_TTL)	+= gpio-janz-ttl.o
obj-$(CONFIG_GPIO_KEMPLD)	+= gpio-kempld.o
obj-$(CONFIG_ARCH_KS8695)	+= gpio-ks8695.o
obj-$(CONFIG_GPIO_INTEL_MID)	+= gpio-intel-mid.o
obj-$(CONFIG_GPIO_LP3943)	+= gpio-lp3943.o
obj-$(CONFIG_ARCH_LPC32XX)	+= gpio-lpc32xx.o
obj-$(CONFIG_GPIO_LYNXPOINT)	+= gpio-lynxpoint.o
obj-$(CONFIG_GPIO_MAX730X)	+= gpio-max730x.o
obj-$(CONFIG_GPIO_MAX7300)	+= gpio-max7300.o
obj-$(CONFIG_GPIO_MAX7301)	+= gpio-max7301.o
obj-$(CONFIG_GPIO_MAX732X)	+= gpio-max732x.o
obj-$(CONFIG_GPIO_MC33880)	+= gpio-mc33880.o
obj-$(CONFIG_GPIO_MC9S08DZ60)	+= gpio-mc9s08dz60.o
obj-$(CONFIG_GPIO_MCP23S08)	+= gpio-mcp23s08.o
obj-$(CONFIG_GPIO_ML_IOH)	+= gpio-ml-ioh.o
obj-$(CONFIG_GPIO_MM_LANTIQ)	+= gpio-mm-lantiq.o
obj-$(CONFIG_GPIO_MOXART)	+= gpio-moxart.o
obj-$(CONFIG_GPIO_MPC5200)	+= gpio-mpc5200.o
obj-$(CONFIG_GPIO_MPC8XXX)	+= gpio-mpc8xxx.o
obj-$(CONFIG_GPIO_MSIC)		+= gpio-msic.o
obj-$(CONFIG_GPIO_MSM_V1)	+= gpio-msm-v1.o
obj-$(CONFIG_GPIO_MSM_V2)	+= gpio-msm-v2.o
obj-$(CONFIG_GPIO_MVEBU)        += gpio-mvebu.o
obj-$(CONFIG_GPIO_MXC)		+= gpio-mxc.o
obj-$(CONFIG_GPIO_MXS)		+= gpio-mxs.o
obj-$(CONFIG_GPIO_OCTEON)	+= gpio-octeon.o
obj-$(CONFIG_GPIO_OMAP)		+= gpio-omap.o
obj-$(CONFIG_GPIO_PCA953X)	+= gpio-pca953x.o
obj-$(CONFIG_GPIO_PCF857X)	+= gpio-pcf857x.o
obj-$(CONFIG_GPIO_PCH)		+= gpio-pch.o
obj-$(CONFIG_GPIO_PL061)	+= gpio-pl061.o
obj-$(CONFIG_GPIO_PXA)		+= gpio-pxa.o
obj-$(CONFIG_GPIO_RC5T583)	+= gpio-rc5t583.o
obj-$(CONFIG_GPIO_RDC321X)	+= gpio-rdc321x.o
obj-$(CONFIG_GPIO_RCAR)		+= gpio-rcar.o
obj-$(CONFIG_GPIO_SAMSUNG)	+= gpio-samsung.o
obj-$(CONFIG_ARCH_SA1100)	+= gpio-sa1100.o
obj-$(CONFIG_GPIO_SCH)		+= gpio-sch.o
obj-$(CONFIG_GPIO_SCH311X)	+= gpio-sch311x.o
obj-$(CONFIG_GPIO_SODAVILLE)	+= gpio-sodaville.o
obj-$(CONFIG_GPIO_SPEAR_SPICS)	+= gpio-spear-spics.o
obj-$(CONFIG_GPIO_STA2X11)	+= gpio-sta2x11.o
obj-$(CONFIG_GPIO_STMPE)	+= gpio-stmpe.o
obj-$(CONFIG_GPIO_STP_XWAY)	+= gpio-stp-xway.o
obj-$(CONFIG_GPIO_SX150X)	+= gpio-sx150x.o
obj-$(CONFIG_GPIO_SYSCON)	+= gpio-syscon.o
obj-$(CONFIG_GPIO_TB10X)	+= gpio-tb10x.o
obj-$(CONFIG_GPIO_TC3589X)	+= gpio-tc3589x.o
obj-$(CONFIG_ARCH_TEGRA)	+= gpio-tegra.o
obj-$(CONFIG_GPIO_TIMBERDALE)	+= gpio-timberdale.o
obj-$(CONFIG_GPIO_PALMAS)	+= gpio-palmas.o
obj-$(CONFIG_GPIO_TPS6586X)	+= gpio-tps6586x.o
obj-$(CONFIG_GPIO_TPS65910)	+= gpio-tps65910.o
obj-$(CONFIG_GPIO_TPS65912)	+= gpio-tps65912.o
obj-$(CONFIG_GPIO_TS5500)	+= gpio-ts5500.o
obj-$(CONFIG_GPIO_TWL4030)	+= gpio-twl4030.o
obj-$(CONFIG_GPIO_TWL6040)	+= gpio-twl6040.o
obj-$(CONFIG_GPIO_TZ1090)	+= gpio-tz1090.o
obj-$(CONFIG_GPIO_TZ1090_PDC)	+= gpio-tz1090-pdc.o
obj-$(CONFIG_GPIO_UCB1400)	+= gpio-ucb1400.o
obj-$(CONFIG_GPIO_VIPERBOARD)	+= gpio-viperboard.o
obj-$(CONFIG_GPIO_VR41XX)	+= gpio-vr41xx.o
obj-$(CONFIG_GPIO_VX855)	+= gpio-vx855.o
obj-$(CONFIG_GPIO_WM831X)	+= gpio-wm831x.o
obj-$(CONFIG_GPIO_WM8350)	+= gpio-wm8350.o
obj-$(CONFIG_GPIO_WM8994)	+= gpio-wm8994.o
obj-$(CONFIG_GPIO_XGENE)	+= gpio-xgene.o
obj-$(CONFIG_GPIO_XILINX)	+= gpio-xilinx.o
obj-$(CONFIG_GPIO_XTENSA)	+= gpio-xtensa.o
obj-$(CONFIG_GPIO_ZEVIO)	+= gpio-zevio.o
obj-$(CONFIG_GPIO_ZYNQ)		+= gpio-zynq.o

# Morse Driver Module
obj-m						+= gpio-morse.o


6. gpio-morse 모듈 컴파일

가. linux/ 디렉토리에서 다음과 같이 입력

$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
그림5.png

* RaspberryPi의 Kernel Soruce에 포함시켜 Kernel Module 형태의 Device Driver를 만드는 과정


나. 완료 후 명령어 뒤에 modules를 추가하여 다시 입력

$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- modules

그림6.png

* 앞으로 자주 사용될 명령어이므로 스크립트로 만들어 놓는 것을 추천



7. RaspberryPi로 linux/driver/gpio/gpio-morse.ko 전송

가. gpio-morse.ko가 생성 된 것을 확인

$ cd drivers/gpio
$ ls gpio-morse*
그림4.png

* .ko는 kernel object를 의미하며 이 파일이 커널 모듈 형태의 디바이스 드라이버임


나. scp를 사용하여 RaspberryPi로 전송

$ scp gpio-morse.ko root@192.168.1.231:/root
그림7.png

* 192.168.1.231은 RaspberryPi의 주소이며 상황에 따라 다를 수 있으므로 유의


8. gpio-morse 모듈 삽입

*RaspberryPi에서 진행

가. 다음과 같은 명령어를 사용하여 모듈 삽입

# cd /root
# insmod gpio-morse.ko
그림8.png


나. 다음과 같은 명령어를 사용하여 모듈 삽입 확인

# lsmod
그림9.png


다. 다음과 같은 명령어를 사용하여 커널 메시지 확인

# dmesg

그림10.png

* 디바이스 드라이버가 커널에 삽입되었을 때 커널 메시지가 출력되었음



9. gpio-morse 장치 파일 생성

가. 삽입된 모듈로 장치 파일을 생성해야 사용자 프로그램에서 사용 가능

나. 다음과 같은 명령어를 사용하여 장치 파일 생성

* 사용자 프로그램에서도 mknod() 함수를 사용하여 생성 가능하지만 여기서는 쉘 명령어로 생성할 것임

* # mknod [장치 파일 경로] [장치 종류] [주번호] [부번호]

# mknod /dev/morse c 230 0

그림11.png

* 응용프로그램에서 이 장치 파일을 open() 함수를 통해 열 것이므로 주번호와 부번호, 그리고 이름을 잘 기억할 것


다. 다음과 같은 명령어를 사용하여 장치 파일 생성 확인

# ls /dev/morse -al
그림12.png

10. gpio_morse_app.c 추가

가. Windows에서 WinSCP로 RaspberryPi에 접속하여 복사

그림13.png

1). 호스트 이름(H): RaspberryPi에서 사용중인 IP 주소

2). 사용자 이름(U): ‘root’ 입력

3). 비밀번호(P): RaspberryPi에서 사용중인 비밀번호, 기본값은 ‘root’

4). Ubuntu로 복사했을 때처럼 복사


나. 또는 Windows에서 Ubuntu로 공유 폴더를 이용하여 복사한 후 scp를 이용하여 RaspberryPi로 복사

* 번거로우므로 권장하지 않음


다. 또는 RaspberryPi에서 vim 유틸리티를 통해 직접 작성

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/kdev_t.h>

/* mknod로 생성한 장치 파일의 위치 */
#define _MORSE_PATH_ "/dev/morse"

int main(int argc, char *argv[]){
	int fd = 0;
	/* 이 함수를 사용하면 mknod 명령어를 사용하지 않아도 됨
	원본 소스 코드에서는 주석처리 되어 있으므로 mknod 명령어를 사용해야 함 */
	//mknod(_MORSE_PATH_, S_IRWXU | S_IRWXG | S_IFCHR, MKDEV(230, 0));
	/* 장치 파일을 여는 순간 커널 메시지 출력 */
	if((fd = open( _MORSE_PATH_, O_RDWR | O_NONBLOCK)) < 0){
		perror("open()");
		exit(1);
	}
	/* open() 후 메시지를 콘솔에 출력하고 2초간 잠듦 */
	printf("open sungkong!\n");	sleep(2);
	/* 파일을 닫는 순간 커널 메시지 출력 */
	close(fd);
	return 0;
}


11. gpio_morse_app.c 컴파일

* RaspberryPi에서 진행

가. RaspberryPi에 포함된 컴파일러를 사용하여 컴파일

나. 다음과 같은 명령어를 사용하여 컴파일

# gcc gpio_morse_app.c -o gpio_morse_app
그림14.png



12. gpio_morse_app 실행

# ./gpio_morse_app


가. “open sungkong!” 메시지를 확인

그림15.png


나. 다음과 같은 명령어를 사용하여 커널 메시지 확인

# dmesg
그림16.png

* 응용프로그램에 의해 장치파일이 열렸을 때, 닫혔을 때 각각 커널 메시지가 출력되었음


13. gpio-morse 모듈 제거

가. 다음과 같은 명령어를 사용하여 장치 파일 제거

# rm /dev/morse
그림17.png


나. 다음과 같은 명령어를 사용하여 모듈 제거

# rmmod gpio-morse
그림18.png


다. 다음과 같은 명령어를 사용하여 모듈 제거 확인

# lsmod | grep gpio_morse 
그림19.png


라. 다음과 같은 명령어를 사용하여 커널 메시지 확인

# dmesg 
그림20.png

* 디바이스 드라이버가 커널에서 제거되었을 때 커널 메시지가 출력되었음




--

Device Driver를 이용한 GPIO Control - 01 개발 환경 소개

Device Driver를 이용한 GPIO Control - 03 GPIO 16번에 LED를 연결하여 켜고 끄기

Device Driver를 이용한 GPIO Control - 04 busy wait을 사용하여 LED를 점멸하게 만들기

Device Driver를 이용한 GPIO Control - 05 다른 GPIO도 제어할 수 있도록 함수 일반화

Device Driver를 이용한 GPIO Control - 06 system timer를 사용하여 정밀하게 시간 제어

Device Driver를 이용한 GPIO Control - 07 사용자 입력을 받아와 Morse Code로 출력


위로