• 검색 결과가 없습니다.

Segment Counter

문서에서 GettingStarted-with-HBE-EMPOSII (페이지 96-109)

8. 디바이스 드라이버

8.2. EMPOSⅡ에서 디바이스 드라이버 작성

8.2.2 Segment Counter

디바이스 드라이버는 file_operations에 의해 정의된 인터페이스를 제공한다. 이 곳에는 한

int ioctl(int d, int request, …)

여기서 ‘d’는 파일 기술자이고 request는 파일 기술자에 매치 되는 디바이스 드라이버 안 의 ioctl에서 수행될 command이다. 이 command의 구성은 상위 16비트의 기본번호와

Documentation/ioctl-number.txt에 정의 되어 있다. 여기서는 기본 번호로 커널 소스에 정의되지 않은 0xbb를 사용할 것이다. 명령코드를 만드는 방식으로 다음 네 가지 형태의 ioctl 함수 호출하여 사용한다.

_IO(base, command) 선택된 명령을 정의 한다. ioctl을 실행하는 어

플리케이션과 데이터를 주고 받지 않는다.

_IOR(base,command,size) 어플리케이션의 읽기 ioctl. size는 전송되는 인 수의 크기이다.

_IOW(base,command,size) 어플리케이션의 쓰기 ioctl. size는 전송되는 인 수의 크기이다.

_IOWR(base,command,size) 읽기와 쓰기 ioctl. 일반적으로 size는 주고 받 은 인수의 크기를 가리킨다.

디바이스 드라이버에서는 이들 command에 따라 switch문으로 분리하여 각각의 명령을 수행한다.

세그먼트 LED에 대해 살펴보면, 하나의 세그먼트 LED는 아래의 그림처럼 연결되어 각각 의 위치에 맞는 비트 값을 셋 하면 그 곳의 led가 빛을 발한다. 즉 예를 들어 1을 쓰고 싶으면 세그먼트 led의 ‘B’와 ‘C’를 셋 하면 된다. 이것을 비트 값으로 보면 D1과 D2를 셋 하면 된다. 즉 코드 값 0x6이 되는 것이다. 현재 타겟 보드에서는 하위 8비트(0~7)에 세그먼트 하나와 그 위 8비트(8~16)에 또 다른 하나의 세그먼트 LED가 연결되어 사용되 고 있다.

- 세그먼트 LED의 기능 구조

지금까지 세그먼트 LED를 제어 하기 위한 기초 지식을 살펴봤다. EMPOSⅡ에 있는 세그 먼트 LED를 제어하여 카운터를 위해 사용되는 세그먼트 LED의 가상 주소는 왼쪽에 있는 것이 0xf1400000이고 오른쪽 있는 것은 0xf1300000이다.

이것을 기초로 하여 세그먼트의 디바이스 드라이버를 만들고 응용프로그램에서 ioctl로 명 령을 내려 카운터를 만들기 위해 다음과 같은 소스 파일을 생성한다.

- segmentport.c

//fimename : segmentport.c

#include <linux/module.h>

#include <asm/hardware.h>

#include <asm/uaccess.h>

#include <linux/kernel.h>

#include <linux/fs.h>

#include <linux/errno.h>

#include <linux/types.h>

#include <asm/ioctl.h>

#include <linux/ioport.h>

#define SEGMENTPORT_MAJOR 0

#define SEGMENTPORT_NAME "SEGMENT PORT"

#define SEGMENTPORT_MODULE_VERSION "SEGMENT PORT V0.1"

#define SEGMENTPORT_ADDRESS_LOW 0xf1300000

#define SEGMENTPORT_ADDRESS_HIGH 0xf1400000

#define SEGMENTPORT_ADDRESS_RANGE 4

#define SEGMENTPORT_BASE 0xbb

#define SEGMENT_WRITE_LOW _IOW(SEGMENTPORT_BASE,0,4)

#define SEGMENT_WRITE_HIGH _IOW(SEGMENTPORT_BASE,1,4)

unsigned int *addr_low;

unsigned int *addr_high;

//Global variable

// define functions...

