11장 LDS2000 임베디드 실습
LED Hardware CS2 단자에 연결된 LED 제어 레지스터는 0x08000008에 위치 CS2 가 1 이고 A0-A2:0, A3:1 일 때 레지스터 접근
LED 접근 예제 [LDS2000-SW]/application/device/LED/led.c int main() { int i, LED_fd; unsigned char *mmap_addr; unsigned char *LEDReg; LED_fd = open("/dev/mem", O_RDWR|O_SYNC); mmap_addr=mmap(NULL, 1024, (PROT_READ|PROT_WRITE), MAP_SHARED, LED_fd, 0x08000000); /* CS2 */ LEDReg = (unsigned char *)(mmap_addr + 0x08); /* 0x8000008 */ for(i=0;i<8;i++) { *LEDReg = 0x01 << i ; sleep(1); } munmap(mmap_addr, 1024); close(LED_fd); return 0; } #include <sys/types.h> // for open #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> // for mmap #include <sys/mman.h> <가상 어드레스에 의한 LED제어 응용 프로그램>
KEYPAD Hardware KEYPAD IN, KEYPAD OUT 레지스터를 이용, KEYPAD 제어 데이터 D0 D1 D2로 3개의 열을 구분 A3,A1은 1, A2,A0는 0의 값을 가질 때, 즉 옵셋 0x0A 로 접근 KEYPAD OUT 레지스터 지정된 열에서 눌러진 4개의 키를 구분 A0,A1이 0, A2,A3이 1의 값을 가질 때, 즉 옵셋 0x0C로 접근 <KEYPAD의 하드웨어 흐름과 비트구성>
<모듈의 처리 함수 구조체 선언> KEYPAD 모듈 선언 #include <linux/kernel.h> #include <linux/module.h> #ifdef CONFIG_MODVERSIONS #define MODVERSIONS #include <linux/modversions.h> #endif #include <linux/fs.h> #include <linux/kdev_t.h> #include <asm/uaccess.h> #include <linux/slab.h> #include <asm/io.h> #include <linux/ioport.h> #define DEVICE_NAME "KEYPAD" static int KEYPAD_Major = 0; static int KEYPAD_DeviceOpen = 0; unsigned char *KEYPAD_BasePtr; unsigned char *KEYPAD_IN_RegPtr; unsigned char *KEYPAD_OUT_RegPtr; int KEYPAD_virtual_memory_allocate(); void KEYPAD_virtual_memory_free(); static int KEYPAD_open(struct inode *inode,struct file *filp); static int KEYPAD_release(struct inode *inode,struct file *filp); static ssize_t KEYPAD_write(struct file *filp,const char *buffer,size_t length,loff_t *offset); static ssize_t KEYPAD_read(struct file *filp, char *buffer,size_t length,loff_t *offset); <헤더 파일 선언> struct file_operations KEYPAD_fops = { open : KEYPAD_open, release : KEYPAD_release, read : KEYPAD_read, write : KEYPAD_write, }; <모듈의 처리 함수 구조체 선언> <전역 변수/함수 선언>
KEYPAD init_module() & cleanup_module() int init_module() { int error; KEYPAD_Major = register_chrdev(0, DEVICE_NAME, &KEYPAD_fops); printk("KEYPAD Device Driver registration OK with major number = %d\n",KEYPAD_Major); error = KEYPAD_virtual_memory_allocate(); return 0; } void cleanup_module() { int nRetCode; printk("Unloading KEYPAD Device Driver...\n"); KEYPAD_virtual_memory_free(); nRetCode = unregister_chrdev(KEYPAD_Major, DEVICE_NAME); int KEYPAD_virtual_memory_allocate() { KEYPAD_BasePtr = ioremap((unsigned int)0x08000000, (unsigned int)(1024)); /* get virtual address for KEYPAD control register */ /* 0xA offset from base ptr, (0x0800000A) */ KEYPAD_IN_RegPtr = KEYPAD_BasePtr + 0x0A; /* 0xC offset from base ptr, (0x0800000C) */ KEYPAD_OUT_RegPtr = KEYPAD_BasePtr + 0x0C; return 0; } void KEYPAD_virtual_memory_free() { iounmap(KEYPAD_BasePtr); <모듈의 등록과 해제 함수> <keypad 제어 레지스터 접근을 위한 가상 주소 영역 설정과 해제>
KEYPAD open() & release() int KEYPAD_open(struct inode *inode, struct file *filp) { printk("KEYPAD open with major/minor(%d / %d)\n", MAJOR(inode->i_rdev), MINOR(inode->i_rdev)); if (KEYPAD_DeviceOpen) { printk("KEYPAD Device Driver already open\n"); return -EBUSY; } ++KEYPAD_DeviceOpen; MOD_INC_USE_COUNT; return 0; } int KEYPAD_release(struct inode *inode, struct file *filp) { printk("KEYPAD release(close) with major/minor (%d / %d)\n", MAJOR(inode->i_rdev), MINOR(inode->i_rdev)); if (!KEYPAD_DeviceOpen) { printk("KEYPAD Device has not opened\n"); return -EINVAL; } --KEYPAD_DeviceOpen; MOD_DEC_USE_COUNT; return 0; } < 열기/닫기 처리 함수>
KEYPAD read() & write() ssize_t KEYPAD_write(struct file *filp,const char *buffer, size_t length, loff_t *offset) { const char *data = buffer; int count=0; char c; while(length > 0) { get_user(c, (char *)(data++)); /* Get user data from kernel buffer (*buffer)*/ outb(c,KEYPAD_IN_RegPtr); /* write user data into KEYPAD IN register */ --length; ++count; } return count; } ssize_t KEYPAD_read(struct file *filp, char *buffer, size_t length, loff_t *offset) { unsigned char key[2]; int retval = 0; /* read KEYPAD OUT register, only lower 4 bit is used */ key[0] = *KEYPAD_OUT_RegPtr & 0x0F; key[1] = *KEYPAD_IN_RegPtr & 0x07; if(key[0] > 0x00 && key[0] < 0x10) retval = 1; put_user(key[0], buffer); put_user(key[1], buffer+1); return retval; } < 쓰기/읽기 처리 함수>
<KEYPAD 제어용 응용 프로그램> #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <math.h> #include <stdlib.h> char key_analyze(int column, char key); char key_map[3][4] = { {‘1’, ‘4’, ‘7’, ‘*’}, {‘2’, ‘5’, ‘8’, ‘0’}, {‘3’, ‘6’, ‘9’, ‘#’} }; int bi_int[8] = {0, 1, 2, 0, 3}; int main(int argc , char **argv) { int fd, i = 0; char data, key[2], key_pressed; fd = open("/dev/keypad", O_RDWR); while(1) { /* KEYPAD IN data */ data = 0x1 << i; write(fd, &data, 1); /* KEYPAD OUT check */ if(read(fd, key, 2)) { key_pressed = key_analyze(key[0], key[1]); printf("Key Pressed: %c\n", key_pressed); fflush(stdout); if (key_pressed == '#') { printf("Exit program \n"); break; } if (++i > 2) i = 0; close(fd); return 0; char key_analyze(const char key, const char column) { int idx, col; col = bi_int[column >> 1]; idx = bi_int[key >> 1]; return key_map[col][idx]; } <KEYPAD 제어용 응용 프로그램>
KEYPAD를 이용한 인터럽트 처리 KEYPAD의 인터럽트 발생 KEYPAD OUT 레지스터의 출력된 키 데이터를 OR 연산 연산 값과 KEYPAD IN 레지스터의 D3와 NAND 연산 후 CPU 인터럽트 입력 LDS2000에서는 KEYPAD의 인터럽트 선을 GP14에 연결 < 인터럽트 발생 조건> <KEYPAD의 인터럽트 흐름>
GPIO(General-Purpose I/O) 일반적인 용도로 사용 가능한 디지털 입출력 기능의 Port pin들임 PXA255의 GPIO는 총 85개이며 각각의 핀들은 입력 또는 출력으로 프로그램 될 수 있다. 핀이 입력으로 사용되면 인터럽트 소스의 기능도 수행이 가능하다. PXA255의 대부분의 GPIO는 단순히 디지털 입출력 뿐만 아니라 부가적인 기능을 갖고 있다. 부가적인 기능과 단순 IO는 함께 사용할 수 없기 때문에 하드웨어 설계시 유의해야 한다. <PXA255 Block Daigram>
GPIO와 인터럽트 인터럽트와 관련된 레지스터의 기능 인터럽트와 GPIO GRER: 입력 핀의 상태가 Low에서 High로 변했을 때 GEDR을 1로 설정하는 것을 허가 GFER: 입력 핀의 상태가 High에서 Low로 변했을 때 GEDR을 1로 설정하는 것을 허가 GEDR: GRER이나 GFER의 허가 비트가 설정되어 있을 경우 해당 에지가 발생하면 1로 설정하여 에지 발생을 표시 인터럽트와 GPIO GPIO는 입력의 에지가 검출될 때 인터럽트가 발생하게 설정할 수 있다. 이것은 인터럽트 컨트롤러 안에 있는ICPR(Interrupt Controller Pending Register)를 설정함으로써 가능 GPIO 11-27: 개별적으로 인터럽트 발생의 허가와 불가를 선택할 수 없다. GPIO 0-10: 개별적으로 인터럽트 발생의 허가가 가능하다. <General-Purpose I/O Block Daigram>
인터럽트 처리 인터럽트 처리 Bottom half 방식 일반적으로 인터럽트에 등록된 핸들러 함수에서 처리 인터럽트 핸들러 함수는 태스크 호출 후 종료 실제적인 인터럽트 처리는 태스크 형태로 처리하도록 수행되는 기법 <bottom half 처리 흐름 >
KEYPAD 인터럽트 제어 모듈 헤더 파일과 함수 선언 #include <linux/kernel.h> /* We're doing kernel work */ #include <linux/module.h> /* Specifically, a module */ #include <asm/uaccess.h>/* for put_user */ #include <linux/init.h> #include <linux/fs.h> /* file operation */ #include <asm/io.h> #include <asm/irq.h> #include <linux/sched.h> #include <linux/string.h> #include <linux/errno.h> #include <linux/ioport.h> #include <linux/interrupt.h> #include <asm/hardware.h> #ifdef CONFIG_MODVERSIONS #define MODVERSIONS #include <linux/modversions.h> #endif #define DEVICE_NAME "KEYPAD_INT" #define KEYPAD_MAJOR 88 #define KEYPAD_IRQ IRQ_GPIO(14) static int keypad_DeviceOpen = 0; static int keypad_IntNum = -1; unsigned char *Virtual_BasePtr, *KEYPAD_IN_RegPtr, *KEYPAD_OUT_RegPtr; int keypad_virtual_memory_allocate(); void keypad_virtual_memory_free(); void user_wait(unsigned int); static int Keypad_Open(struct inode *, struct file *); static int Keypad_Release(struct inode *, struct file *); static void keypad_handler(int irq, void *dev_id,struct pt_regs *regs); void keypad_BH_handler(void *); static void keypad_BH_bottom_handler();
전역 설정: 인터럽트 제어를 위한 구조체 모듈의 처리 함수 구조체 선언 큐 생성 및 태스크 등록 전역 설정: 인터럽트 제어를 위한 구조체 모듈의 처리 함수 구조체 선언 큐 생성 및 태스크 등록 /* 키 값을 알아내기 위한 코드는 인터럽트 처리 함수에 존재*/ static struct file_operations keypad_fops = { open: Keypad_Open, release: Keypad_Release, } /*태스크 큐를 생성하고 태스크 구조체 생성 */ DECLARE_TASK_QUEUE(keypad_TaskQ); /* Task Queue Declare */ struct tq_struct keypad_TaskQ_structure; /* Task Queue Structure */
Init_module() 인터럽트 함수 등록 #define KEYPAD_IRQ IRQ_GPIO(14) Int init_module(void) { int keypad_Major; keypad_Major = register_chrdev(KEYPAD_MAJOR, DEVICE_NAME, &keypad_fops); keypad_virtual_memory_allocate(); /* Register IRQ14 interrupt number */ keypad_IntNum = KEYPAD_IRQ; request_irq(keypad_IntNum, keypad_irq_handler, SA_INTERRUPT, DEVICE_NAME, NULL); printk("Keypad IRQ(GP14) handler function registeration OK\n"); <플래그 설정 정보> <인터럽트 핸들러 등록함수>
Init_module() 인터럽트 처리 함수의 태스크 등록 bottom half 함수의 초기화 인터럽트 허용 /*routine 대응함수가 실제 인터럽트 처리 함수 */ keypad_TaskQ_structure.routine=keypad_BH_handler; keypad_TaskQ_structure.data = NULL; init_bh(KEYPAD_BH, keypad_BH_bottom_handler); /*태스크 초기화*/ *KEYPAD_IN_RegPtr = 0x0F; enable_irq(keypad_IntNum); /*내부 인터럽트 허용*/ return 0; }
IRQ_GPIO(14)의 분석 IRQ_GPIO(14) /*include/asm-arm/arch/irqs.h*/ & /*include/asm/arch-pxa/irqs.h*/ #define IRQ_GPIO(x) \ (((x) < 2) ? (IRQ_GPIO0 + (x)) : GPIO_2_80_TO_IRQ(x)) ->#define IRQ_GPIO0 PXA_IRQ(8) /* GPIO0 Edge Detect */ #define PXA_IRQ(x) ((x) – PXA_IRQ_SKIP) #define PXA_IRQ_SKIP 8 /* The first 8 IRQs asre reserved */ ->#define GPIO_2_80_TO_IRQ(i) ((i) - PXA_IRQ(32) + 2) IRQ_GPIO(14) == (((14) < 2) ? (IRQ_GPIO0 + 14) : GPIO_2_80_TO_IRQ(14) => GPIO_2_80_TO_IRQ(14) == 14 – PXA_IRQ(32) + 2 = 14 – (32 – PXA_IRQ_SKIP) + 2 = 14 – (32 – 8) + 2 = -8
cleanup_module() void cleanup_module (void) { int UnReg; printk("Unloading KEYPAD Driver for interrupt\n"); if ( keypad_IntNum >= 0) { free_irq(keypad_IntNum, NULL); printk("IRQ(GP14) line is removed\n"); } else printk("IRQ2 line was not registered\n"); keypad_virtual_memory_free(); UnReg = unregister_chrdev(KEYPAD_MAJOR, DEVICE_NAME); printk("Unregistered with Major Number OK\n"); }
인터럽트 Handler 함수 & bottom half으로 처리되는 함수 /* interrupt handler for IRQ14 */ void keypad_irq_handler(int irq, void *dev_id,struct pt_regs *regs) { printk(" IRQ !!!!! \n"); /* block keypad interrupt */ *KEYPAD_IN_RegPtr=0x00; /* insert bottom half task into task queue */ queue_task(&keypad_TaskQ_structure, &keypad_TaskQ); /* call bottom half task */ mark_bh(KEYPAD_BH); } <인터럽트 핸들러 함수> /* 인터럽트에서 처리해야 할 실제 작업이 bottom half 방식으로 처리되는 것 */ void keypad_BH_bottom_handler() { /* run some task from task queue */ run_task_queue(&keypad_TaskQ); } <init_bh()에 의해 호출되었으며, mark_bh()가 호출될 때 실행되는 실제 함수>
Bottom Half 처리 함수 /* Bottom half function - detect which key is pressed */ void keypad_BH_handler(void *unused) { unsigned char buff; *KEYPAD_IN_RegPtr = 0x01; switch (*KEYPAD_OUT_RegPtr) { case 0x01 : buff='1'; printk("key = %c\n", buff); goto finish; case 0x02 : buff='4'; printk("key = %c\n", buff); goto finish; case 0x04 : buff='7'; printk("key = %c\n", buff); goto finish; case 0x08 : buff='*'; printk("key = %c\n", buff); goto finish; default : break; } *KEYPAD_IN_RegPtr = 0x02; case 0x01 : buff='2'; printk("key = %c\n", buff); goto finish; case 0x02 : buff='5'; printk("key = %c\n", buff); goto finish; case 0x04 : buff='8'; printk("key = %c\n", buff); goto finish; case 0x08 : buff='0'; printk("key = %c\n", buff); goto finish; *KEYPAD_IN_RegPtr = 0x04; case 0x01 : buff='3'; printk("key = %c\n", buff); goto finish; case 0x02 : buff='6'; printk("key = %c\n", buff); goto finish; case 0x04 : buff='9'; printk("key = %c\n", buff); goto finish; case 0x08 : buff='#'; printk("key = %c\n", buff); goto finish; finish: *KEYPAD_IN_RegPtr = 0x0F;
< 인터럽트 처리 응용 프로그램> 모듈의 인터럽트 처리에 의해 키 값을 읽어오고 있으므로 별도의 읽기, 쓰기가 불필요 < 인터럽트 처리 응용 프로그램> #include <stdlib.h> #include <unistd.h> #include <sys/mman.h> #include <asm/fcntl.h> int main() { int fd; inti, j; if((fd = open("/dev/keypad_int", O_RDWR)) < 0) { perror("keypad device open fail\n"); exit(2); } printf("keypad Device Open!!\n"); printf("Checking interrupt signal from keypad \n"); close(fd); return 0; }
LED 와 KEYPAD 동시 제어 KEYPAD로부터 눌린 키를 받아 그에 해당하는 LED를 점등하는 응용 프로그램 작성 <응용 프로그램과 디바이스 드라이버의 처리 흐름>
연동 제어를 위한 모듈 #include <linux/kernel.h> #include <linux/module.h> #ifdef CONFIG_MODVERSIONS #define MODVERSIONS #include <linux/modversions.h> #endif #include <linux/fs.h> #include <linux/kdev_t.h> #include <asm/uaccess.h> #include <linux/slab.h> #include <asm/io.h> #include <linux/ioport.h> #define DEVICE_NAME "KEYPAD_LED" static int KEYPAD_LED_Major = 0; static int KEYPAD_LED_DeviceOpen = 0; unsigned long *BR2_OR2; unsigned char *KEYPAD_BasePtr; unsigned char *LED_RegPtr; unsigned char *KEYPAD_IN_RegPtr; unsigned char *KEYPAD_OUT_RegPtr; int KEYPAD_LED_virtual_memory_allocate(); void KEYPAD_LED_virtual_memory_free(); static int KEYPAD_LED_open(struct inode *inode,struct file *filp); static int KEYPAD_LED_release(struct inode *inode,struct file *filp); static ssize_t KEYPAD_LED_write(struct file *filp,const char *buffer,size_t length,loff_t *offset); static ssize_t KEYPAD_LED_read(struct file *filp, char *buffer,size_t length,loff_t *offset); char key_analyze(char column, char key); void user_wait(unsigned int delay_factor); <헤더 파일과 함수 선언>
Init_module() for KEYPAD_LED <모듈의 처리 함수 구조체 선언> struct file_operations KEYPAD_LED_fops = { open : KEYPAD_LED_open, release : KEYPAD_LED_release, read : KEYPAD_LED_read, write : KEYPAD_LED_write, }; <모듈의 등록과 해제 함수> int init_module(void) { KEYPAD_LED_Major = register_chrdev( 0, DEVICE_NAME, &KEYPAD_LED_fops); printk("KEYPAD_LED Device Driver registrered OK with major number = %d\n", KEYPAD_LED_Major); KEYPAD_LED_virtual_memory_allocate(); return 0; } void cleanup_module(void) { int nRetCode; printk("Unloading KEYPAD_LED Device Driver...\n"); KEYPAD_LED_virtual_memory_free(); nRetCode = unregister_chrdev(KEYPAD_LED_Major, DEVICE_NAME); int KEYPAD_LED_virtual_memory_allocate() { KEYPAD_BasePtr = ioremap((unsigned int)0x8000000, (unsigned int)(1024)); /* 0 offset from base ptr, ( 0x8000008 ) */ LED_RegPtr = KEYPAD_BasePtr + 0x08; /* 0x0A offset from base ptr, ( 0x800000A ) */ KEYPAD_IN_RegPtr = KEYPAD_BasePtr + 0xA; /* 0x0C offset from base ptr, ( 0x800000C ) */ KEYPAD_OUT_RegPtr = KEYPAD_BasePtr + 0xC; return 0; } void KEYPAD_LED_virtual_memory_free() { iounmap(KEYPAD_BasePtr); <가상 주소 영역 설정과 해제>
KEYPAD_LED_open() & KEYPAD_LED_release() int KEYPAD_LED_open(struct inode *inode, struct file *filp) { printk("KEYPAD_LED open with major/minor (%d / %d)\n", MAJOR(inode->i_rdev), MINOR(inode->i_rdev)); if (KEYPAD_LED_DeviceOpen) { printk("KEYPAD_LED Device Driver already open\n"); return -EBUSY; } ++KEYPAD_LED_DeviceOpen; MOD_INC_USE_COUNT; return 0; } <열기와 닫기 처리 함수> int KEYPAD_LED_release(struct inode *inode, struct file *filp) { printk("KEYPAD_LED release(close) with major/minor (%d / %d)\n", MAJOR(inode->i_rdev), MINOR(inode->i_rdev)); if (!KEYPAD_LED_DeviceOpen) { printk("KEYPAD_LED Device has not opened\n"); return -EINVAL; } --KEYPAD_LED_DeviceOpen; MOD_DEC_USE_COUNT; return 0; }
KEYPAD_LED_write() & KEYPAD_LED_read() ssize_t KEYPAD_LED_write(struct file *filp,const char *buffer, size_t length, loff_t *offset) { const char *data; int count=0; char c; data = buffer; while(length > 0) { /* Get user data from kernel buffer */ get_user(c, (char *)(data++)); /* write user data into LED control register */ outb(c, LED_RegPtr); --length; ++count; } return count; } ssize_t KEYPAD_LED_read(struct file *filp, char *buffer, size_t length, loff_t *offset) { unsigned char key, key_num; int i; for (i=0;i<3;i++) { /* write sequencial scan data into KEYPAD IN register, 0x01, 0x02, 0x04 */ outb(0x1 << i, KEYPAD_IN_RegPtr); user_wait(3500000); /* read KEYPAD OUT register, only lower 4 bit is used*/ key = *KEYPAD_OUT_RegPtr & 0x0F; /* analyze which key os pressed */ key_num = key_analyze(i, key); /*check if any key is pressed, or skip to scan*/ if (key_num != 0) { put_user(key_num, (char *)buffer); key_num = '\0'; /* find valid key is pressed, so we return immediately */ return 1; } } /* end of for() */ /* no key is pressed */ put_user(0, (char *)buffer); return 1; } <쓰기/읽기 처리 함수소스>
연동 제어를 위한 응용 프로그램 #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <math.h> int key_led_transform(char key); int main(int argc , char **argv) { int fd; int i, len; char data[2], key[2]; fd = open("/dev/keypad_led", O_RDWR|O_SYNC); while(1) { /* read which key is pressed */ len = read(fd, key, 1); /* detect any valid key */ if (key[0] != 0) { data[0] = key_led_transform(key[0]); switch(data[0]) { case -1: printf("Exit Program :: key=%c\n", key[0]); case 0x0F: printf("lower half LED ON :: key=%c\n", key[0]); break; case 0xF0: printf("upper half LED ON :: key=%c\n", default: printf("%c-th LED ON :: key=%c\n", key[0], key[0]); } /* turn on one LED according to key value */ write(fd, data, 1); } if (key[0] == '#') break; } close(fd); printf("KEYPAD_LED device driver close OK\n"); return 0; } <연동제어 응용 프로그램>
Key_lead_transform() int key_led_transform(char key) { int retval; switch(key) { case '0': retval = 0x00; break; case '9': retval = 0x0F; break; case '*': retval = 0xF0; break; case '#': retval = -1; break; default : retval = 0x1 << (key – ‘1’); break; } return retval; } <연동제어 응용 프로그램(계속)>
LDS2000 확장 보드 활용 LDS2000 확장보드 <LDS2000 확장보드의 블록 다이어그램>
<확장보드 LED 제어 프로그램> LDS2000 확장 LED 출력 제어 #include <stdlib.h> #include <unistd.h> #include <sys/mman.h> #include <asm/fcntl.h> int main() { int i, MemFD; unsigned char *mmap_addr; unsigned char *ExtBD_LEDReg; MemFD = open("/dev/mem", O_RDWR|O_SYNC); mmap_addr=mmap(NULL, 1024, (PROT_READ|PROT_WRITE), MAP_SHARED, MemFD, 0x14100000); /* CS5_Base + 0x100000 */ ExtBD_LEDReg = (unsigned char *)(mmap_addr); /* LED TEST - 32bits access 상위 8비트 사용 */ for(i=0; i<8; ++i) { *ExtBD_LEDReg = 0x01 << i; sleep(1); } printf("\n-------- Exit -------\n"); munmap(mmap_addr, 1024); close(MemFD); return 0; } <확장보드 LED 제어 프로그램>
<7-Segment 제어 레지스터의 비트 구성> 7-Segment LED 제어 5개의 7-segment 표현 값은 8비트 제어 레지스터에 의해 결정 상위 4비트:5개의 7-segment 구분 하위 4비트: 지정된 세그먼트 LED 십진 숫자 결정 <7-Segment 제어 레지스터의 비트 구성>
<7-Segment 제어 프로그램> 7-segment LED 출력 장치 제어 #include <stdlib.h> #include <unistd.h> #include <sys/mman.h> #include <asm/fcntl.h> int main() { int i, j, MemFD; unsigned char *mmap_addr; unsigned char *SevenSegReg; char *message[5] = { "First", "Second", "Third", "Fourth", "Fifth"}; printf("----- 7-Segment Test Program ----\n"); printf(" on the LDS2000 External Board \n\n"); MemFD = open("/dev/mem", O_RDWR|O_SYNC); mmap_addr=mmap(NULL, 1024, (PROT_READ|PROT_WRITE), MAP_SHARED, MemFD, 0x14080000); /* CS#5 Base + 0x80000 */ SevenSegReg = (unsigned char *)(mmap_addr); for (i = 0; i < 5; i++) { printf("------> %s Segment \n", message[i]); for (j = 0; j < 11; j++) { *SevenSegReg = i << 4 + j; sleep(1); } } printf("\n---------Exit -----------\n"); munmap(mmap_addr, 1024); close(MemFD); return 0; <7-Segment 제어 프로그램>
LDS2000 확장 보드 제어(계속) USB 컨트롤러 드라이버 USB를 연결을 이용한 TCP/IP 연결 실습 LDS2000에서는 USB Server와 Client 지원 커널에서 제공하는 드라이버를 이용하여 USB 드라이버 실습
USB 컨트롤러 드라이버(계속) LDS2000의 커널 컴파일 [System type], [Intel PXA250/210 Implementations] 순서로 선택 [PXA USB Function support] 항목과 [Support for PXA USB network link function] 항목을 선택하고 설정 저장 'CoreBell LDS2000 X-scale Board' 항목이 선택이 되어있는지 확인 <LDS2000에서의 USB 드라이버 확인>
USB 컨트롤러 드라이버(계속) LDS2000의 IP 설정 USB 인터페이스에 임의의 IP 주소 설정
USB 컨트롤러 드라이버(계속) 호스트 PC의 모듈 컴파일 호스트 PC를 USB 서버로 동작하기 위해 커널의 기능 지정 커널 메뉴 [USB support] 선택, USB network 드라이버를 설정하고 저장 커널 2.4.18-8 Support for USB', 'UHCI (Intel PIIX, VIA, ...) support', 'UHCI Alternate Driver (JE) support', 'USB-to-USB Networking cables, Linux PDAs, ... (EXPERIMENTAL)' 네 항목을 모듈로 선택 <호스트의 USB케이블 연결시 정상 커널 메시지>
USB 컨트롤러 드라이버(계속) 호스트 PC의 IP 설정 호스트 PC에서 USB 네트워크 인터페이스 usb0에 적절한 IP 주소를 지정 <호스트의 USB 확인>
LDS2000 확장 보드 제어(계속) TFT LCD 출력 장치 제어 LDS2000은 640*480의 16비트 color를 지원하는 TFT LCD와 ADS7843 Touch Panel 장착 Tiny-X를 이용 LCD를 통한 GUI 구현 임베디드 시스템의 GUI 사용을 위해 X, QT, Micro Window 등이 이용됨 Tiny-X 설치 #cd [LDS2000-SW]/External/LCD/Tiny-X/xc #make World #make install
TFT LCD 출력 장치 제어(계속) 필요 library 설치 실행 파일 및 라이브러리 파일 복사 X Server 실행을 위한 PATH 지정 #ls /usr/local/LDS-ARM-Linux/lib ----- ltermcap, lflex 바이너리 파일 확인----- #cp [LDS2000-SW]/External/LCD/RefLIB/libtermcap* /usr/local/LDS-ARM-Linux/lib #cp [LDS2000-SW]/External/LCD/RefLIB/libfl* /usr/local/LDS-ARM-Linux/lib #cd /NFS #cp -R /usr/X11R6-arm usr/ #vi /NFS/root/.bashrc PATH=$PATH:/usr/X11R6-arm/bin LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/X11R6-arm/lib export PATH LD_LIBRARY_PATH
TFT LCD 출력 장치 제어(계속) 프레임 버퍼 디바이스 노드 추가 터치스크린 디바이스 생성 커널 부팅 후 X 서버 실행 #cp –dpR /dev/fb0 /NFS/dev/fb0 #ln –sf dev/fb0 dev/fb #mknod /NFS/dev/ts c 16 0 [root@192.168.0.12 bin]$ Xlds2000 &
LDS2000 확장 보드 제어(계속) 사운드 장치 제어 PXA255에 내장된 AC97 Controller를 사운드 장치로 사용 코덱은 CIRRUS LOGIC의 CS4297A를 사용 커널 설정 최상위 메뉴의‘Sound’에서‘Sound support’항목 설정 확장되는 메뉴에서‘Intel PXA250/210 AC97 audio’항목 선택 사운드 플레이와 볼륨제어를 위해 splay와 smixer 사용
사운드 장치 제어(계속) splay 설치 smixer 설치 # cd [LDS2000-SW]/External/Sound/Splay-0.8.2 # make clean # ./configure # make CC=arm-linux-gcc CXX=arm-linux-c++ # cd [LDS2000-SW]/External/Sound/Splay-0.8.2/src/splay # ls # cd [LDS2000-SW]/External/Sound/smixer # make clean # make # ls
사운드 장치 제어 사운드 디바이스 노드 추가 smixer 환경 설정 후 splay 동작 주의점 생성된 splay, smixer, smixer.conf 파일을 /NFS 폴더에 복사 # cp –dpR /dev/dsp /NFS/dev/dsp 또는 # mknod c 14 3 /NFS/dev/dsp # cp –dpR /dev/mixer /NFS/dev/mixer 또는 # mknod c 14 0 /NFS/dev/mixer [root@192.168.0.12 /]$ smixer –s /etc/smixer.conf [root@192.168.0.12 /]$ splay /home/Sample.mp3
시리얼 통신 프로그래밍 시리얼 통신을 이용한 minicom 구현 시리얼 포트를 통해 비정규 모드에서 명령을 전송하고 데이터를 읽어 화면에 출력 #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/wait.h> #include <fcntl.h> #include <termios.h> void butify_output(char *rbuff); /* 타겟에서 받은 데이터를 출력하기 좋게 변형 */ int main(int argc, char *argv[]) { int fd, nret, status; pid_t pid; /* ① 터미널을 제어하기 위해 termios 변수 선언 */ struct termios oldtios, newtios; char rbuff[1024], wbuff[1024];
<termios 구조체 구성> minicom 구현(계속) <termios 구조체 구성> #define NCCS 32 struct termios { tcflag_t c_iflag; /* 입력모드 플래그 */ tcflag_t c_oflag; /* 출력모드 플래그 */ tcflag_t c_cflag; /* 제어모드 플래그 */ tcflag_t c_lflag; /* 로컬모드 플래그 */ cc_t c_line; /* 라인 규칙(POSIX는 미사용) */ cc_t c_cc[NCCS]; /* 제어 문자들 */ speed_t c_ispeed; /* 입력 속도 */ speed_t c_ospeed; /* 출력 속도 */ }; fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY); /* 시리얼 포트 개방 */ tcgetattr(fd, &oldtios); /* 현재 시리얼 포트의 설정을 보관 */ <open() 함수>
<termios 구조체 설정> minicom 구현(계속) <termios 구조체 설정> ① memset(&newtios, 0x00, sizeof(newtios)); ② newtios.c_cflag = B9600 | CS8 | CLOCAL | CREAD; ③ newtios.c_iflag = IGNPAR | ICRNL; /* 패리티 에러 무시, CR은 NL로 변환 */ ④ newtios.c_oflag = 0; /* 사용되지 않는다. */ ⑤ newtios.c_lflag = 0; /* 비 정규 모드(canonical mode) */ ⑥ newtios.c_cc[VTIME] = 5; /* 타임아웃을 0.5초로 설정 */ ⑦ newtios.c_cc[VMIN] = 0; /* 사용되지 않는다. */ <정규모드로 설정> newtios.c_lflag = ICANON; /* 정규 모드(canonical) */ newtios.c_cc[VEOF] = 10; /* 줄 단위 구분 문자는 LF(Line Feed) */ newtios.c_cc[VMIN] = 1; /* 최소 1문자 입력때까지 read에서 대기 */ <tcflush() 함수> tcflush(fd, TCIFLUSH); tcsetattr(fd, TCSANOW, &newtios);
< butify_optput()> minicom 구현(계속) <부모 프로세스> if ((pid = fork()) > 0) { /* parent */ while (1) { nret = read(fd, &rbuff, 1024); /* 읽기 작업 수행 */ rbuff[nret] = '\0'; /* 받은 데이터를 C-String으로 변환 */ butify_output(rbuff); /* 출력하기 좋은 형태로 데이터 변환 */ printf("%s", rbuff, nret); fflush(stdout); /* 받은 데이터를 터미널에 모두 출력 */ /* 자식 프로세스가 죽으면 종료 */ if (waitpid(pid, &status, WNOHANG)) { printf("Exiting parent\n"); break; } } } < butify_optput()> void butify_output(char *rbuff) { while (*rbuff != '\0') { if (!strncmp(rbuff, "\n\n", 2)) *rbuff = ' '; ++rbuff; } }
minicom 구현(계속) else if (pid == 0) { //child <부모 프로세스> else if (pid == 0) { //child while (1) { nret = 0; while (wbuff[nret-1] != '\n' && nret <= 1024) /* 사용자 입력 */ wbuff[nret++] = getchar(); wbuff[nret] = '\0'; write(fd, &wbuff, nret); /* /x 나 /X를 입력받으면 프로그램 종료 if (!strncmp(wbuff, "/X\n\0", 4) || !strncmp(wbuff, "/x\n\0", 4)) { printf("Exiting child\n"); break; } } exit(0); } <부모 프로세스> tcsetattr(fd, TCSANOW, &oldtios); close(fd); return 0; }
시리얼 통신 프로그래밍(계속) mini-minicom 실행 모니터링 실험을 위해 프로그램 수행 # gcc -o mini-minicom mini-minicom.c # ./mini-minicom