'인터럽트'에 해당되는 글 6건

  1. 2018.01.31 왜 RTOS가 필요한가?
  2. 2017.05.08 인터럽트의 처리(2)
  3. 2017.03.18 인터럽트와 volatile 지시자 1
  4. 2017.03.12 인터럽트의 처리(1)
  5. 2017.03.11 Arduino Uno R3의 개요
  6. 2014.04.23 C 언어로 개발시 유의사항


실시간 시스템(real time system)은 임의의 정보가 시스템 내/외부에서 이벤트 혹은 인터럽트(interrupt)의 형태로 시스템에 입력되었을 때 주어진 시간 안에 작업이 완료되어 결과가 주어져야 하는 시스템입니다. 즉, 이벤트 발생과 처리가 실시간으로 이루어지는 시스템입니다. 물론 CPU의 처리 속도를 증가시킬 수도 있지만 속도는 느리더라도 이벤트 처리 시간을 보장할 수 있도록 하여야 한다는 의미입니다.


RTOS(Real Time Operating System) 없이 펌웨어를 제작하여도 모든 작업을 할 수는 있습니다. 예전의 시스템에서 펌웨어(firmware)는 비교적 간단해서 OS의 개념을 적용하지 않고 순차적인 프로그램만으로 작성했어도 그리 문제가 되지 않았습니다. 하지만 최근에 점점 더 소형기기에 들어가는 기능이 다양해지고 Windows OS처럼 이벤트가 수ms 정도에 처리되어도 가능한 상황과 달리, 임베디드 시스템(embedded system)에서는 훨씬 빠른 응답을 요구하여 순차적인 프로그래밍 방식으로는 기능을 구현하기에 어려워졌고 시간도 많이 걸려 다음의 장점으로 여러개의 태스크(task)로 분할하여 개발할 필요가 있다는 것입니다. 왜냐면 여러가지 일을 할 수 있는 순차적인 프로그램 방법으로 구현하면 프로그램의 복잡도가 일의 갯수의 승수에 비례하기 때문입니다.


1. 코드의 개발, 수정, 유지, 보수가 보다 용이합니다.

2. 이벤트에 대해 보다 신속하게 응답할 수 있습니다. - 인터럽트가 발생하면, 진행 중인 태스크 대신 인터럽트를 처리할 태스크를 우선적으로 실행할 수 있습니다.

3. 시스템의 신뢰도와 성능을 높일 수 있습니다.


한편 우선 순위 방식에는 2가지가 있는데, 우선 순위가 높은 태스크가 우선 순위가 낮은 태스크를 잠시 멈추게 하고 프로세서를 차지할 수 있는 권한이 부여되면 최근 대부분 OS가 그렇듯 선점형 우선순위(Preemptive)가 됩니다. 반면에 우선 순위가 높다 하더라도 우선 순위가 낮은 태스크가 완료할 때까지 기다려 주는 방식을 Windows 95이전의 OS와 같은 비선점형 우선순위(Non-preemptive)라 부릅니다.


임베디드 시스템에서는 주로 선점형을 사용하는데, 우선 순위에 따라 프로세서의 사용 권한을 조정하는 것을 스케줄링(Scheduling)라고 부릅니다. 이와 같이 진행 중인 태스크가 바뀌게 되면 컨텍스트(Context)라고 불리는 기존의 태스크에 대한 정보를 저장하고 새로운 태스크를 불러오는 동작을 해야 하는데 이를 컨텍스트 스위칭(context switching)이라고 합니다. 또한 진행 중인 태스크들의 각종 정보를 태스크 컨트롤 블럭(TCB: Task control block)이라고 부릅니다. 다음은 선점형 OS에서 사용하는 스케줄링 방식입니다.


1. Priority Scheduling Algorithm

2. Round-Robin Scheduling Algorithm


