반응형

전전실험 8주차 내용으로 인터럽트를 이용한 예제 두개와 과제를 포스팅하겠습니다.

 

개요 : Interrupt에 대한 소개

  • External Interrupt(EICRx, EIMSK, EFIR)에 대한 소개
  • 예제 1 : 인터럽트에 의한 LED ON/OFF
  • 예제 2 : 두 개의 인터럽트가 들어왔을 때
  • 과제1 : Interrupt (up/down count)

배경지식

1. 인터럽트는 하드웨어에서 발생하는 Function call이다.

(vs. Polling : 일정시간 간격으로 확인, 차례대로 검사)(7번에서 더 자세히...)

2. 인터럽트에는 우선순위가 있어 그 중 가장 큰 우선순위는 Reset이다.

3. sREG : AVR status register, 인터럽트의 enable/disable을 결정

(sei() 사용o, cli() 사용x)

EICRx : 인터럽트가 어떨 때 발생이 되는지 설정.(ex rising edge, falling edge)

EIMSK : 어떤 입력을 인터럽트가 걸리는지를 설정

EFIR : INT0~INT7 중에 어떤 인터럽트가 트리거 되었는가?”를 표시함

4. 인터럽트 INT0~3EICRA , INT4~7EICRB이고 EICRAEICRB의 각각 Interrupt register는 서로 다르다.

+ 예를 들어, EICRB=0b00001010;로 설정되면, INT4INT5의 인터럽트가 falling edge일 때 발생)

EICRA bit table
EICRB bit table

5. volatile 키워드에 대한 조사 (+ optimization option)

- 우선 volatile의 사전적인 의미는 휘발성이다. 이름 그대로 volatile 변수는 외부적 요인에 의해서 언제든 값이 바뀔 수 있음을 의미합니다. 이에 따라, compilervolatile로 선언된 변수에 대해서는 최적화를 수행하지 않습니다. Volatile 변수는 레지스터에 로드된 값을 사용하지 않고, 메모리에서 직접 참조합니다.

- 여기서 최적화란 compiler가 속도 향상을 위해 불필요한 메모리 사용을 없애는 것인데, 하드웨어 조작에 있어서 임의로 코드가 제거/조정된다면 원하는 결과를 가져올 수 없을 것입니다. 따라서 volatile 변수로 선언하면 최적화를 수행하지 않고 메모리를 그대로 사용합니다.

- 과제1을 수행할 때 ‘cnt’를 이용해서 숫자의 증/감을 구현할 것인데, volatile이 아니라 register 변수로 선언하게 된다면 인터럽트 루틴에서 변수 내용을 바꿀 때 레지스터 값이 바뀌므로 언제 값이 없어질지 알 수 없습니다. 이런 경우 메모리 변수로 선언하기 위해 volatile을 사용합니다.

- 보통 I/O Port를 특별한 변수 이름으로 지정해놓고 계속적으로 사용하고자 할 때, volatile 키워드를 붙여서 사용합니다.

6. ISR (Interrupt Service Routine) 조사

- 다른 이름으로는 “Interrupt Handler”라고 불립니다. 하드웨어에서 발생한 인터럽트 요청을 소프트웨어적으로 처리를 일컫습니다. ISR은 진행 중인 프로세스를 interrupt하고, 요청을 제어하고 CPU에게 보냅니다. ISR이 끝난 뒤에는 다시 프로세스가 재개됩니다.

- 대표적인 예로는 키보드 입력이 있습니다. 키보드가 눌릴 때 마다 ISR은 입력을 처리합니다. 만약 키보드에서 를 누른다하면 CPU는 이 정보를 활성화된 프로그램에 보내서 커서를 오른쪽으로 한 칸 옮깁니다. 우선순위(인터럽트 테이블)를 미리 정해놓는데 보통 키보드가 높은 우선순위에 있습니다.

- AVR의 경우 vector no.1external pin, power-on reset, brown-out reset, watchdog reset, and JTAG AVR reset이 있습니다.

7. Interrupt vs. Polling Comparison

no.

Interrupt

Polling

1.

인터럽트는 장치(device)CPU에게 처리를 요청합니다.

폴링은 장치가 요청하든 말든, CPU가 지속적으로 확인을 합니다.

