이전 포스트에서 작성한 라이브러리를 스케치에서 사용하는 방법을 알아보자.

#include <Morse.h> //라이브러리 인클루드
Morse morse(13);
void setup() {
}
void loop() {
   morse.dot(); morse.dot(); morse.dot();
   morse.dash(); morse.dash(); morse.dash();
   morse.dot(); morse.dot(); morse.dot();
   delay(3000);
}

이 파일은 원래의 스케치파일과 비교하여 몇 가지 다른 점이 있다. 먼저 라이브러리를 인클루드하기 위해서 #include <Morse.h>를 서두에 추가한 것이다. 이것으로 라이브러리에서 정의된 클래스를 이용할 수 있다. 두 번째로 클래스의 인스턴스를 다음과 같이 생성한다는 것이다.

Morse morse(13);

이것이 실행되면 Morse 클래스의 생성자가 실행되며 13이라는 핀번호를 넘겨주게 된다. 이 생성자 안에서 pinMode()함수를 실행했으므로 setup() 함수에서는 그럴 필요가 없다. 그리고 dot(), dash() 함수를 호출하기 위해서는 이 클래스 인스턴스를 매개로 해야 한다. 예를 들어서 13번 핀과 12번 핀에 LED가 연결되어 있다면 다음과 같이 하면 된다.

Morse morse(13);
Morse morse2(12);


멤버 함수 하이라이트 기능

 만약 아두이노 IDE를 사용한다면 추가로 keyword.txt 파일을 만들어서 하이라이트될 함수명을 지정해 줄 수 있다.


Morse KEYWORD1
dash KEYWORD2
dot KEYWORD2

 

각각의 줄은 함수명과 탭구분자 그리고 KEYWORD1/KEYWORD2 라는 지정어가 와야 한다. KEYWORD1은 클래스명을 지정하며 KEYWORD2 는 멤버함수임을 지정한다.

예제 파일 제공

 만약 다른 사용자들의 편의를 제공하기 위해서 예제를 제공하고 싶다면 Morse 폴더 밑에 examples 폴더를 생성한 후 그 안에 각각의 예제를 위한 폴더들을 만들고 그 안에 예제파일을 두면 된다. 이 예제들은 아두이노 IDE의 파일>예제 메뉴에 자동으로 나타난다.

#ifndef ~ #endif 전처리문의 이해

 동일한 헤더 파일(.h 파일)을 중복해서 선언하면 문법적으로 오류를 발생하게 되어 있다. 예를 들어서 MyLib.cpp 파일에서

#include “MyLib.h”
#include “MyLib.h”
....

와 같이 중복선언을 한다면 컴파일러 오류가 발생할 것이다. 사용자가 이런 식으로 작성하는 경우는 거의 없겠지만 개발자의 의도와 관계없이 이런 경우가 발생할 수도 있다. 예를 들어서 a.h 에서 b.h 를 인클루드 했는데 a.cpp 파일에서 이를 간과하고 a.h 와 b.h 둘 다 인클루드 했다면 결과적으로 b.h 는 두 번 포함되는 것이다.


 이 문제는 #ifndef _상수 ~ #endif 전처리문으로 회피할 수 있다. 전처리문은 컴파일이 수행되기 이전에 처리되며 이것의 동작은 이것으로 묶인 부분이 컴파일에 포함되려면 ‘_상수’ 가 정의되지 않은 상태여야 한다.(if not defined 가 조합된 명령어가 #ifndef 이다.)

해결책은 이전 포스트에서 언급한 바와 같이 헤더 파일 전체를 다음과 같이 이 전처리문으로 묶는 것이다.

#ifndef MYLIB_H
#define MYLIB_H
class MyLib {
   ...
};
#endif