이처럼 예전에는 하드웨어 성능과 크기의 제약으로 OS 없이 단순한 기능만 수행 가능했던 것이 하드웨어 성능의 향상으로 다양한 일이 요구되었습니다. 이에 RTOS라는 OS 개념을 도입하여 각각의 태스크에 대한 스케줄링으로 자원을 효율적으로 사용할 수 있게 하고 복잡한 일들을 태스크로 나눔으로써 일을 단순화 시킬 수 있다는 것입니다. 뿐만 아니라 다음과 같은 까다로운 문제가 발생하는데, 이들 문제를 직접해결하기 보다는 멀티태스킹(multi-tasking; 엄밀하게 multi-threadingOS를 도입하는 것이 훨씬 효과적이라는 것입니다. 근래에는 멀티태스킹 뿐 아니라 Network, File System, GUI도 구현하고 있습니다.


1. 태스크간 경쟁의 관리

2. 데드락(deadlock)

3. 우선 순위 역전(priority inversion)

4. 재진입 문제(reentrancy)

5. 태스크간 통신


※ 일반적인 OS vs. RTOS

    • 효율성 / 시간 제약성 : 일반 OS 경우에 태스크들 사이에 효율성을 유지하려고 하지만 real-time OS에서는 태스크에 시간 제약성이 존재하고 이런 시간 제약성 때문에 효율성을 무시하는 경우가 발생하며 효율성은 고려하지 않습니다.

    • 공평성 / 우선순위 : 일반 OS 경우 여러 명의 사용자가 쓰는 경우에는 각 사용자들이 실행하는 프로그램이 태스크로서 수행이 되고 대개의 경우에는 각 태스크가 공평성을 유지하려고 한다. 그러나 real-time OS에서 태스크는 대개 우선순위가 차이가 있도록 하며 이때 태스크 사이의 공평성은 고려하지 않습니다.



결론적으로 
RTOS는 시간의 정확성을 보장하는 멀티태스킹 호출과 인터럽트에 대한 반응시간의 최대값을 보장할 수 있고 실행시간의 편차가 작아야 하는데 즉, 작업의 소요시간을 예측할 수 있어야 합니다. 우선순위가 높은 일이 우선적으로 자원을 분배하여 시간제한 내에 끝날 수 있도록 해야 합니다.


1. 다수의 작업에 우선 순위를 두어 멀티태스킹을 지원합니다.

2. 짧은 interrupt lattency interrupt latency는 인터럽트가 걸려서 인터럽트 핸들러에 도착하기까지의 시간이며 이벤트에 의한 반응 속도가 빠릅니다.

3. 적은 용량의 kernel 사용 - 작고 유연한 구조(10 ~ 50 KB 수준)



'Embedded Programming > Environment' 카테고리의 다른 글

Firmware와 RTOS의 차이점  (0) 2017.12.16
Posted by Nature & Life
Embedded Lecture/Arduino2017. 5. 8. 20:34


인터럽트(interrupt)란 지정된 핀의 신호가 원하는 조건과 일치하면 미리 등록한 인터럽트 callback 함수(ISR; Interrupt Service Routines)를 자동으로 호출해주는 기능입니다. 이 ISR 함수는 사용자가 만든 함수이며, 이때 실행 중이던 loop() 함수 내부의 루틴은 인터럽트 callback 함수가 끝날 때까지 멈추게 됩니다.


즉, 특정 핀의 입력 상태가 바뀔 때 Arduino는 이를 자동으로 감지해서 모든 동작을 잠시 멈춘 다음, ISR(Interrupt Service Routines) 이라 불리는 미리 지정된 함수를 실행하고, 다시 원래 작업으로 복귀한다는 것입니다. 이를 '하드웨어 인터럽트'라 부릅니다. 이 외에도 비슷한 타이머(timer) 인터럽트가 있는데 이는 사용법이 전혀 다르므로 이 글에서 다루진 않습니다.

♧ 타이머 인터럽트 - AVR 칩을 비롯한 임베디드 시스템에서는 시간을 재기 위해서 타이머(Timer)/카운터(Counter)를 내장합니다. 만일 타이머/카운터가 없다면, 소프트웨어적으로 시간지연 함수를 사용해야 함으로써 MCU는 동작을 멈추게 되고, 이로 인해 MCU의 효율을 떨어뜨리게 된다는 것입니다.

뿐만 아니라 이와 같은 내장 타이머/카운터는 정확한 시간을 잴 수 있으며, MCU가 다른 작업과 병행할 수 있고, 미리 설정한 조건에서 타이머 인터럽트도 발생하게 할 수 있다는 것입니다. 이러한 각각 인터럽트들은 동시에 발생할 수도 있으므로 우선 순위를 갖습니다.


Arduino의 인터럽트 핀

Arduino Uno 기준으로 2개의 인터럽트 핀이 할당되어 있습니다. 즉, Number 0 (D2), Number 1 (D3). 몇개의 보드별 지원되는 인터럽트 핀은 다음과 같습니다. 인터럽트 callback(ISR)을 등록할 때 유의할 점은 핀 번호가 아니라 반드시 인터럽트 넘버를 사용한다는 것입니다. 예를 들어, 다음 표에서 Leonardo 보드는 Uno 보다 많은 5개의 인터럽트 핀을 제공합니다. 그리고 다섯번째 인터럽트 핀(int4)은 Number 7이라는 것입니다. 참고로 Arduino Due 보드는 모든 핀에 인터럽트가 지원됩니다.


 보드

 int0

 int1

 int2

 int3

 int4

 int5

 Uno/Ethernet

 2

 3

 x

 x

 x

 x

 Mega2560

 2

 3

 21

 20

 19

 18

 Leonardo

 3

 2

 0

 1

 7

 x


인터럽트 callback 함수(ISR)는 입력 인자가 없고 반환값이 없습니다. 즉 파라미터를 전달하거나 리턴할 수가 없다는 것입니다. 게다가 ISR 함수 내에서는 delay() 함수를 사용할 수 없습니다. 또한 milli second의 시각을 가져오는 함수인 millis()를 사용하더라도 값이 증가하지는 않습니다. delayMicroseconds()의 경우에는 인터럽트에 독립적이므로 정상 동작합니다.


뿐만 아니라 ISR 함수 내에서는 Serial data를 읽을 경우 값이 소실되며, 이전 글에서 언급했던 것처럼 ISR 함수 내에서 업데이트 되는 전역 변수는 volatile로 반드시 선언되어야 합니다. ISR 함수를 만드는 요령은 최대한 짧고 빠르게 수행되도록 간결하게 작성해야 합니다. 왜냐면 이 코드가 길어지는 만큼 CPU는 멈추어 메인 작업을 수행하지 않아 효율적이지 않다는 것입니다.


또한 여러 개의 ISR 함수가 등록되어 있더라도 동시에 수행되지는 않습니다. 블루투스 모듈과의 통신을 위해 주로 사용되는 Software Serial 라이브러리의 경우, 내부적으로 인터럽트를 사용하는 것으로 알려져 있습니다. 따라서 Uno 보드의 경우 D2, D3 에 연결해야만 정상 동작 가능합니다. 따라서 이를 회피하기 위해서 D0, D1 핀에 연결해서 Hardware Serial로 동작시킬 수 있습니다.



Posted by Nature & Life
Embedded Lecture/Arduino2017. 3. 18. 16:33


AVR 칩을 사용하는 Arduino Uno 보드 혹은 다른 임베디드(Embedded) 시스템에서 전역 변수(globlal variable)는 주메모리인 SRAM에 저장되고, 지역 변수(local variable)는 레지스터(register) 영역에 저장됩니다. 그러나 인터럽트 서비스 루틴(ISR)에서의 지역 변수는 SRAM의 Stack 영역에 저장되므로 빠른 인터럽트 처리를 위해서 루틴 내에 잦은 지역 변수의 사용은 바람직하지 않다는 것입니다.


사용자의 코드를 컴파일러(Compiler)는 나름데로 최적화시키는데, 예를 들어 사용자가 전역 변수 지정을 하였지만 그 값이 시종일관 변하지 않는 변수는 컴파일러가 임의로 상수처럼 여겨 레지스터 영역에 저장하게 한다는 것입니다. 물론 한정된 레지스터 영역의 리소스를 낭비하는 것 아니냐고 생각할 수 있지만, 컴파일러 입장에서는 빠른 실행 속도를 위해서 SRAM 보다는 레지스터 영역에 저장한다는 것입니다.


그러나 임베디드 시스템의 경우에 인터럽트 서비스 루틴에서 전역 변수의 갱신이 있었다 하더라도, 컴파일러는 실제 런타임 시 처럼 외부에서 인터럽트가 걸리는 상황이 아니므로 그 값은 변하지 않는다고 생각하여 결국 레지스터에 저장하고 주메모리 상에 변경된 값의 반영이 없다는 것입니다.


이런 상황을 위해서 지시자(directive)인 'volatile'의 선언은 변수형 앞에 두어 컴파일러가 임의로 판단하여 그 변수를 레지스터에 할당하지 못하도록 한다는 것입니다. 즉, 어떤 변수를 volatile로 지정하면 그 변수는 레지스터의 임시 저장소가 아니라 SRAM에서 직접 읽어오도록 컴파일한다는 것입니다.


결론적으로 Arduino 코딩의 경우 보통은 volatile로 정의할 필요는 없으나 인터럽트 서비스 루틴 내부에서 그 값이 변경되는 변수는 반드시 volatile로 선언해야 실시간으로 변경되는 데이터 값을 ISR 루틴 외부에서 읽어올 수 있게 된다는 것입니다.


int pin = 13;

volatile int state = LOW;


void setup()

{

  pinMode(pin, OUTPUT);

  attachInterrupt(0, blink, CHANGE);

}


void loop()

{

  digitalWrite(pin, state);

}


void blink()

{

  state = !state;

}


위의 예제에서 blink() 함수는 인터럽트 서비스 루틴으로 0번 인터럽트 핀의 상태가 변할 때마다 13번에 연결된 LED를 교대로 점멸하게 됩니다. 만일 state 변수에 volatile 선언이 없었다면, 비록 blink() 함수 내에 state 값이 변하더라도 이 함수를 벗어나서는 state 값이 갱신되지 않기 때문에 0번 인터럽트의 상태 변화에 LED의 토글(toggle) 현상이 보이지 않는다는 것입니다.



'Embedded Lecture > Arduino' 카테고리의 다른 글

아날로그 입력 및 온도계 예제  (0) 2017.03.19
아날로그 입력  (0) 2017.03.18
아두이노의 TWI(I2C) 통신  (0) 2017.03.18
아두이노의 시리얼 통신  (0) 2017.03.18
인터럽트의 처리(1)  (0) 2017.03.12
Posted by Nature & Life
Embedded Lecture/Arduino2017. 3. 12. 20:40

 

프로그램은 사용자가 편의를 위해서 코딩합니다. 즉 무엇인가 자동으로 일괄처리를 하기 위함인데 이는 마이크로 프로세서에서도 마찬가지 입니다. 하지만 프로그램 혹은 알고리즘(algorithm)은 일방적이기 보다는 주로 사용자와 대화형이고 키보드 뿐만 아니라 다양한 입출력 장치들과 각종 센서들에서 입력되는 정보로 프로그램과 대화하게 됩니다.


프로그램이 진행 중, 사용자에게 요구 사항이 있으면 팝업 창을 띄우고 묻기 위해 대기할 수 있지만, 사용자나 센서가 문득 프로그램에 어떤 입력을 주고 싶은 경우도 있습니다. 이를 '끼어든다'고 하여 인터럽트(interrupt)라 부르고 프로그램은 하던 작업을 멈추고 가급적 빨리 이를 처리하고 원래 수행하던 작업으로 되돌아가야 합니다. 이처럼 즉시 처리해야 하는 특수한 이벤트인 인터럽트가 발생하면, 프로세서는 그 이벤트를 처리할 작업들이 수록된 함수를 호출하는데, 이 함수를 인터럽트 서비스 루틴(ISR; interrupt service routine)이라고 합니다.

 


예를 들어, 키보드에 특정 키가 눌러저 있는지 이벤트를 감지하려면 모든 프로그램은 아두이노의 loop() 함수 같이 반복 수행되는 루프 내에서 항상 이 키의 상태를 검사해야 합니다. 이를 '소프트웨어 인터럽트' 혹은 '폴링(polling) 방식'이라고 하고 마이크로 프로세서의 경우에는 보다 정확하고 신속한 '하드웨어 인터럽트(인터럽트 방식)'도 제공하는데, 이를 이용하면 소프트웨어적으로 이 버튼의 상태를 항상 검사할 필요가 없다는 것입니다. 때문에 프로그램은 불필요한 부분에 시간을 낭비할 필요가 없게 됩니다. 그러므로 폴링 방식은 단일 이벤트 처리에 적합하고 인터럽트 방식은 다중 이벤트 처리에 효율적입니다. 


즉, MCU는 다른 작업을 하고 있다가 '키가 눌려지면' 그 즉시 하드웨어 인터럽트를 발생하여 하던 일을 멈추고 이를 처리할 ISR 함수를 호출하고, ISR이 끝나면 하던 일로 되돌아가게 됩니다. Arduino Uno 보드의 경우 인터럽트를 처리할 수 있는 핀은 2번과 3번 핀으로 이는 MCU에서 지원하기 때문이며 각각 INT0, INT1이라고 부릅니다.


ISR을 사용하기 위해서는 attachInterrup()라는 함수로 미리 알려야 합니다.


attachInterrupt( pin, ISR, mode)

pin : 첫 번째 인자로 인터럽트로 사용할 핀을 지정합니다. INT0의 경우 '0', INT1의 경우 '1'이 됩니다.

ISR : 두 번째 인자로 인터럽트가 걸렸을 때 호출할 함수의 이름으로 사용자가 만든 함수 이름입니다.

mode : 세 번째 인자로 RISING, FALLING, CHANGE, LOW 중 하나가 됩니다.



'RISING'이란 인터럽트 핀에 신호가 '0'에서 '1'로 변하는 순간이며 'FALLING'은 그 반대입니다. 'CHANGE'는 RISING과 FALLING 모두를 의미하며 'LOW'는 logical '0'값일 때를 의미합니다. ISR 함수는 입력 인수를 받을 수 없고 반환값도 void형이어야 하며, attatchInterrupt() 함수는 통상 setup() 함수 안에서 사용되어 초기에 인터럽트를 설정하게 됩니다.


다음은 터치 센서를 한 번 터치하면 LED가 점등하고 다시 터치하면 LED가 커지도록 동작하는 스케치를 예를 들어 봅니다. 이를 위해서 Ardunio Uno 보드의 11번에 부저를 연결하고, 3번 핀(INT1)에 터치 센서를 연결하여 하드웨어 인터럽트를 사용할 것입니다. 소스 코드는 다음과 같습니다.


#define TS 3

#define BUZ 11

void setup() {

    pinMode(LED_BUILTIN, OUTPUT);

    pinMode(BUZ, OUTPUT);

    pinMode(TS, INPUT);

    attachInterrupt(INT1, toggleLed, RISING);

}

void loop() {

    digitalWrite(BUZ, HIGH);

    delay(50);

    digitalWrite(BUZ, LOW);

    delay(450);

}

void toggleLed() {

    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));

}