2.

프로토콜이 아닌 하드웨어적 원리로 작동됩니다.

프로토콜(데이터 통신을 위한 통신 규약, 예를 들어 신호 송신의 순서, 데이터 표현법 등..)입니다.

3.

장치의 요청을 interrupt handler를 통해 처리합니다.

CPU가 직접 장치를 처리합니다.

4.

폴링과 다르게 언제든(any time) 권한을 받을 수 있습니다.

반면, CPU가 특정 인터벌에 따라 지속적으로 정합니다.

5.

IRQ(Interrupt Request Line)은 장치가 서비스 요청한다는 지시를 명시합니다.

폴링에는 Command Ready Bit가 있는데, 이를 통해서 장치가 서비스 요청함을 알 수 있습니다.

6.

그 어떤 장치가 요청하면 간단하게 프로세서는 인터럽트를 받을 수 있습니다.

지속적으로 확인하기 때문에 프로세서가 많은 시간을 command ready bit를 확인하는 곳에 낭비합니다.

- 위의 표를 통해 파악할 수 있는 사실은

Interrupt는 폴링보다 다소 복잡하지만, 처리의 정확한 시간(timing)을 요구하는 곳에 적합하고, 지속적인 체크가 필요 없기 때문에 프로세서 시간 낭비(시스템 성능 저하)가 없습니다.

Polling은 구현이 상대적으로 간단하지만, 리소스(시간)를 많이 잡기 때문에 성능 저하의 큰 원인이 됩니다. 거기에 소프트웨어의 처리이기 때문에 속도가 느리다는 단점이 있습니다.

예제1. 인터럽트에 의한 LED ON/OFF

그림1. 포트/인터럽트 선언 및 초기화

- (그림1)에선 init_port()를 사용해서 포트(LED)에 대한 초기화와 init_interrupt()를 사용해서 INT5(인터럽트)에 대한 초기화를 설정했습니다.

- DDRF = 0xF0을 통해서 PF7~4 , LED를 출력으로 설정하였고, DDRE = 0x00을 이용해서 모두 입력으로 받고 그 중에서 INT4,5를 사용합니다.

-EICRB = 0x08;를 이용해서 INT5falling edge에 사용하는 것으로 설정, EIMSK = 0x20;(0b0010_0000)를 써서 INT5를 씀을 선언합니다.

그림2. 소스코드

- (그림2)에서 포트/인터럽트 초기화 이후 sei()를 이용해서 인터럽트를 set해줬습니다.

- while(1) , 무한루프를 돌게 만들었고 ISR(INT5_vect)를 통해서 INT5 인터럽트가 들어오게 된다면 PORTF(LED)의 여집합 상태를 갖게 만들었습니다.

결과를 확인해보면,

버튼 클릭 (INT5누를 시 LED가 켜지는 것 확인(다시 누르면 꺼진다, 여집합))

예제 2 : 두 개의 인터럽트가 들어왔을 때

그림1. 포트/인터럽트 선언 및 초기화 with optimization option O0
그림2. 소스코드

- (그림2)실험은 인터럽트 45번이 동시에 눌렸을 때 LED의 변화를 보는 실습이다. 하나를 누를 때는 동작하지 않고, 두 버튼이 모두 눌러지게 되면 인터럽트가 활성되기 전 Flag 값을 확인해 LED1,2ON합니다.

- 인터럽트 활성화 후에는 VECTOR TABLE에 따라 INT4번이 먼저 실행된 후 5번이 실행되는 것을 알 수 있었습니다. LED 3,4가 켜지고, INT4번은 PORTF 상태의 여집합으로 켜지는 것이었고, INT5번은 LED1,4가 켜지 는 것이었다. 인터럽트가 발생했을 때 FLAG값은 0으로 변하므로 인터럽트 해제 전에 모든 LED를 끄고(PORTF = 0x00) 인터럽트를 해제하게 됩니다.

결과를 확인해보면,

●●○○
○○●●
●○○●

