AVR FreeRTOS : Interrupt Management 1. 이장의개요 Embedded Real Time 시스템은주변장치로부터발생하는 Event 에실시간으로응답하여야하는응용분야에많이이용된다. 응용분야에따라서는여러개의 Interrupt Source로부터발생하는 Event를실시간으로처리하여야하고, 각각의 Interrupt 처리는서로다른처리시간과속도를필요로하기때문에최적의 Event 처리를위한전략이필요하다 A. 효과적인 Event 처리를위하여고려하여야할사항 i. 어떻게 Event를 Detect 할것인가? : Interrupt 가일반적으로 Event 발생에사용되지만경우에따라서는 Polling이필요할수있다. ii. Interrupt를사용할때 Interrupt Service Routine(ISR) 내에서처리하는부분과, ISR 외에서는처리하는부분을어떻게할당하여처리할것인가? : 가능하면 ISR 내에서처리하는부분을짧게하는것이좋다. iii. 어떻게 Event 가 Non-ISR code와통신하도록할것인가. FreeRTOS는특별히 Event 처리를위한 API와 Macros를별도로가지고있지는않다. 그러나일부 API와 Macros를 ISR 내에서사용할경우에는끝에 FromISR 이포함된 API 함수와 FROM_ISR 이포함된 Macros를사용하여야한다. B. 이장의중요목표 i. ISR에서사용되는 API 함수에대한이해 ii. iii. iv. Interrupt를사용하는시스템의구현에대한이해 Binary Semaphores( 세마포어 ) 와 Counting Semaphores를 Create 하고사용하는방법에대한이해 Binary Semaphores와 Counting Semaphores의차이 v. Queue( 큐 ) 를이용한 ISR 과 non-isr 사이의 Data Passing vi. Interrupt Nesting Model
2. Deferred Interrupt Processing A. Binary Semaphores를이용한동기 (Synchronization) i. Binary Semaphores는 Interrupt가발생하였을때특정한 를 Unblock 하는데사용할수있다. 이러한기능은 Interrupt과 를동기시키데효과적으로이용할수이고, 또한 ISR을매우빠르고적은량의프로그램코드만으로구성할수있게한다. ii. Time Critical 한 Interrupt 처리가필요한경우에는 Handler 의 Priority를다른 보다높게설정하여 (Pre-empts) ISR에서직접 Handler 로 Return 하여 Handler 가바로실행되도록한다. 현재실행중인 가 Interrupt 되고 Handler 에 Return 되는예. 2.ISR이실행된다. ISR에서 Semaphore를사용하여 Handler 를 Unblock 한다. ISR Handler 1 3.Handler 는가장높은 Priority를갖고있고, ISR이 Context Switch를수행하기때문에 ISR은직접 Handler 로 Return 한다. 1은 Ready 상태가된다. 4. Handler 는 Event 처리를완료하고, 다음 Event를기다리기위하여 Semaphore에의하여 Block 된다. 보다낮은 Priority를갖고있는 1이다시시작된다. 1.1 이실행중 일때 Interrupt 가발 생하였다. t1 t2 t3 t4 iii. iv. Handler 는 Event 가발생하는것을기다리기위하여 Take 를 Call 하여 Blocking 상태에있게되고, Event 가발생하면 ISR 에서 Give Operation 으로 Handler 가
Unblock 상태가되게한다. v. 위에서설명한 Interrupt 와 를동기시키는시나리오에서사용한 Binary Semaphore는길이가 1인 Queue로생각할수있다. 위예에서 Queue는하나의 Item만저장할수있기때문에 Empty 상태아니면 Full 상태 ( Binary ) 에있다. vi. vii. viii. Handler 에서 를 Call 하였을때 Queue가비어있으면 는 Blocked State가된다. 이상태에서 Event가발생하면 ISR에서 xsemaphoregiveisr() 함수를사용하여 Token을 Queue에놓게되고 Queue는 Full 상태가된다. 그결과 Handler 는 Token 을제거함과동시에 Blocked State를벗어나서 Interrupt에동기하여실행하여야할 Process를처리하게된다. 그리고, Queue는다시 Empty 상태가되어다음 Event를기다리게된다. Binary Semaphore 를이용한 Interrupt 와 동기 Semaphore가 Not Available 한상태임. 는 Blocked 되고 Semaphore 를기다림 Interrupt xsemaphoregivefromisr() Interrupt 이발생하고 Token 을 Give 한다.
Interrupt xsemaphoregivefromisr() 는 Unblock 상태이고, Semaphore가 Available 한상태임. 성공적으로 Semaphore 를 Take 하여 Process 를처리함. Queue 는다시 Empty 상태가됨. 는처리를완료하고다시 Semaphore Take 를시도함. 그결과다시 Blocked 상태가되었다.
B. vsemaphorecreatebinary() API Function Semaphore는사용되기전에 Create되어야한다. xsemaphorecreatebinary() API Function의 Prototype void xsemaphorecreatebinary ( xsemaphorehandle xsemaphore); Parameter Name xsemaphore Description Semaphore Handle C. API Function Taking Semaphore의의미는 Semaphore를 Receive 또는 Obtain 의의미이다. 는사용되기전에 Create되어야한다. 는 Interrupt Service Routine에서사용할수없다. API Function의 Prototype portbase_type xsemaphoretake ( xsemaphorehandle xsemaphore, portticktype xticktowait ); Parameter Name/ Returned value xsemaphore Semaphore Handle Description xticktowait 가 Blocked State에서 Semaphore를사용할수있을때까지기다리는최대 Tick 수만약 xtickstowait가 0 이면 Semaphore를회득할수없으면 는즉시 Return 된다. FreeRTOSConfig.h에서 INCLUDE_vSuspend 가 1로 Set되고, xtickstowait 가 portmax_delay로설정된경우 는 Timing out 없이무한이기다린다. Returned value 2가지가능한값을갖는다. pdpass: 가성공적으로 Semaphore를획득한경우 pdfalse: Semaphpre를획득할수없는경우
D. xsemaphoregivefromisr() API 함수 xsemaphoregivefromisr() 는 ISR 에서사용하는 xsemaphoregive() 함수의특별한형태이다. xsemaphoregivefromisr() API Function 의 Prototype portbase_type xsemaphoregivefromisr ( xsemaphorehandle xsemaphore, portbasetype *pxhigherprioritywoken ); Parameter Name/ Returned value xsemaphore Semaphore Handle Description pxhigherprioritywoken Semaphore 를사용할수있을때까지기다리는 가하나또는여러개일수있다. 만약 xsemaphoregivefromisr() 에의하여 unblocked 되는 의 Priority 가 Interrupt 에의하여실행이중단된 보다높거나같은경우 xsemaphoregivefromisr() 함수는 *pxhigherprioritywoken 을 pdtrue 로 Set 한다. *pxhigherprioritywoken pdtrue 로 Set 된경우 Interrupt ISR 에서직접가장 Priority 가높은 로직접 Context Switching 할수있다. Returned value 2 가지가능한값을갖는다. pdpass: xsemaphoregivefromisr() 이성공한경우 pdfalse: Semaphpre 가이미획득가능한상태이기때문에 Semaphpre 를 Give 할수없는경우 E. Interrupt 와 동기에 Binary Semaphpre 를사용하는예 : RT_SW_Interrupt_binary_semaphore // 실험목표 // Binary Semaphore 를이용하여 Interrupt 와 를동기방법에 // 대한이해 // // 실험방법
// Interrupt 입력장치로 SW PD0 를사용한다. // 출력장치로 LED 를사용한다. // 프로그램이처음시작하면 LED 가 1 회점멸한다. // SW 가 Push 되는순간 Interrupt 가발생하고, 이 Interrupt 에동기하여 // SW 가 Push 된회수가 LED 에표시된다. //#define TICK_INTERRUPT_TIMER0 #include <stdlib.h> #include <stdio.h> #include <avr/io.h> #include <avr/interrupt.h> #include <compat/deprecated.h> //FreeRTOS include files #include "FreeRTOS.h" #include "task.h" #include "croutine.h" #include "semphr.h" //User include files #define SW_DEBOUNCE_TIME 20 static void vsw_counterhandler(); static void vled_display(); static void init_port_setting(void); // xsemaphorehandle 변수를선언한다. // Semaphore 는 와 Interrupt 의동기에사용한다. xsemaphorehandle xbinarysemaphore = NULL; static char sw_counter; static void vsw_counterhandler() sw_counter = 0x00; for(;;) // Semaphore 를획득하지못하면 는 Blocked 상태가된다. // 외부 SW 의 Interrupt 에의하여 Semaphore 을획득할때까지 // 이상태가지속된다.
if(xsemaphoretake(xbinarysemaphore, portmax_delay ) == pdtrue) vdelay(sw_debounce_time); // 만약 SW PD0 가눌렸으면 if((pind & 0x01) == 0) sw_counter++; EIMSK = 0x01; // External Interrupt 0 enable // SW PD0 의누르면 SW 가 Push 된수를 LED 에표시한다. static void vled_display() PORTF = 0xff; // LED 가 1 회깜박인다. vdelay(500); PORTF = 0x00; vdelay(500); for(;;) PORTF = sw_counter; vdelay(20); portshort main(void) init_port_setting(); // Semaphore 는사용하기전에 Create 되어야한다. vsemaphorecreatebinary(xbinarysemaphore ); if(xbinarysemaphore!= NULL) // Interrupt 와동기를위하여 vsw_counterhandler 에 // 가장높은 Priority 를설정하였다. xcreate(vsw_counterhandler, (signed portchar *)"vsw_counterhandler", configminimal_stack_size*3, NULL, tskidle_priority + 3, NULL ); xcreate(vled_display, (signed portchar *)"vled_display", configminimal_stack_size,
NULL, tskidle_priority + 1, NULL ); // RunSchedular vstartscheduler(); for(;;) return 0; static void init_port_setting(void) cli(); //Disable all interrupts outp(0x00,ddrd); // PORTD 를 Input Port 로설정한다. outp(0xff,portd); // Pull up resistor 설정 outp(0xff,ddrf); // PORTF 를 Output Port 로설정한다. outp(0x00,portf); // clear LED. EICRA = 0x02; EIMSK = 0x01; sei(); // External Interrupt 0, Falling Edge // Asynchronously Interrupt // External Interrupt 0 enable // Re-enable interrupts // static void interrupt far SIG_INTERRUPT0( void ) SIGNAL (INT0_vect) static portbase_type xhigherprioritywoken; portbase_type xsemastatus; EIMSK &= ~0x01; // External Interrupt 0 Disable xhigherprioritywoken = pdfalse; // 'Give' the semaphore to unblock the task. xsemastatus = xsemaphoregivefromisr(xbinarysemaphore, &xhigherprioritywoken ); if((xsemastatus == pdtrue) && (xhigherprioritywoken == pdtrue )) // Semaphore 를획득한 가 Unblocked 상태가된다. // 그리고이 의 Priority 가 Interrupt 에의하여중단된
// 보다높은경우이 로직접 Return 을한다. taskyield(); 3. Counting Semaphores A. RT_SW_Interrupt_binary_semaphore 예제프로그램에서 Binary Semaphore가 Interrupt와 를동기시키는데사용되었다. Interrupt와 를동기시키는순서는다음과같다. i. Interrupt가발생한다. ii. Interrupt service Routine이실행되고, Semaphore Giving 에의하여 Handler 가 Unblock 상태로된다.. iii. Interrupt 가완료되자마자 Handler 가실행된다. Handler 는 Semaphore를 Take 한다. iv. Handler 는 Event 처리프로그램을실행하고, 다시 Semaphore Take 를시도한다. 만약 Semaphore 가즉시유효하지않으면다시 Blocked 상태가된다. 위의실행순서는 Interrupt의발생주기가 Event 처리속도보다느린경우에는잘실행된다. B. 만약다른 Interrupt가 Handler 가먼저발생한 Event를처리하는도중에발생한다면 Semaphore는 Event를 Latch(Semaphore 가다시유효한상태가됨 ) 한상태가되고, Handler 가앞에발생한 Event를처리하고 Semaphore Take 를시도하면, Handler 는 Block 상태에놓이지않고바로새로 Latch된 Event를처리한다. C. 그러나, 만약, Handler 가먼저발생한 Event를처리하는도중에 Semaphore에 Event 가 Latch 되고이것이 Take 되기전에또새로운 Interrupt 가발생한다면, 이후에발생하는 Event 정보는상실된다. D. 이러한문제를해결하기위하여 Counting Semaphore가필요하다. E. Binary Semaphores 는길이가 1인 Queue 와같이생각할수있고, Counting Semaphores는 1보다큰길이를갖는 Queue로생각할수있다. 단이경우 는 Queue의 Data에는관심이없고 Queue가 Empty 인가아닌가에만관심이있다. F. Counting Semaphores를사용하는경우에는 FreeRTOS.h 파일의 configuse_counting_semaphores 가 1로 Set 되어야한다.
Binary Semaphore 를이용한 Interrupt 와 의동기예에서앞서발생한 Event 를처리중에새로운 Interrupt 가발생하는경우의처리순서 Semaphore가 Not Available 한상태임. 는 Blocked 되고 Semaphore 를기다림 Interrupt xsemaphoregivefromisr() Interrupt 이발생하고 Token 을 Give 한다. Interrupt xsemaphoregivefromisr() 는 Unblock 상태이고, Semaphore가 Available 한상태임.
성공적으로 Semaphore 를 Take 하여 Process 를처리함. Queue 는다시 Empty 상태가됨. Interrupt xsemaphoregivefromisr() 가첫번째발생한 Event를처리하는중에 Interrupt가발생하고, ISR이실행되어 Event 가 Semaphore에 Latch 됨 는아직첫번째 Event 를처리하는중임. 가첫번째발생한 Event 를처리를완료하고 를 Call 한다. Semaphore 가이미유용한상태이기때문에 는 Blocked 상태에놓이지않고, 바로 Semaphore 를 Take 한다.
G. Counting Semaphores 의대표적인용도는다음과같다. i. Counting Semaphore 를 Event Counting 에사용하는경우 Event Handler(ISR) 은 Event 가발생할때마다 Semaphore 를 Give 한고, Semaphore 의 Count 는 1 씩증가한다. Handler 각 Event 의처리가끝날때마다 Semaphore 를 Take 한다. 이때마다 Semaphore count 는 1 씩감소한다. Counting Semaphore 를 Event Counting 에사용하는경우 Count Value 의초기값을 0 로하여 Counting Semaphore 를 Create 한다. Counting Semaphore 를이용한 Events Count 의처리과정 [Semaphore count 0] 는 Blocked 되고 Semaphore 를기다림 Interrupt xsemaphoregivefromisr() [Semaphore count 1] Interrupt 이발생하고 Token 을 Give 한다. Interrupt xsemaphoregivefromisr() [Semaphore count 1] 는 Unblock 상태이고, Semaphore가 Available 한상태임.
[Semaphore count 0] 성공적으로 Semaphore 를 Take 하여 Process 를처리함. Queue 는다시 Empty 상태가됨. Interrupt xsemaphoregivefromisr() [Semaphore count 2] 가첫번째발생한 Event 를처리하는중에다른 2 개의 Interrupt 가발생하고, ISR 이실행되어 2 개의 Event 가 Semaphore 에 Latch 됨 는아직첫번째 Event 를처리하는중임. Interrupt xsemaphoregivefromisr() [Semaphore count 1] 가첫번째발생한 Event 를처리를완료하고 를 Call 한다. Semaphore 가이미유용한상태이기때문에 는 Blocked 상태에놓이지않고, 바로하나의 Semaphore 를 Take 한다. 다른또하나의 Semaphore 는 Latch 되어유용한상태가된다.
ii. Counting Semaphore 를 Resource Management 에사용하는경우이경우 Semaphore 의 Count Value 는사용가능한자원의수를표시한다. Resource 의사용권을획득하기위하여 는먼저 Semaphore 를 Take 하여야한다. 가 Semaphore 를 Take 할때마다 Semaphore 의 Count Value 는 1 씩감소하고 Count Value 가 0 가되면사용할수있는 Resource 가없게된다. 의 Resource 의사용이완료될경우 는 Semaphore 를 Give 하고 Count Value 는 1 증가한다. 그러면다른 가자원을하나더사용할수있게된다. Counting Semaphore 를자원관리에사용하는경우 Count Value 의초기값을사용가능한자원의개수로하여 Counting Semaphore 를 Create 한다. H. xsemaphorecreatecounting() API 함수 FreeRTOS Semaphore 의 Handle 은 xsemaphorehandle 형태의변수에저장된다. Semaphore 는사용하기전에 Create 되어야한다. Counting Semaphore 는 xsemaphorecreatecounting() API 함수에의하여 Create 된다. xsemaphorecreatecounting() API 함수의 Prototype xsemaphorehandle xsemaphorecreatecounting ( unsigned portbase_type uxmaxcount, unsigned portbase_type uxinitialcount) Parameter Name/ Description Returned value uxmaxcount Count 할 Semaphore의최대값. Semaphore가 Count 나 Events Latch로사용될경우 uxmaxcount는 Latch 되는 Event의최대값 uxinitialcount Semaphore 가 Create 될때 Count Value의초기값 Semaphore가 Event counting에사용될경우아직 Event 가발생하지않은경우에 0로설정한다. Semaphore가자원관리에사용될경우사용가능한자원의최대수 (uxmaxcount) 로설정한다. Returned value NULL이 Return 되는경우에는 Semaphore가 Create 되지못한경우이다. Non-NULL이 Return 된경우이값이 Create 된 Semaphore의 Handle로저장된다.
I. 와 Interrupt 의동기에 Counting Semaphore 를사용하는예 프로그램예 : RT_SW_Interrupt_counting_semaphore 참고요 4. Interrupt Service Routine 에서 Queue 의사용 Interrupt 와 간의동기및 Data Transfer 에 Queue 를이용한다. ISR 에서는 xqueuesendtofrontfromisr(), xqueuesendtobackfromisr(), xqueuereceivefromisr() API 함수를사용한다. xqueuesendtofrontfromisr() API 함수의 Prototype portbase_type xqueuesendtofrontfromisr ( xqueuhandle xqueue, void *pvitemtoqueue, portbase_type *pxhigherprioritywoken); xqueuesendtobackfromisr() API 함수의 Prototype portbase_type xqueuesendtofrontfromisr ( xqueuhandle xqueue, void *pvitemtoqueue, portbase_type *pxhigherprioritywoken); Parameter Name/ Returned value xqueue pvitemtoqueue Description Data를보낼 (Written) Queue의 Handle, Queue Handle은 Queue가생성될때 Return 된다. Queue에복사될 Data의 Pointer 각 Item의 Size는 Queue 가생성될때결정된다. pxhigherprioritywoken Semaphore 를사용할수있을때까지기다리는 가하나또는여러개일수있다. 만약 xqueuesendtofrontfromisr() 나 xqueuesendtobackfromisr() 에의하여 unblocked 되는 의 Priority 가 Interrupt 에의하여실행이중단된 보다높거나같은경우함수는 *pxhigherprioritywoken 을 pdtrue 로 Set 한다. *pxhigherprioritywoken 이 pdtrue
로 Set 된경우 Interrupt ISR 에서직접가장 Priority 가높은 로직접 Context Switching 할수있다. Returned value 2 가지가능한값을갖는다. pdpass: Data 가성공적으로 Queue 에보내진경우 errqueue_full: Queue 가 Full 상태라 Data 가 Queue 에보내지지못한경우 A. Interrupt 와 Queue 를이용한 Serial 통신프로그램예 Serial 통신에사용하는 UART Driver 는 Interrupt 와 Queue 를이용하는대표적인예이다. Serial 통신예에서 Transmit Interrupt Handler 와 Receive Interrupt Handler 는 Queue 를통하여문자를주고받는다. Serial.c 프로그램예 : RT_serial_comm 참고요 // USART0 를사용하도록설정함 /* BASIC INTERRUPT DRIVEN SERIAL PORT DRIVER. */ static xqueuehandle xrxedchars; static xqueuehandle xcharsfortx; /*--------------------------------------------------*/ xcomporthandle xserialportinitminimal( unsigned long ulwantedbaud, unsigned portbase_type uxqueuelength ) unsigned long ulbaudratecounter; unsigned char ucbyte; portenter_critical(); /* Create the queues used by the com test task. */ xrxedchars = xqueuecreate( uxqueuelength, ( unsigned portbase_type ) sizeof( signed char ) ); xcharsfortx = xqueuecreate( uxqueuelength, ( unsigned portbase_type ) sizeof( signed char ) ); // Calculate the baud rate register value from the equation // in the data sheet. ulbaudratecounter = ( configcpu_clock_hz /
( serbaud_div_constant * ulwantedbaud ) ) ( unsigned long ) 1; /* Set the baud rate. */ ucbyte = ( unsigned char ) ( ulbaudratecounter & ( unsigned long ) 0xff ); UBRR0L = ucbyte; ulbaudratecounter >>= ( unsigned long ) 8; ucbyte = ( unsigned char ) ( ulbaudratecounter & ( unsigned long ) 0xff ); UBRR0H = ucbyte; // Enable the Rx interrupt. The Tx interrupt will get enabled // later. Also enable the Rx and Tx. UCSR0B = ( serrx_int_enable serrx_enable sertx_enable ); /* Set the data bits to 8. */ // UCSR0C = ( serucsrc_select sereight_data_bits ); UCSR0C = ( sereight_data_bits ); portexit_critical(); // Unlike other ports, this serial code does not allow for more // than one com port. We therefore don't return a pointer to a // port structure and can instead just return NULL. return NULL; //------------------------------------------------- signed portbase_type xserialgetchar( xcomporthandle pxport, signed char *pcrxedchar, portticktype xblocktime ) /* Only one port is supported. */ ( void ) pxport; // Get the next character from the buffer. Return false if no // characters are available, or arrive before xblocktime expires. if( xqueuereceive( xrxedchars, pcrxedchar, xblocktime ) ) return pdtrue; else
return pdfalse; //------------------------------------------------- signed portbase_type xserialputchar( xcomporthandle pxport, signed char coutchar, portticktype xblocktime ) /* Only one port is supported. */ ( void ) pxport; // Return false if after the block time there is no room on the // Tx queue. if( xqueuesend( xcharsfortx, &coutchar, xblocktime )!= pdpass ) return pdfail; vinterrupton(); return pdpass; //------------------------------------------------- SIGNAL( SIG_UART0_RECV ) signed char cchar; signed portbase_type xhigherprioritywoken = pdfalse; // Get the character and post it on the queue of Rxed characters. // If the post causes a task to wake force a context switch as the // woken task may have a higher priority than the task we // have interrupted. cchar = UDR0; xqueuesendfromisr( xrxedchars, &cchar, xhigherprioritywoken ); if( xhigherprioritywoken!= pdfalse ) taskyield();
/*-------------------------------------------------*/ SIGNAL( SIG_UART0_DATA ) signed char cchar, cwoken; if( xqueuereceivefromisr( xcharsfortx, &cchar, &cwoken ) == pdtrue ) /* Send the next character queued for Tx. */ UDR0 = cchar; else /* Queue empty, nothing to send. */ vinterruptoff(); 5. Interrupt Nesting 최근의 FreeRTOS Ports 는 Interrupt Nest 를허용한다. 이경우아래에서설명하는하나또는두개의상수를 FreeRTOSConfig.h 에서정의하여주어야한다. configkernel_interrupt_priority Tick Interrupt 에서사용하는 Interrupt Priority 를설정한다. 만약 configmax_syscall_interrupt_priority 상수를사용하지않는경우에는 interrupt-safe FreeRTOS API 를사용하는 Interrupt 는이 Priority 에서실행된다. configmax_syscall_interrupt_priority interrupt-safe FreeRTOS API 를 Call 할수있는가장높은 Interrupt Priority 를설정한다. Full Interrupt Nesting Model 은 configkernel_interrupt_priority 보다더높은값으로 configmax_syscall_interrupt_priority 를설정함으로서구현된다. FreeRTOS 에서는일반적으로아래의예와같이 Interrupt 를사용할수있다. 그러나특정한프로세서에적용하는경우프로세서에서제공하는 Interrupt 에대하여이해하고응용하여야한다.
configmax_syscall_interrupt_priority = 3 configkernel_interrupt_priority = 1 API 함수를사용하지않는 Interrupt는어떤 Priority에서도사용할수있고 Nesting 할수있다. Priority 7 Priority 6 Priority 5 Priority 4 Priority 3 Priority 2 Priority 1 이영역 Priority의 Interrupt는 Kernel에의한지연없이실행되고 Nesting 할수있다. 그러나 API 함수는사용할수없다. 이영역의 Interrupt는 API 함수를사용할수있고, Nesting 할수있다. 그러나 Critical Section으로 Mask 되어야한다.