setup()함수 내에 attachInterrupt(INT1, toggleLed, RISING)를 삽입하여 INT1을 사용하였고 ISR로써 toggleLed() 함수를 추가하였습니다. 세 번째 인자인 mode가 RISING 이므로 toggleLed() 함수는 외부 인터럽트인 3번 핀의 신호가 '0'에서 '1'로 바뀌는 rising edge에서 하드웨어적으로 자동 호출됨을 의미합니다.


loop() 함수에서는 부저를 단지 0.5초마다 한 번씩 울리는 일이 전부이며, 터치 센서가 눌러질 때마다 toggleLed() 함수 내에 digitalWrite() 함수가 실행되고 이는 아두이노 보드 내 이전의 LED 값에 따라 그 반대의 값을 쓰게 됩니다. 즉 켜져 있으면 크고 커져 있으면 켜게 됩니다. 참고로 digialRead() 함수는 단일 인자를 가지며 읽고자 하는 핀 번호를 인자로 넘겨주면 그 logical 값을 반환합니다.



'Embedded Lecture > Arduino' 카테고리의 다른 글

인터럽트와 volatile 지시자  (1) 2017.03.18
아두이노의 TWI(I2C) 통신  (0) 2017.03.18
아두이노의 시리얼 통신  (0) 2017.03.18
LED 깜박이기  (0) 2017.03.12
아두이노(Arduino) 코딩의 시작  (0) 2017.03.12
Posted by Nature & Life


