7 장. 인터럽트의동작 한국산업기술대학교 이응혁 ehlee@kpu.ac.kr WWW.ROBOTICSLAB.CO.KR 1 7.1 인터럽트 (Interrupt) 개요 인터럽트개념 프로그램이수행되고있는동안에어떤조건이발생하여수행중인프로그램을일시적으로중지시키게만드는조건이나사건의발생 비동기적으로처리 다른프로그램이수행되는동안여러개의사건을처리할수있는메커니즘 인터럽트가발생하면마이크로컨트롤러는현재수행중인프로그램을일시중단하고, 인터럽트처리를위한프로그램을수행한후에다시원래의프로그램으로복귀 ISR(Intertupt Service Routine ) 또는 Interrupt handler : 인터럽트처리하는프로그램 2 1
7.1 인터럽트 (Interrupt) 개요 주프로그램과인터럽트프로그램의차이 < 인터럽트가없는프로그램실행 > < 인터럽트가있는프로그램실행 > 인터럽트가발생할때주프로그램은일시적으로정지하고 ISR로분기하고, ISR이실행되고연산이수행된후에 ISR 프로그램이종료되면, 주프로그램의중지된부분부터다시수행 인터럽트의종료는 인터럽트로부터의복귀 (return from interrupt):rett 명령에의해수행 Background/Foreground 3 7.1 인터럽트 (Interrupt) 개요 인터럽트의종류 인터럽트발생원인에의한분류 하드웨어인터럽트 소프트웨어인터럽트 내부인터럽트 외부인터럽트 : 마이크로컨트롤러내부의기능에의해발생 : 마이크로컨트롤러외부에부가된소자에의해발생 4 2
7.1 인터럽트 (Interrupt) 개요 인터럽트처리방식에의한분류 일반적인인터럽트 (/INT) 프로그램에의하여인터럽트의요청을받아들이지않고무시할수있는구조의인터럽트 (maskable interrupt) 를의미 우선처리메커니즘 우선처리메커니즘의경우보통인터럽트를허용하는방법은인터럽트마스크레지스터또는인터럽트허용레지스터를사용하여각각의인터럽트를개별적허용하고이것들을다시전체적으로허용함. 차단불가능인터럽트 (/NMI) 프로그램에의해어떤방법으로도인터럽트요청이차단될수없는인터럽트 (non-maskable interrupt) 를의미 전원이상이나비상정지스위치등과같이시스템에치명적인오류를대비하기위해주로사용 5 7.1 인터럽트 (Interrupt) 개요 인터럽트의제어및처리절차 벡터형인터럽트인터럽트가발생할때마다인터럽트를요청한장치가인터럽트벡터를마이크로컨트롤러에게전송하는방식각주변장치가각각의인터럽트신호선을가지고있고, 각주변장치가인터럽트를요청하면마이크로컨트롤러는각각의인터럽트에따라미리지정된인터럽트벡터를가지고있어즉시해당인터럽트서비스루틴을찾아가는방식 인터럽트처리응답시간이빠르고, AVR에구현된방식 인터럽트의우선순위 2 개이상의주변장치가동시에마이크로컨트롤러에인터럽트를요구하는경우우선순위를미리정하여번에하나의인터럽트를선택하여처리우선순위가높은인터럽트의처리중 -> 낮은순위의인터럽트는대기상태우선순위가높은인터럽트의처리끝 -> 낮은순위의인터럽트는미리지정된우선순위에의해처리 6 3
7.1 인터럽트 (Interrupt) 개요 인터럽트처리순서 Interrupt Vector Table JUMP ISR0 JUMP ISR1 JUMP ISRn 1 IVT 참조 2 3 IRQ1요청 5 Main 프로그램복귀 JUMP ISR Application Program ISR 실행 ISR0 ISR1 4 ISRn Interrupt Service Routine 7 인터럽트의종류 리셋을포함하여총 28종의인터럽트소스가존재 외부인터럽트 5개 타이머 / 카운터0,2(8-비트 ) 에관련된인터럽트각 2개 타이머 / 카운터1,3(16-비트 ) 에관련된인터럽트각 4개 USART0와 USART1에관련된인터럽트각 3개 기타인터럽트 (EEPROM, 아날로그비교기, 등 ) 4개인터럽트의동작형태 인터럽트가발생하면관련플래그비트를 1로세트하여트리거시키는형태 프로그램카운터가실제인터럽트벡터로지정되어인터럽트처리루틴을수행 해당플래그는하드웨어에의해자동으로 0으로클리어됨. 인터럽트플래그는해당비트에 1을써넣음으로써 0으로클리어할수있음. 인터럽트마스크레지스터또는 SREG 레지스터를통해금지상태로설정하여놓았더라도인터럽트가발생하면해당인터럽트플래그가 1로설정되어인터럽트대기상태로되며, 나중에인터럽트가허가상태로설정될때해당인터럽트가처리됨. 인터럽트조건이발생한동안에만인터럽트를트리거하는형태 인터럽트는인터럽트발생조건이사라지면인터럽트요청도없어지므로나중에인터럽트가다시허용상태로되더라도인터럽트는요청되지않음. 8 4
인터럽트의종류및인터럽트벡터 벡터번호 벡터주소 인터럽트소스 인터럽트발생조건 ( 우선순위 ) 1 0x0000 RESET 외부핀, 전원투입리셋, 저전압검출리셋, 워치독리셋,JTAGAVR리셋 2 0x0002 INT0 외부인터럽트 0 3 0x0004 INT1 외부인터럽트 1 4 0x0006 INT2 외부인터럽트 2 5 0x0008 PCINT0 핀변화인터럽트 0 6 0x000A PCINT1 핀변화인터럽트 1 7 0x000C TIMER3 CAPT 타이머 / 카운터3 입력캡쳐 8 0x000E TIMER3 COMPA 타이머 / 카운터3 비교일치A 9 0x0010 TIMER3 COMPB 타이머 / 카운터3 비교일치B 10 0x0012 TIMER3 OVF 타이머 / 카운터3 오버플로우 11 0x0014 TIMER2 COMP 타이머 / 카운터2 비교일치 12 0x0016 TIMER2 OVF 타이머 / 카운터2 오버플로우 13 0x0018 TIMER1 CAPT 타이머 / 카운터1 입력캡쳐 14 0x001A TIMER1 COMPA 타이머 / 카운터1 비교일치A 15 0x001C TIMER1 COMPB 타이머 / 카운터1 비교일치B 16 0x001E TIMER1 OVF 타이머 / 카운터1 오벌플로우 17 0x0020 TIMER0 COMP 타이머 / 카운터0 비교일치 18 0x0022 TIMER0 OVF 타이머 / 카운터0 오버플로우 19 0x0024 SPI, STC 직렬전송완료 20 0x0026 USART0, RXC USART0, 수신완료 21 0x0028 USART1, RXC USART1, 수신완료 22 0x002A USART0, UDRE USART0, 데이터레지스터비움 23 0x002C USART1, UDRE USART1, 데이터레지스터비움 24 0x002E USART0, TXC USART0, 송신완료 25 0x0030 USART1, TXC USART1, 송신완료 26 0x0032 EE_RDY EEPROM 준비 27 0x0034 ANA_COMP 아날로그비교기 28 0x0036 SPM_RDY 저장프로그램메모리준비 9 리셋및인터럽트벡터의배치 BOOTRST IVSEL 리셋벡터주소 인터럽트벡터의시작주소 1 0 0x0000 0x0002 1 1 0x0000 부트리셋주소 + 0x0002 0 0 부트리셋주소 0 1 부트리셋주소 리셋및인터럽트벡터는 BOOTRST와 IVSEL 비트의조합에의해가변적으로배치 부트리셋주소 : 부트로더섹션의크기의설정에따라달라짐. ( 표 2.22 참조 ) 예 ) BOOTSZ1 0 = 00 이면부트사이즈는 1024워드로되어부트리셋주소는 0x1C00이됨. 일반적인 ATmega162에서는 BOOTRST 비트는 1로설정되고, IVSEL은 0으로설정 10 5
인터럽트벡터의배치인터럽트벡터는응용프로그램섹션과부트로더섹션사이에서배치가능 일반인터럽트제어레지스터 (GICR, General e Interrupt Control o Register) 를사용 IVSEL과 IVCE 비트를사용 IVSEL ( 인터럽트벡터선택, Interrupt Vector Select) IVSEL = 0 : 인터럽트벡터는응용프로그램섹션인플래시메모리의시작부분에위치 IVSEL = 1 : 인터럽트벡터는부트로더섹션의시작부분에위치 IVCE ( 인터럽트벡터변경허가, Interrupt Vector Change Enable) IVSEL 비트의변경을허가하기위해서 IVCE 비트는 1로설정되어있어야함. 11 외부인터럽트의개요 5개의외부인터럽트입력 : INT0, INT1, INT2, PCI0, PCI1 INT1와 INT0은상승 / 하강에지및 Low 상태에의해인터럽트발생 INT2 는상승 / 하강에지에의해서만인터럽트발생 PCI0 는 PCINT7~0핀에변화가발생하면인터럽트발생 PC11은 PCINT15~8핀에변화가발생하면인터럽트발생 INT0, INT1이에지방식에의한인터럽트발생방법으로설정되면, I/O클럭에동기를맞추어인터럽트가발생 INT0, INT1이레벨변화방식으로설정되고, 에지트리거방식으로설정된 INT2와 PCINT15~0 핀의변화가일어나는경우의 PCI 인터럽트들은비동기적으로인터럽트가발생비동기적으로발생하는인터럽트들은휴면모드를제외하고슬립모드를해제하는수단으로사용가능 INT0와 INT1이에지방식에의해설정되면이는 I/O 클럭을필요로하므로이것들은 I/O 클럭이차단되는휴면모드이외의슬립모드에서는슬립모드를해제하는수단으로사용할수없음. 레벨변화방식으로사용되는인터럽트가전원차단모드의해제수단으로사용되는경우에는좀더긴인터럽트신호가요구됨. -> 슬립모드를해제하고인터럽트가발생되려면충분히긴시간동안인터럽트신호가 L 상태로입력되어야함. 12 6
외부인터럽트제어레지스터 외부인터럽트레지스터 MCUCR 설명 MCU 제어레지스터 EMCUCR 확장 MCU 제어레지스터 GICR 일반인터럽트제어레지스터 GIFR 일반인터럽트플래그레지스터 PCMSK1 핀변경마스크레지스터 1 PCMSK0 핀변경마스크레지스터 0 MCUCR 제어레지스터 외부인터럽트 INT0, INT1 핀으로입력되는신호에인터럽트트리거방법을설정하는용도로사용, MCU 의제어에관련된기능을포함, 13 ISC11, ISC10( 인터럽트감지제어비트,Interrupt Sense Control Bit 1 and Bit0) 외부인터럽트 1에대해인터럽트발생방식을결정 외부인터럽트 1 은 SREG I( 글로벌인터럽트허가비트 ) 와 GICR( 인터럽트마스크레지스터 ) 의해당비트가 1로설정되어있는상황에서외부 INT1 핀에인터럽트신호가인가되면인터럽트활성화 ISC11 ISC10 인터럽트발생방식 0 0 INT1 핀의 L상태입력이인터럽트를발생 0 1 INT1 핀에서어떠한논리의변화가발생하더라도인터럽트를발생 1 0 INT1 핀에하강에지의신호가입력되면인터러트가발생 1 1 INT1 핀에상승에지의신호가입력되면인터럽트가발생 ISC01, ISC00 ( 인터럽트감지제어비트, Interrupt Sense Control Bit 1 and Bit0) 외부인터럽트 1 에대해인터럽트발생방식을결정 INT1 과동일한방법으로방생방식결정 14 7
EMCUCR 제어레지스터 외부인터럽트 INT2 핀으로입력되는신호에대한인터럽트트리거방법을설정, MCU 의제어에관련된기능을포함 비트 0 : ISC2 ( 인터럽트감지제어비트, Interrupt Sense Control Bit 2) ISC2 = 0 : INT2는하강에지에서인터럽트발생 ISC2 = 1 : INT2는상승에지에서인터럽트발생 SREG I ( 글로벌인터럽트허용비트 ) 와인터럽트마스크레지스터 GICR 의해당비트가설정되어있을때, 외부 INT2 신호가인가되면인터럽트활성화 INT2는 INT0, INT1과는달리클럭에비동기적으로동작한다. 이로인하여 INT2에인가되는입력펄스의주기는다음표와같이최소한 50ns 이상이어야함. 15 일반인터럽트제어레지스터 (GICR, General Interrupt Control Register) 외부인터럽트를개별적으로허가하는비트인터럽트벡터를제어 INT2~INT0 ( 외부인터럽트 n 허가 ) INT2~0 비트는외부인터럽트를각각허가하는용도로사용되는비트 INTx =1 : 외부인터럽트허가, INTx =0 : 외부인터럽트금지 SREG I ( 글로벌인터럽트허용비트 ) 가 1 로설정되어있어야함. PCIE1~PCIE0 ( 핀변경인터럽트 n 허가 ) PCIE1~0 비트는핀변경인터럽트를각각허가하는용도로사용되는비트 PCIEx =1 : 인터럽트허가, PCIEx =0 : 인터럽트금지 SREG I( 글로벌인터럽트허용비트 ) 가 1로설정되어있어야함. 16 8
일반인터럽트플래그레지스터 (GIFR, General Interrupt Flag Register) 외부인터럽트와핀변경인터럽트의발생상태를알려주는상태레지스터 INTF1~INTF0 ( 외부인터럽트플래그 1, 0) INT1,INT0에인터럽트가발생 INTF1~0=1로세트 SREG I =1, GICR의해당 INT비트 =1 해당인터럽트벡터로점프, ISR을수행함. 플래그비트는다시 0으로클리어됨. 비트 5 : INTF2 ( 외부인터럽트플래그 2) INT2에인터럽트신호발생 INTF2 = 1로세트 INT2 인터럽트가금지된상태에서슬립모드로들어갈경우이핀의입력버퍼는금지되어서 INTF2 플래그를 1로설정하는내부신호에논리변화를일으키게됨.( 유의사항 ) 비트 4-3 : PCIF1-0 ( 핀변경인터럽트플래그 1, 0) PCINT15-0 핀에논리변화가일어나면인터럽트가발생 PCIF1~0 = 1로세트 SREG I =1, GICR의해당 INT 비트 =1 해당인터럽트벡터로점프, ISR을수행함. 플래그비트는다시 0으로클리어됨. 17 핀변경레지스터 (PCMSK, Pin Change Mask Register) 16 개의 PC(Pin Change) INT15~0 핀들중실제 PCI 인터럽트로사용할핀을설정하는레지스터 PCMSK1 레지스터 : PCINT15~8 중에서어느핀을 PCI1 인터럽트를발생할지설정 PCMSK0 레지스터 : PCINT7~0 중에서어느핀을 PCI0 인터럽트를발생할지설정 각비트 = 1 : 해당핀을인터럽트핀으로허가 각비트 = 0 : 해당핀을인터럽트핀으로금지 PCINT 인터럽트는에지변화방식으로감지되며, 상승 / 하강에서모두동작 18 9
7.3 ATmega162 의인터럽트처리 인터럽트처리메카니즘 인터럽트가발생하였을때 MCU 내부에서의동작 현재명령어의수행을마침 스택에 PC를저장 현재인터럽트상태를내부적으로저장 다른인터럽트가받아들여지지는않는다. 즉, 블록킹됨 ISR의벡터주소가 PC에적재됨 ISR이수행 ISR은인터럽트로복귀 (RETI) 명령어로끝나게된다. 이명령으로인해스택으로부터 PC의이전값과인터럽트상태의이전값을되찾게되어, 주프로그램의수행이중단되었던곳부터다시계속수행함. 19 7.3 ATmega162 의인터럽트처리 ATmega 162 의인터럽트처리메카니즘 ATmega162 에서의인터럽트처리는정해진우선순위에의해처리 ATmega162 에서는여러인터럽트기동시에발생하였을때우선순위가높은인터럽트가먼저처리되고, 이우선순위는변경이불가능 인터럽트가발생하면인터럽트에해당하는플래그비트가세트, 이플래그비트에의해인터럽트가요청, 전체인터럽트허가비트 I 와해당인터럽트허가비트가모두 1 로설정되어있으면, 인터럽트가요청되어인터럽트벡터의주소를찾아가인터럽트서비스루틴 (ISR) 을수행하게됨 ISR 이수행되고있을때, ATmega162 는자동적으로전체인터럽트허가비트 (SREG의 I 비트 ) 를클리어모든인터럽트의발생을금지서비스루틴의종료와함께인터럽트를허용 ATmega162 가 RETI 명령에의하여 ISR 의실행을마치고주프로그램으로복귀하는데에도 4 클럭사이클이소요된다. 이시간동안에 PC 의값이스택으로부터복구됨 20 10
7.4 CodeVision 을이용한 ISR 의작성 ISR 의작성 인터럽트의서비스는벡터주소라는교유번지에서시작 인터럽트벡터에는인터럽트기능을서비스하기위한프로그램이위치해있어야함 인터럽트서비스루틴이호출되기위해서는 C 언어에서인터럽트서비스루틴이올바르게선언되어있어야한다. 인터럽트서비스루틴의선언 : interrupt [n] void int_func_name (void) 타이머 0의오버플로우인터럽트에대한인터럽트서비스루틴의작성예 // Called automatically on TIMER0 overflow unsigned int interrupt_cnt; unsigned char second; interrupt [18] void timer0_overflow(void) if (++interrupt_cnt == 4000) // count to 4000 second++; // second counter Interrupt_cnt = 0; // clear int counter 21 7.4 CodeVision 을이용한 ISR 의작성 인터럽트의허가및금지방법 인터럽트를사용하려면전체인터럽트허가비트 (SREG의 I 비트 ) 를 1로설정 SREG의 I를이용하여전체인터럽트를허가함. SREG = 0x80; // SREG의 7비트를 1로설정하여전체인터럽트를허가함 어셈블리명령어를이용하여인터럽트를허가하고금지하는방법 sei 와 cli 의명령어를사용 #asm("sei"); // 전체인터럽트허가 #asm("cli"); // 전체인터럽트금지 #define 전처리기를이용하여 C 언어함수로구현하는방법 <mega162.h> 에포함하여사용 #define sei() (#asm("sei")) #define cli() (#asm("cli")) 22 11
ISR 의초기화과정 MCUCR 레지스터의비트설정을통한외부인터럽트의트리거모드설정 GICR 레지스터의비트설정을통한사용하고자하는인터럽트의허가 SREG의 I 비트의설정을통한전체인터럽트를허가 예 ) 외부 INT0 핀의신호가하강에지에의해인터럽트를발생하도록초기화하는과정 void Interrupt_init(void) MCUCR = 0x02; // ISC01=1, ISC00=0 ( 외부인터럽트 0 하강에지트리거 ) GICR = 0x40; // INT0 비트설정 ( 외부인터럽트0 허가 ) sei(); // 전체인터럽트허가 ISR 의초기화과정 : 비트단위의제어 MCUCR 레지스터에는인터럽트제어외에여러가지기능을포함하고있어, 이상과같이레지스터에직접바이트단위로쓰게되면기존의정보는소실됨. 비트단위로제어하는것이편리함. 23 MCUCR 레지스터는그림과같은비트구성을가지고있으므로각비트명에대해다음과같이선언 #define ISC00 0 #define ISC01 1 #define ISC10 2 #define ISC11 3... ISC01 비트만 1 로설정하기위한과정 왼쪽시프트연산자 << 를사용 1<<ISC01 ISC01 은 1 이므로 1<<1 // 0b00000001 을 1 비트시프트 0b00000010 최종적으로 MCUCR 의 ISC01 비트가세트됨 24 12
MCUCR 레지스터의 ISC01 을세트하는프로그램 MCUCR = 1<<ISC01; 이상의비트제어를통해인터럽트초기화함수 Interrupt_init() 의프로그랩을재작성 void Interrupt_init(void) MCUCR = 1<<ISC01; GICR = 1<<INT0; sei(); 25 예제 7-1: 폴링방식의프로그램과인터럽트프로그램의차이점 PORTB 의스위치의신호를계수하여 PORTB 의 LED 에카운트된값을출력하는프로그램작성 #include <mega162.h> #include <delay.h> Byte count, change; bit key7; Byte Exch(void) while(1) key7 = PINB.7; if(key7 == 0) count++; delay_ms(1000); return 1; void main(void) // count 변수의선언및초기화 Count = 0; ex = 0; // 포트 B의상위니블을입력, 하위니블을출력으로설정 // 포트 B의 LED를모두 OFF DDRB = 0x0f; PORTB = 0x0f; while(1) key7 = PINB.7; if(change == 0 && key7 == 1) change = Exch(); if(change == 1 && key7 == 1) change = 0; PORTB = ~(count&0x0f); If(count>= 15) count = 0; 26 13
27 예제 7-2: 외부인터럽트 0 서비스루틴의작성 VCC AR5 470 R16 470 R17 470 R18 470 R19 470 VCC 1 C D13 LED D14 LED D15 LED D16 LED 19 18 9 1 2 3 4 5 6 7 8 10 11 12 13 14 15 16 17 U19 XTAL1 PA0(AD0/PCINT0) PA1(AD1/PCINT1) XTAL2 PA2(AD2/PCINT2) PA3(AD3/PCINT3) RESET PA4(AD4/PCINT4) PA5(AD5/PCINT5) PB0(OC0/T0) PA6(AD6/PCINT6) PB1(OC2/T1) PA7(AD7/PCINT7) PB2(RXD1/AIN0) PB3(TXD1/AIN1) PC0(A8/PCINT8) PB4(SS/OC3B) PC1(A9/PCINT9) PB5(MOSI) PC2(A10/PCINT10) PB6(MISO) PC3(A11/PCINT11) PB7(SCK) PC4(A12/TCK/PCINT12) PC5(A13/TMS/PCINT13) PC6(A14/TDO/PCINT14) PD0(RXD0) PC7(A15/TDI/PCINT15) PD1(TXD0) PD2(INT0/XCK1) PE0(ICP1/INT2) PD3(INT1/ICP3) PE1(ALE) PD4(TOSC1/XCK0/OC3A) PE2(OC1B) PD5(OC1A/TOSC2) PD6(WR) PD7(RD) 39 38 37 36 35 34 33 32 21 22 23 24 25 26 27 28 31 30 29 PC3 SW1 2 3 4 5 ATmega162 SW1 PC3 INT1 INT0 C37 100nF C32 100nF C22 100nF C23 100nF SW11 SW SW10 SW SW13 SW SW12 SW - 외부인터럽트실험회로 - 28 14
PORTB 에연결된스위치대신에 INT0 핀에연결된스위치의입력을계수하여 PORTB 의 LED 에출력하는프로그램작성 #include <mega162.h> #include <delay.h> Byte count; interrupt [EXT_INT0] void ext_int0(void) count++; void Interrupt_init(void) MCUCR = 1 << ISC01; // MCUCR = 0x02; GICR = 1 << INT0; // GICR = 0x40; SREG = 0x80; // sei() void main(void) DDRB = 0x0f; DDRD = 0x00; count = 0; Interrupt_init(); while(1) PORTB = ~count; if(count >= 15) count = 0; 29 30 15
예제 7-3: 외부인터럽트 0 서비스루틴의작성 ( 인터럽트모드의변경 ) 회로에서 INT0 핀에 Low 신호가입력되면 PORTB 포트에연결되어있는모든 LED 를켜고, 인터럽트가해제되면 LED 는 off 상태를그대로유지하는프로그램작성 #include <mega162.h> #include <delay.h> bit exchange; interrupt [EXT_INT0] void ext_int0(void) exchange = ~exchange; void Interrupt_init(void) MCUCR = 0; // MCUCR = 0x00; GICR = 1 << INT0; // GICR = 0x40; SREG = 0x80; // sei() void main(void) DDRB = 0x0f; DDRD = 0x00; Interrupt_init(); PORTB = 0x0f; exchange = 0; while(1) if(exchange) PORTB = 0x00; else PORTB = 0x0f; 31 예제 7-4: 외부인터럽트 0 의활용 LED가처음에는시프트동작을수행하고있다. 이상황에서외부 INT0 키가눌릴때마다반대의순서로 LED 점등하도록하는프로그램작성 #include <mega162.h> #include <delay.h> bit Direction; interrupt [EXT_INT0] void ext_int0(void) Direction = ~Direction; void PB_LShift(void) int i; Byte Temp; Temp = 0xfe; for(i=0; i<4;i++) delay_ms(500); PORTB = Temp; Temp = (Temp<<1) 0x01; void PB_RShift(void) int i; Byte Temp; Temp = 0xef; for(i = 0; i < 4; i++) delay_ms(500); Temp = (Temp >> 1); PORTB = Temp; void Interrupt_init(void) MCUCR = 1 << ISC01; GICR = 1 << INT0; SREG = 0x80; // sei() void main(void) DDRB = 0x0F; DDRD = 0x00; Interrupt_init(); PORTB = 0x0f; Direction = 0; while(1) if(direction) PB_RShift(); else PB_LShift(); 32 16