리눅스 디바이스 드라이버 프로그래밍(3) - 7segment led 예제 fpga에 적용시켜보기

내가 학교 실습시간에 사용한 키트는 DE1-SoC로, microSD카드에 리눅스를 넣어 사용하였다.
이전 포스팅을 참고하면 이해하는데 조금 도움이 될 것이다.
2019/06/12 - [임베디드 시스템] - 리눅스 디바이스 드라이버 프로그래밍(2) - 모듈 프로그래밍
리눅스 디바이스 드라이버 프로그래밍(2) - 모듈 프로그래밍
커널 모듈 (Kernel Module) 시스템 부팅 후에 동적으로 loading 할 수 있는 커널 구성 요소를 말한다. 커널을 다시 컴파일하거나 시스템 재부팅 할 필요 없이 커널의 일부분을 교체하는 것이 가능하다 디바이스..
butter-shower.tistory.com
7 segment LED 출력 장치 예제 코드
6개의 7 segment LED 출력장치를 /dev/hex 문자 장치 파일을 통하여 사용할 수 있도록 디바이스 드라이버를 작성해보자. (major number = 240)
write 함수에서는 32비트(실제로는 하위 24비트가 출력됨)을 6개의 7세그먼트 LED에 16진수로 출력한다. (하위 16비트를 4개의 7 segment LED를 위한 32비트 데이터로 변환하고, 상위 8비트를 2개의 7 segment LED를 위한 데이터로 변환한다.)
read 함수에서는 현재 출력된 32비트의 값을 출력한다. 출력 값을 변수에도 저장하고 이 값을 반환한다.
이 예제에서는 open, release 함수에서는 아무 동작도 하지 않기 때문에 바로 return해주는 동작을 한다.
7 segment LED 디바이스 드라이버 내용
아래는 디바이스 드라이버를 생성해 준 코드이다.
//8-2.c #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/interrupt.h> #include <asm/io.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/types.h> #include <linux/ioport.h> MODULE_LICENSE("GPS"); MODULE_AUTHOR("Jiyeon Lee"); MODULE_DESCRIPTION("Seven Segment LED"); #define base_lwFPGA 0xFF200000 #define len_lwFPGA 0x200000 #define addr_LED 0 #define addr_HEX0 0x20 #define addr_HEX1 0x30 #define addr_SW 0x40 #define addr_KEY 0x50 #define HEX_DEVMAJOR 240 #define HEX_DEVNAME "HEX" static void *mem_base; static void *hex0_addr; //hex3-hex0 static void *hex1_addr; //hex5-hex4 static unsigned int data = -1; static unsigned int mode = 0; #define NOFILL 4 #define BLINK 8 unsigned int hex0, hex1; static int turnoff = 0; int hex_conversion[16] = { 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x67, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71 }; static int hex_open(struct inode *minode, struct file *mfile){return 0;} static int hex_release(struct inode *minode, struct file *mfile){return 0;} //write 연산 static ssize_t hex_write(struct file *mfile, const char __user *buf, size_t count, loff_t *f_pos){ unsigned int hex_data = 0; unsigned int nofill = 0; get_user(hex_data, (unsigned int *)buf); hex_data = hex_data & 0xFFFFFF; data = hex_data; if(mode & NOFILL) nofill = 1; hex1 = 0; hex0 = hex_conversion[hex_data&0xF]; do{ hex_data >>= 4; if(nofill && hex_data==0) break; hex0 |= hex_conversion[hex_data&0xF]<<8; hex_data >>=4; if(nofill && hex_data==0) break; hex0 |= hex_conversion[hex_data&0xF]<<16; hex_data >>=4; if(nofill && hex_data==0) break; hex0 |= hex_conversion[hex_data&0xF]<<24; hex_data >>=4; if(nofill && hex_data==0) break; hex1 |= hex_conversion[hex_data&0xF]; hex_data >>=4; if(nofill && hex_data==0) break; hex1 |= hex_conversion[hex_data&0xF]<<8; }while(0); iowrite32(hex0, hex0_addr); iowrite32(hex1, hex1_addr); return 4; } //read 연산 static ssize_t hex_read(struct file *mfile, const char __user *buf, size_t count, loff_t *f_pos){ put_user(data, (unsigned int*)buf); return 4; } static long hex_ioctl(struct file *file, unsigned int cmd, unsigned long arg){ unsigned int newcmd; mode = cmd; return 0; } static struct file_operations hex_fops = { .read = hex_read, .write = hex_write, .open = hex_open, .release = hex_release .unlocked_ioctl = hex_ioctl, }; static int __init hex_init(void){ int res; res = register_chrdev(HEX_DEVMAJOR, HEX_DEVNAME, &hex_fops); if(res<0){ printk(KERN_ERR " HEX : failed to register device. \n"); return res; } mem_base = ioremap_nocache(base_lwFPGA, len_lwFPGA); if(!mem_base){ printk("Error mapping memory.\n"); release_mem_region(base_lwFPGA, len_lwFPGA); return -EBUSY; } printk("Device : %s / Major : %d \n\n\n", HEX_DEVNAME, HEX_DEVMAJOR); hex0_addr = mem_base + addr_HEX0; hex1_addr = mem_base + addr_HEX1; return 0; } static void __exit hex_exit(void){ unregister_chrdev(HEX_DEVMAJOR, HEX_DEVNAME); printk(" %s unregistered. \n\n", HEX_DEVNAME); iowrite32(0, hex0_addr); iowrite32(0, hex1_addr); iounmap(mem_base); } module_init(hex_init); module_exit(hex_exit);
보면 저번 포스팅에서 한 디바이스 드라이버의 골격을 제대로 갖춘 것을 확인할 수 있다.
그럼 시리얼 통신으로 fpga를 테라텀으로 연결한 곳으로 가서 우리가 짠 디바이스 드라이버를 등록해주자. Makefile을 만든 후 바로 make 명령어를 실행한다. (이전 포스팅 참고)