Arduino Uno R3는 가장 널리 사용되는 입문용 기본 보드로 R3는 세번째 버젼을 의미합니다. 이 보드는 8-bit 마이크로 콘트롤러인 ATmega328P을 탑재하며 PC와 USB로 연결할 수 있어 프로그램 다운로드 및 시리얼 통신에 가능합니다. 그 밖에도 ATmega16U2의 또 다른 마이크로 콘트롤러를 내장하는데, 이는 기존 보드들에서 사용하던 FTDI FT232R USB-to-Serial 드라이버 칩을 대체하기 위한 것으로 USB-to-Serial 변환 프로그램이 들어 있습니다. ATmega328P는 1KB의 부트로터(Bootloader)용을 포함한 32KB의 Flash 메모리와 2KB의 SRAM, 1KB의 EEPROM을 갖고 있으며, 클럭 속도(Clock speed)는 16MHz입니다.


5V로 동작하는 이 Uno 보드의 전원공급은 두 가지 방법이 있는데, 첫번째는 USB로부터 제공되는 5V를 그대로 사용할 수 있으며, 두번째로는 7~12V의 AC 어댑터를 잭에 꼽아 외부에서 공급하는 방법인데 이는 Uno 보드가 내부적으로 5V를 정류하는 레귤레이터(Regulator)를 내장하고 있기 때문이며 위의 두 가지 전원이 모두 연결되어 있다면 외부 전원이 우선이 됩니다.