이제는 MyLib.h 헤더파일을 아무리 중복해서 인클루드해도 오류가 발생하지 않는다. 그 원리는 맨 처음 인클루드 명령에서는 MYLIB_H라는 상수가 정의되지 않았기 때문에 #ifndef ~ #endif 안의 프로그램이 수행되는데 그 안에서는 MYLIB_H 상수가 정의된다. 즉 맨 처음 이 구문이 포함될 때 상수 MYLIB_H가 정의되므로 만약 중복되서 인클루드되는 경우가 발생하더라도 두 번째 부터는 #ifndef ~ #endif 안의 프로그램이 포함이 되지 않는 것이다.

 결과적으로 맨 처음에 수행되는  #include “MyLib.h” 문만 처리되며 그 이후의 중복되는 #inclde 명령은 처리되지 않게 되어 MyLib 클래스의 선언부가 한 번만 확장된다.



Posted by 살레시오

 본 포스트에서는 사용자 라이브러리를 작성하는 방법을 예제로 설명하도록 하겠다. 다음과 같이 간단한 LED로 모르스 부호를 표시하는 스케치 프로그램으로 시작한다.


int pin = 13;
void setup() {
   pinMode(pin, OUTPUT);
}
void loop() {
   dot(); dot(); dot();
   dash(); dash(); dash();
   dot(); dot(); dot();
   delay(3000);
}
void dot() {
   digitalWrite(pin, HIGH);
   delay(250);
   digitalWrite(pin, LOW);
   delay(250);
}
void dash() {
   digitalWrite(pin, HIGH);
   delay(1000);
   digitalWrite(pin, LOW);
   delay(250);
}

 

이것을 업로드하여 실행하면 내장 LED로 SOS신호를 3초 간격으로 반복하여 생성한다.

 이 프로그램에서 dot() 함수와 dash()를 라이브러리로 빼내서 만들어보도록 하겠다. 여기서는 내장 LED를 사용하였지만 라이브러리에는 LED가 연결한 핀을 생성자에서 지정할 수 있도록 할 것이다. 생성자 안에서는 pinMode()함수를 호출하여 지정된 핀을 출력으로 지정하도록 한다. 이제 변환을 시작해 보자.

 아두이노 라이브러리는 보통 두 개의 파일이 필요하다. 헤더 파일(.h 파일)에는 라이브러리에서 제공하는 클래스와 변수 등의 선언이 위치한다. 클래스 멤버함수 등의 실제 코드는 .cpp 파일에 작성하면 된다. 이렇게 작성해 놓으면 만약 다른 사용자가 이 라이브러리를 참고할 때에는 헤더 파일만 보면 될 것이다. 다른 사용자가 굳이 보지 않아도 되는 실제 코드 부분은 다른 파일로 분리시켜서 가독성을 향상시키는 것이다.

 헤더파일의 핵심은 다음과 같은 클래스 선언 부분이다.


class Morse {
   public:
       Morse(int pin);
       void dot();
       void dash();
   private:
       int _pin;
};

 

클래스에는 사용되는 함수와 변수의 선언이 있으며 외부에서 호출해야 하는 함수는 public 으로, 외부에 굳이 노출시킬 필요가 없는 함수/변수는 private 으로 선언한다. 클래스에는 생성자(constructor)라는 것이 있는데 이것은 클래스의 인스턴스가 생성되는 시점에서 호출되는 함수이다. C++의 생성자는 클래스와 이름이 같으며 출력형식은 지정해주지 않는다.

 헤더파일을 작성할 때 추가로 고려해야 되는 사항이 있는데 먼저 아두이노의 표준 함수들을 사용하기 위해서 다음과 같이 Arduino.h 헤더파일을 인클루드시켜야 한다. 이 헤더는 스케치파일의 경우 자동으로 인클루드되므로 별도로 작성할 필요는 없지만 라이브러리 파일의 경우에는 명시적으로 다음과 같이 포함시켜야 한다.

#include "Arduino.h“

또한, 전체 헤더파일을 다음과 같은 전처리문으로 둘러싸주는 것이 필요하다.


#ifndef Morse_h
#define Morse_h
// the #include statment and code go here...
#endif