위의 그림은 INT4/5누를 시 LED가 켜지는 것 확인(●●○○ -> ○○●● -> ●○○● (4321)순서

과제1 : Interrupt (up/down count)

그림1. 포트(7_segment)/인터럽트 선언 및 초기화

- (그림1)에서 지난 7_segment 수업때 사용했던 SEGNP, SEGWP, SEGPOS를 가져왔습니다. DDRA/E/G를 통해서 LED 사용에 대한 초기화를 했습니다.

- 예제에서 사용하였던 interrupt 초기화 함수를 사용하였습니다.

- 앞서 설명했던 volatile 키워드를 사용했습니다. Memory에 저장해서 인터럽트 루틴 안에서 ‘cnt’를 사용해서 숫자 counting’을 할 것입니다. 다시 기능에 대한 요약을 하자면 compiler가 최적화 하지 않고 memory에서 변수를 사용하는 것입니다.

그림2. 소스코드_7 segment에 사용한 time_check() 함수

- (그림2)은 저번 7 segment 과제를 수행하면서 사용했던 display용 함수이고, 이번 과제는 정확한 시간을 계산하는 것이 아니기 때문에 delay 함수에 각각 5ms 씩 넉넉하게 시간을 부여했습니다.

그림3. 소스코드

- INT4를 누르면 증가, INT5를 누르면 감소하게 만드는 실험이었습니다. (그림3)의 코드를 보면 우선 포트(LED), 인터럽트에 대한 초기화를 진행하였습니다. 숫자판에 사용될 a, b, c, d도 각각 int형으로 선언하고 0으로 초기화했습니다.

- 인터럽트를 사용할 것이기 때문에 cntwhile문 안에서 제어하지 않습니다. while문에서는 display를 위해서 time_check 함수만 계속 실행시키고, 각각 자릿수에 들어갈 숫자들만 계산을 해줍니다. 최고자리수인 1000의 자리는 1000으로 나눈 몫을 통해 구하고, 나머지는 구할 숫자의 값으로 나누고 %10(나머지 연산)을 통해 자릿수의 값을 구했습니다. 추가적으로, 9999를 넘어서면 0으로 보내주고 0에서 더 낮아지면 9999로 보내주기 위해 if문을 사용하였습니다.

- ISR(INT4_vect)를 통해서 volatile 변수로 선언된 cnt의 값을 ‘+1’해주었습니다. 마찬가지로 ISR(INT5_vect)를 통해서 cnt 값의 감소를 구현하였습니다. 이렇게 인터럽트 루틴에서 사용되는 변수는 volatile 변수로 선언하여야합니다. (이유는 앞서 설명한 메모리 저장)

결과를 확인해보면,

INT4를 누르면 +1(증가)
INT5를 누르면 –1(감소)

각각 0->9999, 9999->0 으로 넘어가는 상황

이번 실험을 요약하자면

-(예제 1)을 통해 인터럽트가 들어올 때, PORTF 상태의 여집합 상태가 되도록 만들었습니다. 또한, 보드에서 명시된 것과 다르게 schematic을 확인해보면 INT4INT5의 명시가 거꾸로 되어있는 것을 확인할 수 있었습니다.

-(예제 2)를 진행하였습니다. 인터럽트 활성화 후에 VECTOR TABLE에 따라 INT4가 실행된 후 5번이 실행되는 것을 알 수 있었습니다. 인터럽트가 발생했을 때 FLAG값은 0으로 변하는 것을 확인하였습니다.

-과제_interrupt up and down을 진행하였습니다. 여기서 volatile 키워드를 사용하였고 volatile 변수는 C/C++에서 최적화 등 컴파일러의 재량을 제한하는 역할을 한다는 것을 확인했습니다. , volatile로 선언된 변수는 최적화에서 제외가 됩니다. 또한 OS와 연관되어 장치제어를 위해 지정한 주소를 직접 엑세스하는 방식을 설정할 수 있습니다.

Reference

- volatile 키워드
https://ko.wikipedia.org/wiki/Volatile_%EB%B3%80%EC%88%98
- ATmega128
http://www.kjit.bme.hu/images/stories/targyak/mechatronika_robot_mcu/SH_kepzes/atmel_atmega128_manual.pdf
+ schematic.pdf
- ISR 조사
https://techterms.com/definition/isr
- Interrupt vs. Polling
https://www.geeksforgeeks.org/difference-between-interrupt-and-polling/
반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기