디지털 입출력 핀 14개 (0번~13번)

디지털 입출력 핀들을 이용해서 외부의 이진 신호를 읽어들어나 또는 이진 신호를 내보낼 수 있다. 디지털 입출력으로 사용되면서 또한 다른 기능을 가지는 핀들이 있는데, 이 기능들은 한 핀으로 두가지를 다 사용할 수는 없으며 다른 기능은 다음과 같습니다.

    • 0번~1번: 시리얼 통신에 사용되어 USB로 PC와 통신을 할 수 있습니다.

    • 2번~3번: 인터럽트(interrupt) 기능을 갖습니다.

    • 3, 5, 6, 9, 10, 11번 핀은 PWM 기능을 가지며 아날로그 출력을 흉내낼 수 있다.


아날로그 입력 핀 6개 (A0~A5)

외부의 아날로그 입력값을 읽어들여 ATmega328P칩 내부의 ADC(Analog to Digital Converter)를 이용하여 0~1023 사이의 숫자로 변환합니다. 이때 필요한 기준 전압은 5V 이지만 1.1V의 내부 전압이 사용될 수 있으며 AREF핀으로 기준 전압을 직접 인가할 수도 있습니다. 그리고 아날로그 핀은 디지털 입출력 핀으로도 사용할 수 있습니다.