이것은 C++에서 특정 헤더파일은 중복해서 인클루드되는 것을 방지하는 일반적인 방법이므로 이해가 가지 않아도 상관없지만 관련 부분을 검색에서 한 번 숙지하기를 권한다.

 마지막으로 주석문을 이용하여 라이브러리에 대한 간단한 설명을 헤더 파일 머리에 작성해주는 것이 좋다. 여기에는 보통 보통 작성자, 날짜, 버전, 라이센스에 대한 정보를 기록한다. 이러한 고려사항들을 모두 적용하면 헤더 파일은 다음과 같은 모양을 가질 것이다.

/*
Morse.h - Library for flashing Morse code.
Created by David A. Mellis, November 2, 2007.
Released into the public domain.
*/
#ifndef Morse_h
#define Morse_h
#include "Arduino.h"
class Morse {
   public:
       Morse(int pin);
       void dot();
       void dash();
   private:
       int _pin;
};
#endif

 이제 실제 코드가 위치하는 .cpp 파일의 작성법에 대해서 알아보자. 이 파일은 Arduino.h 와 방금 작성한 Morse.h 파일을 처음에 인클루드해야 한다.

#include "Arduino.h"
#include "Morse.h"

그 다음 생성자를 작성한다. 이 생성자는 클래스 인스턴스를 생성할 때 호출되므로 여기에서 pinMode()함수를 호출하여 사용자가 지정한 핀을 출력으로 설정하는 것이 좋다.


Morse::Morse(int pin) {
   pinMode(pin, OUTPUT);
   _pin = pin;
}

 

함수명 앞의 Morse:: 는 이 함수가 Morse라는 클래스의 멤버함수라는 것을 지정한다. 이 클래스의 다른 함수들을 정의할 때에도 같은 문법이 적용된다. 그리고 private 변수 _pin에서 맨 앞의 _은 이 변수가 private 변수라는 것을 관례적으로 표시하는 방법이며 또한 함수의 입력 인수 pin과도 구별시켜주는 역할을 한다.

 그 다음으로 원래의 스케치 파일에서 가져온 dot()함수와 dash()함수를 정의하면 된다. 이 함수의 내부에서는 스케치 파일과 다르게 클래스의 멤버 변수 _pin을 사용한다.

void Morse::dot() {
   digitalWrite(_pin, HIGH);
   delay(250);
   digitalWrite(_pin, LOW);
   delay(250);
}
void Morse::dash() {
   digitalWrite(_pin, HIGH);
   delay(1000);
   digitalWrite(_pin, LOW);
   delay(250);
}

또한 간단한 설명을 담고 있는 주석문을 파일 서두에 포함시키는 것이 좋다. 전체 .cpp 파일은 다음과 같다.

/*
Morse.cpp - Library for flashing Morse code.
Created by David A. Mellis, November 2, 2007.
Released into the public domain.
*/
#include "Arduino.h"
#include "Morse.h"
Morse::Morse(int pin) {
   pinMode(pin, OUTPUT);
   _pin = pin;
}
void Morse::dot() {
   digitalWrite(_pin, HIGH);
   delay(250);
   digitalWrite(_pin, LOW);
   delay(250);
}
void Morse::dash() {
   digitalWrite(_pin, HIGH);
   delay(1000);
   digitalWrite(_pin, LOW);
   delay(250);
}

이것으로 라이브러리의 작성은 끝났으며 이 파일들은 {내문서}\Arduino\libraries\MyLib 폴더에 위치하여야 아두이노 하프가 인클루드시킬 수 있다. 먼저 {내문서}\Arduino\libraries 밑에 Morse 폴더를 생성한 후 이 두 파일을 그 안에 복사해서 넣으면 된다. 폴도의 구성도를 그려보면 다음과 같다.


[그림 1] Morse 라이브러리의 폴더 구조

이제 아두이노 IDE를 재실행시키면 Sketch>Include library 메뉴의 하위에 Morse 항목이 새로 생긴 것을 확인할 수 있을 것이다.

[그림 2] 생성된 Morse 라이브러리

주의할 점은 헤더 파일과 본체 파일 모두 이름이 속한 폴더와 같아야 한다는 것이다. 그리고 확장자는 정확하게 .h와 .cpp 이어야 한다.



Posted by 살레시오