int segmentport_open(struct inode *minode, struct file *mfile) { if(segmentport_usage != 0) return -EBUSY;

MOD_INC_USE_COUNT;

segmentport_usage = 1;

addr_low = (unsigned int *)(SEGMENTPORT_ADDRESS_LOW);

addr_high = (unsigned int *)(SEGMENTPORT_ADDRESS_HIGH);

return 0;

int segmentport_release(struct inode *minode, struct file *mfile) { MOD_DEC_USE_COUNT;

segmentport_usage = 0;

return 0;

unsigned int Getsegcode(int x) {

unsigned int code;

switch (x) {

case 0x0 : code = 0x3f; break;

case 0x1 : code = 0x06; break;

case 0x2 : code = 0x5b; break;

case 0x3 : code = 0x4f; break;

case 0x4 : code = 0x66; break;

case 0x5 : code = 0x6d; break;

case 0x6 : code = 0x7d; break;

case 0x7 : code = 0x07; break;

case 0xC : code = 0x39; break;

case 0xD : code = 0x5e; break;

case 0xE : code = 0x79; break;

case 0xF : code = 0x71; break;

default : code = 0; break;

return code;

void ShowSegment(unsigned short value) {

unsigned int v0,v1,v2,v3;

v0 = Getsegcode(value & 0xF);

v1 = Getsegcode((value >> 4) & 0xF);

v2 = Getsegcode((value >> 8) & 0xF);

v3 = Getsegcode((value >> 12) & 0xF);

*addr_low = (v1 << 8)|v0;

*addr_high = (v3 << 8)|v2;

ssize_t segmentport_write(struct file *inode, const char *gdata, size_t length, loff_t *off_what) {

unsigned short val;

get_user(val,(unsigned short *)gdata);

ShowSegment(val);

return length;

int segmentport_ioctl(struct inode *inode, struct file *file,unsigned int cmd,unsigned long gdata) {

switch(cmd){

case SEGMENT_WRITE_LOW:

get_user(val,(unsigned int *)gdata);

val_low = val % 10;

val_high = val / 10;

val_low = Getsegcode(val_low);

val_high = Getsegcode(val_high);

*addr_low = (val_high << 8)|val_low;

break;

case SEGMENT_WRITE_HIGH:

get_user(val,(unsigned int *)gdata);

val_low = val % 10;

val_high = val / 10;

val_low = Getsegcode(val_low);

val_high = Getsegcode(val_high);

*addr_high = (val_high << 8)|val_low;

break;

default:

printk("driver : no such command! n");

return -ENOTTY;

return 0;

struct file_operations segment_fops = { write: segmentport_write,

int init_module(void) {

int result;

result= register_chrdev(SEGMENTPORT_MAJOR,SEGMENTPORT_

NAME,&segment_fops);

if(result < 0) {

printk(KERN_WARNING"Can't get any major n");

return result;

segmentport_major = result;

if(!check_region(SEGMENTPORT_ADDRESS_LOW, SEGMENTPORT_ADDRESS_RANGE)||!check_region(

SEGMENTPORT_ADDRESS_HIGH,SEGMENTPORT_ADDRES S_RANGE))

{

request_region(SEGMENTPORT_ADDRESS_LOW, SEGMENTPORT_ ADDRESS_RANGE,SEGMENTPORT_NAME);

request_region(SEGMENTPORT_ADDRESS_HIGH, SEGMENTPORT_ADDRESS_RANGE,SEGMENTPORT_NAME);

printk("init module, segmentport major number :

%d n",result);

else printk("driver : unable to register this! n");

return 0;

void cleanup_module(void) {

release_region(SEGMENTPORT_ADDRESS_LOW,SEGMENTPORT_

ADDRESS_RANGE);

if(!unregister_chrdev(segmentport_major,SEGMENTPORT_NAME)) printk("driver : %s DRIVER CLEANUP Ok n",

SEGMENTPORT_NAME);

else

printk("driver : %s DRIVER CLEANUP FALLED n", SEGMENTPORT_NAME);

- counter.c

/* ************************************************************ */

/* Test program of 7 segment Digits(4 digits) display */

/* Hanback Electronics */

/* Date: March 2003 */

/* ************************************************************ */

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <sys/ioctl.h>

#include <termios.h>

#define SEGMENTPORT_BASE 0xbb

#define SEGMENT_WRITE_LOW _IOW(SEGMENTPORT_BASE,0,4)

#define SEGMENT_WRITE_HIGH _IOW(SEGMENTPORT_BASE,1,4)

static struct termios initial_settings, new_settings;

tcgetattr(0,&initial_settings);

new_settings = initial_settings;

new_settings.c_lflag &= ICANON;

new_settings.c_lflag &= ECHO;

new_settings.c_lflag &= ISIG;

new_settings.c_cc[VMIN] = 1;

new_settings.c_cc[VTIME] = 0;

tcsetattr(0,TCSANOW,&new_settings);

void close_keyboard() {

tcsetattr(0,TCSANOW,&initial_settings);

int kbhit() {

char ch;

int nread;

if(peek_character != -1) return 1;

new_settings.c_cc[VMIN] = 0;

tcsetattr(0,TCSANOW,&new_settings);

nread = read(0,&ch,1);

new_settings.c_cc[VMIN] = 1;

tcsetattr(0,TCSANOW,&new_settings);

if(nread == 1) {

peek_character = ch;

return 1;

return 0;

int readch() {

char ch;

if(peek_character != -1) { ch = peek_character;

peek_character = -1;

return ch;

read(0,&ch,1);

return ch;

int main() {

int dev;

unsigned int low_counter, high_counter;

char ch = 'y',command;

dev = open("/dev/segmentport", O_WRONLY);

if(dev < 0) {

printf("application : segment driver open fails! n");

return -1;

printf(" n 7Segment Counter n n");

printf("--- n");

printf(" r for start the counter n");

printf(" s for stop the counter n");

printf(" c for continue the counter n");

printf(" q for exit n");

command = 'r';

ioctl(dev,SEGMENT_WRITE_LOW,&low_counter,4);

ioctl(dev,SEGMENT_WRITE_HIGH,&high_counter,4);

while(ch != 'q') {

if(command != 's') {

ioctl(dev,SEGMENT_WRITE_LOW,&low_counter,4);

ioctl(dev,SEGMENT_WRITE_HIGH,&high_counter,4);

low_counter++;

if(low_counter>99) { low_counter = 0;

high_counter++;

if(high_counter > 59) high_counter = 0;

usleep(10000);

if(kbhit()){

ch = readch();

switch(ch){

case 'r': low_counter = 0; high_counter = 0;

command = 'r'; break;

case 'c': command = 'r'; break;

case 's': command = 's'; break;

high_counter = low_counter = 0x0000;

close_keyboard();

close(dev);

return 0;

- Makefile

# Device Driver Makefile

CC = arm-linux-gcc

KERNELDIR = /usr/local/linux-2.4.19-rmk4-pax2-empx1 INCLUDEDIR = -I$(KERNELDIR)/include -I./

CFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 -I$(INCLUDEDIR)

MODULE_OBJS = segmentport.o MODULE_SRCS = segmentport.c

TEST_TARGET = counter TEST_OBJS = counter.o TEST_SRCS = counter.c

all: $(MODULE_OBJS) $(TEST_TARGET)

$(MODULE_OBJS) :

$(CC) $(CFLAGS) -c $(MODULE_SRCS)

$(TEST_TARGET) : $(TEST_OBJS)

$(CC) -o $@ $(TEST_OBJS)

clean:

소스 생성이 완료 되었으면 make명령을 이용하여 다음과 같이 컴파일 시킨다.

[root@EMPOS/root]$ insmod segmentport.o

[root@EMPOS/root]$ mknod /dev/segmentport c 253 0

이상으로 응용프로그램을 실행할 준비가 끝났으므로 응용프로그램을 다음과 같이 실행한 SEGMENT_WRITE_LOW 또는 SEGMENT_WRITE_HIGH라는 command에 따라 제어하는 세그먼트 LED의 어드레스를 분리해서 그 값을 변화 시킨다. Getsegcode()는 인자를 숫자

문서에서 GettingStarted-with-HBE-EMPOSII (페이지 96-109)