아날로그 출력핀 6개 (3,5,6,9,10,11번 핀)

아날로그 출력핀은 0~5V사이의 전압 값(256레벨)을 가질 수 있으며 이는 PWM(Pulse Width Modulation) 방식으로 동작하므로 흉내를 내는 것입니다.


인터럽트 (2, 3번 핀)

2번과 3번 핀에 가해진 외부 이벤트를 감지하여 사용자가 원하는 방향으로 이를 처리하기 위한 기능으로 이를 인터럽트 처리(interrupt handling)라 부르는데, 즉 이 핀들에 변화가 있는면 하드웨어는 즉각적으로 알리고 이때 사용자가 원하는 정해진 동작을 코딩하여 사용합니다.



ICSP for ATmega328 : 기존의 ATmega328P에 ICSP(In Circuit Serial Programming)를 위한 SPI 통신용 6핀 포트입니다. ICSP는 전통적으로 마이크로 컨트롤러에 직접 펌웨어를 프로그래밍하기 위해 마련된 것입니다.


ICSP for USB interface : 기존의 ATmega16U2에 ICSP(In Circuit Serial Programming)를 위한 SPI 통신용 6핀 포트입니다. 


TWI(I2C) 통신 : A0, A1 핀




Posted by Nature & Life
Embedded Programming/C2014. 4. 23. 09:33