(가독성 넘 떨어지지만..)
make
이 명령어를 통해 커널 오브젝트 파일(.ko)를 생성해준다.
insmod [파일명.ko]
insmod 명령어는 우리가 만든 디바이스 드라이버의 커널 오브젝트 파일을 loading 하는 명령어이다. 정상적 커널에 적재되었다면 lsmod 명령어로 확인할 수 있다.
lsmod
그 다음에는 mknod 명령어를 통해 디바이스 파일을 생성한다.
mknod /dev/HEX c 240 0
위 명령어의 뜻은 /dev/HEX라는 디바이스 파일을 생성하는데, 문자 디바이스(c) 형태이고 Major number는 240, Minor number는 0이라는 뜻이다.
chmod ug+w /dev/HEX
모드를 변경해주면 끝!
그럼 이제 만든 디바이스 드라이버를 테스트하는 프로그램을 작성해보자.
만든 디바이스 드라이버 테스트하는 프로그램 소스코드
//8-2-test.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/ioctl.h> #include <sys/stat.h> #define NOFILL 4 #define BLINK 8 int main(void){ int dev, data, rdata; dev = open("/dev/HEX", O_RDWR); if(dev<0){ fprintf(stderr, "Cannot Open LED device."); return 1; } //ioctl - default ioctl(dev, 0, NULL); //write printf("Input HEX7 data (hex) : "); scanf("%x", &data); write(dev, &data, 4); //read read(dev, &rdata, 4); printf("read data : %x \n", rdata); //write - NOFILL printf("Input HEX 7 data (hex) for NOFILL : "); scanf("%x", &data); ioctl(dev, NOFILL, NULL); write(dev, &data, 4); return 0; }
dev를 보면 파일 형태로 디바이스 드라이버에 접근한다는 것을 알 수 있다. (문자 디바이스 드라이버의 특징) data 변수는 우리가 입력한 숫자 데이터를 의미하고, rdata는 디바이스 드라이버에서 읽는 데이터를 의미한다.
이렇게 프로그램을 작성해준 후 아래와 같은 명령어를 통해 실행파일을 만들어준다.
#cc -o 8-2-test 8-2-test.c #./8-2-test
DE1-SoC 보드에서의 실행 결과는 아래와 같다.
입력 | DE1-SoC |
![]() |
![]() |
![]() |
|
![]() |
![]() |
![]() |
'Computer Engineering > 임베디드 시스템' 카테고리의 다른 글
리눅스 디바이스 드라이버 프로그래밍(4) - ioctl 함수, blocked 입력 (0) | 2019.06.13 |
---|---|
리눅스 디바이스 드라이버 프로그래밍(2) - 모듈 프로그래밍 (0) | 2019.06.12 |
리눅스 디바이스 드라이버 프로그래밍(1) - 디바이스 드라이버 개요, 디바이스 드라이버 종류 (0) | 2019.06.12 |
임베디드 리눅스 커널 프로그래밍(3) - 시스템 호출 함수 구현 (0) | 2019.06.12 |
임베디드 리눅스 커널 프로그래밍(2) - 커널 데이터타입, 커널 인터페이스 함수 (0) | 2019.06.11 |
댓글
이 글 공유하기
다른 글
-
리눅스 디바이스 드라이버 프로그래밍(4) - ioctl 함수, blocked 입력
리눅스 디바이스 드라이버 프로그래밍(4) - ioctl 함수, blocked 입력
2019.06.13 -
리눅스 디바이스 드라이버 프로그래밍(2) - 모듈 프로그래밍
리눅스 디바이스 드라이버 프로그래밍(2) - 모듈 프로그래밍
2019.06.12 -
리눅스 디바이스 드라이버 프로그래밍(1) - 디바이스 드라이버 개요, 디바이스 드라이버 종류
리눅스 디바이스 드라이버 프로그래밍(1) - 디바이스 드라이버 개요, 디바이스 드라이버 종류
2019.06.12 -
임베디드 리눅스 커널 프로그래밍(3) - 시스템 호출 함수 구현
임베디드 리눅스 커널 프로그래밍(3) - 시스템 호출 함수 구현
2019.06.12
댓글을 사용할 수 없습니다.