AVR은 C 언어로 개발하는 경우에도 assembler 못지 않게 최적화된 코드를 생성하기에 다양한 장점이 있습니다. 그 중에서도 avr-gcc라는 컴파일러를 사용하는 것으로 avr-gcc는 전 세계 다양한 사용자(소위 Hacker)들에 의해 개발되어 공유하는 것으로 시행착오를 통하여 결점이 최소화되었다는 것입니다.


AVR을 C 언어로 개발시에는 일반적인 O/S 환경에서가 아닌 AVR 하드웨어에 기반을 두기 때문에 몇 가지 유의사항들이 있습니다.

 

 

 

 

1) 일반적인 C 언어와는 달리 이진수(binary)를 사용할 수 있으며, Boolean 타입의 변수는 사용할 수 없습니다. 

이진수는 예를 들어 0b0100 식으로 표현하며 Boolean 타입 변수 대신에 int형의 0과 1로 대체하여 사용하면 됩니다.


2) 인터럽트 서비스 루틴은 가능한 한 간결하게 작성합니다.

일반적인 C 언어와는 달리 AVR 하드웨어는 외부 인터럽트나 Timer, USART, ADC 등의 주변장치들로부터의 인터럽트를 처리하기 위한 ISR() 함수로 반환값이 없으며 특별히 호출하지도 않습니다. 이러한 인터럽트 서비스 루틴(Interrupt Service Routine, ISR)은 다음과 같은 이유로 최대한 간결하게 작성해야 합니다.


첫째는 인터럽트 함수 내에서는 지역변수가 Register가 아닌 Stack에 저장되므로 처리가 느리기 때문이며, 

둘째는 하나의 인터럽트 처리가 길어지면 이어서 발생하는 다른 인터럽트 처리를 실행하지 못하기 때문입니다.


대부분의 인터럽트는 Timer의 overflow를 처리하기 위한 것으로 만일 인터럽트를 제때에 처리하지 못한다면 AVR 칩이 외부 시스템과 실시간으로 작동하는 경우에는 치명적인 오류가 생길 수 있게 됩니다. 그러므로 인터럽트 발생시 많은 작업을 요구하는 경우에는 인터럽트 루틴에서 Flag를 간단히 설정하여 개시하고 메인 루틴에서 이를 처리하게 해야 합니다.


3) 인터럽트 서비스 루틴 내에서 전역변수들은 volatile로 선언합니다.

컴파일시 최적화를 위해 컴파일러는 나름데로 그 상황에 맞는 상수값으로 처리하도록 하는 경우가 있는데, 이것이 인터럽트 서비스 루틴에서는 의도하지 않은 오류를 초래하기 때문입니다. 그러므로 인터럽트 서비스 루틴에서는 전역변수들은 volatile로 선언하여 컴파일러가 임의로 해석하지 못하게 방지합니다.

ex) volatile char k; 


4) 외부 메모리가 있는 경우가 아니라면 재귀호출(Recursive call)을 사용하지 않는 것이 좋습니다. 

연속되는 재귀호출은 한정된 메모리에 Stack을 쌓이게 하며 결국에는 Stack overflow를 발생시킬 수 있기 때문입니다.


5) 대용량을 갖는 상수 배열(array)은 const 키워드를 사용하여 프로그램 메모리(Flash Memory)에 저장합니다.

AVR에서 변수는 SRAM에 저장하는데 고기능 AVR을 사용하지 않고 기능을 구현하려면 SRAM의 용량을 아껴야 합니다. 따라서 변하지 않는 대용량 상수들은 코드를 적재하는 Flash Memory에 저장하여 SRAM을 확보하는 것이 바람직합니다.

ex) const uint8_t value[64] = 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,

                                  29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,

                                  54,55,56,57,58,59,60,61,62,63,64};

  

 

 

Posted by Nature & Life