본 포스트에서는 사용자 라이브러리를 작성하는 방법을 예제로 설명하도록 하겠다. 다음과 같이 간단한 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 살레시오
,

 본 포스트에서는 인터넷으로 내려 받은 사용자 라이브러리를 설치하는 방법과 직접 라이브러리를 작성하는 방법에 대해서 좀 더 자세히 설명하도록 하겠다.

 아두이노 IDE를 설치하면 {내문서}\Arduino\libraris 폴더가 생성되고 여기에 다운로드 받은 라이브러리를 복사하거나 사용자가 직접 작성한 라이브러리를 저장한다. 컴파일을 수행할 때 이 폴더는 자동으로 인클루드되어서 여기에 있는 라이브러리들이 링크된다.

 라이브러리는 기본적으로 폴더 단위로 저장되어야 한다. 예를 들어서 MyLib 라는 이름으로 (보통 라이브러리명은 대문자로 시작한다.) 라이브러리를 작성한다면 {내문서}\Arduino\libraris 폴더 밑에 MyLib라는 폴더가 있어야 하고 이 폴더 밑에 MyLib.h 헤더 파일과 MyLib.cpp 파일이 있어야 한다. 만약 예제 파일을 제공하고 싶다면 example 폴더 밑에 예제 파일의 이름과 같은 폴더를 두고 그 밑에 .ino 파일을 위치해 두어야 한다. 이를 도시하면 다음과 같다.


[그림 1] 윈도에서 아두이노 라이브러리 폴더의 구조

보통은 라이브러리는 클래스나 변수 등을 선언한 헤더 파일(.h)과 클래스 멤버함수의 정의부가 있는 .cpp 파일로 나뉜다. 그리고 헤더 파일에는 MyLib 클래스가 선언되어 있어야 한다. 보통은 헤더 파일과 본체 파일(.cpp 파일)로 나뉘지만 모든 선언과 정의를 헤더 파일에 둘 수도 있다. 하지만 헤더 파일에는 클래스의 선언부를 작성하고 그것을 구현한 cpp파일을 별도로 두는 것이 더 일반적이고 바람직하다. 이렇게 구성하면 이 라이브러리를 사용하는 스케치파일에서는 헤더 파일만 인클루드해서 사용할 수 있고, 여러 곳에서 중복해서 사용할 경우에도 헤더 파일만 인클루드 하면 되기 때문이다


 아두이노 홈페이지(http://arduino.cc/en/Reference/Libraries)에서 다양한 아두이노의 라이브러리에 대해서 설명하고 있다. 사용자는 여기에서 필요한 라이브러리를 다운로드 받아서 설치할 수 있다. 예를 들어서 OneWire 라는 라이브러리를 다운로드 받았다고 하자. 그러면 다운로드 폴더에 OneWire.zip 파일이 다운로드 되어 있을 것이다. 이 압축파일에는 OneWire.h, OneWire.cpp 파일이 들어 있다. 이 경우 이 압축파일을 이용하여 바로 사용자 라이브러리 폴더에 복사할 수 있는데 다음과 같이 Sketch>Include library > Add .ZIP Library 메뉴를 이용하여 압축파일을 선택해 주면 된다.

[그림 2] 아두이노 IDE의 라이브러리 관리 메뉴

이 메뉴를 선택한 후 OneWire.zip 파일을 선택하면 자동으로 사용자 라이브러리 폴더에 압축이 해제되서 복사해 들어가게 된다.

 OneWire.zip 파일은 다운로드 받은 압축파일에 필요한 라이브러리 파일이 직접 들어가 있는 경우이지만 경우에 따라서 인터넷에서 받은 파일이 여러 버전의 라이브러리를 가지고 있는 것도 있다. 예를 들어서 우노용과 두에용이 분리되어서 있는 경우이다. 이런 경우에는 사용자가 직접 압축을 해제한 후에 필요한 폴더만 사용자 라이브러리 폴더에 수동으로 복사해 주어야 한다.



Posted by 살레시오
,

 아두이노 보드들 중에서 가장 작은 크기를 가지는 보드로 arduino pro mini 가 있다. 동작 전압에 따라 두 가지 모델이 분리되어 있다. (5V 동작 모델/ 3.3V동작 모델)

[그림 1] 아두이노 프로 미니

스펙은 다음과 같으며 단순 스펙 상으로는 아두이노 우노보다 핀수가 조금 더 많다.

  • 메인 프로세서 : ATmega328 (초기 버전에서는 ATmega168)

  • 디지털핀 16개 (6개의 PWM포함)

  • 아날로그핀 8개

  • 크기 : 1.3" x 0.7" (약 33 mm x 18 mm)

  • 클럭 : 16MHz (5V 모델) / 8MHz (3.3V 모델)

  • 가격 : US $9,95 (정품)

  • 전원 : 3.35 -12 V (3.3V model) or 5 - 12 V (5V model)

전원을 인가할 때 한 가지 주의할 점은 정전압 (즉, 정류된 정확한 3.3V/5V 전압) 은 VCC핀에 연결해야 한다는 것이다. 정류되지 않은 전압(즉 정확히 3.3V/5V 가 아니라면)은 RAW핀에 연결해야 하는데 내장된 레귤레이터가 필요한 전압으로 바꿔준다. 그리고 VCC 와 RAW 핀 두 개에 동시에 전원을 인가하면 안된다. 보드가 상할 수도 있다고 한다.

 핀아웃 다이어그램은 다음 그림과 같다.

          [그림 2] 아두이노 프로미니의 핀 기능 다이어그램

 아두이노 우노와의 차이점은 핀 배치가 틀리므로 표준 쉴드(shield)를 사용할 수 없다는 것과 크기를 줄이기 위해서 usb 인터페이스가 생략되어 있다는 점이다. 따라서 프로그램을 다운로드 하려면 별도의 UART to USB 변환기를 사용해야 한다. (아래 그림 참조)

[그림 3] usb-to-serial 장치로 프로그래밍을 하는 모습

따라서 이 제품은 다량의 완성품을 만들어야 하는 경우에 하나의 변환기로 여러 개의 보드를 프로그램할 수 있으므로 단가를 낮출 수 있어 유리하다. 쇼핑몰 sparkfun.com 에서 정품이 개당 9.95$ 이고 ebay.com 같은 곳에서는 3.00$ 짜리 중국산 저가 복제품도 쉽게 찾을 수 있다.(그 가격에 팔면서 이윤이 남는다는 것이 정말 신기하다.) 또한 크기가 작으므로 소형 제품을 설계할 때에도 유리하게 작용한다. 그리고 3.3V 모델이 별도로 마련되어 있어서 라즈베리 파이와의 인터페이스도 용이할 것 같다.

 이것과 유사한 소형 보드로는 아두이노 나노(nano)와 아두이노 미니(mini) 가 있는데 모두 Atmega328 기반의 제품들이다. 나노는 usb시리얼 변환 칩과 usb단자가 있다는 점이 프로미니와 다르다. 미니와 프로미니는 핀배열이 조금 다르고 입력 전압을 인가하는 방식이 약간 틀린 것 같다. 즉 다음과 같은 모델들이 ATmega328 기반의 유사한 스펙이라고 보면 될 것 같다.

  • 아두이노 우노 (uno)

  • 아두이노 나노 (nano)

  • 아두이노 미니 (mini)

  • 아두이노 프로미니 (pro mini)


또 다른 소형 보드로 아두이노 마이크로(micro)라는 제품도 있는데 이것은 레오나르도를 소형화 시켜놓은 것이라고 보면 된다. ( 개인적인 경험으로 레오나르도 보드를 가지고 개발하는 것은 별로 권하고 싶지 않은데, 프로그램을 다운로드 하고 리셋이 걸릴 때마나 가상 시리얼 포트를 매번 잡아주는 것이 영 번거로웠기 때문이다. 우노 계열이 개발하기에 제일 편하다.)

 이것을 프로그램하려면 아두이노IDE에서 [도구]>[보드]>[Arduino Pro or Pro Mini] 메뉴를 선택한다. (아래 그림 참조) 그러면 [도구] 메뉴에 [프로세서] 라는 새로운 메뉴가 생성된다. 여기서 프로세서(328/168)와 전압(5V/3.3V)을 선택하면 된다. 최근의 모델은 모두 ATmega328을 사용한다.

  

[그림 4] 아두이노 프로미니 선택

올바르게 설정한 이후에는 일반적인 아두이노를 프로그램하는 방식과 동일하게 다룰 수 있다.


Posted by 살레시오
,

 아두이노의 인터럽트 기능을 이용하면 추가적인 하드웨어 없이 로터리 엔코더 (rotaty encoder)의 출력을 간단하게 읽어들일 수 있다. 여기에서는 아두이노 Due와 오토닉스(autonics)사의 1800펄스/1회전 분해능의 고정밀 로터리 엔코더를 이용해서 실험을 진행해 보겠다.


 기본적인 아이디어는 다음과 같다.


[그림 1] 오토닉스사의 로커리 엔코더 (E40H8-1800-6-L-5 )


엔코더에서는 세 가지 신호가 나오는데 각각 A상, B상, 그리고 Z상이라고 한다. A상과 B상은 축이 회전하면 위 그림과 같은 펄스를 발생하는데 자세히 보면 위상이 90도 만큼 엇갈려 있다. 일단 회전각은 A상을 기준으로 rising edge 혹은 falling edge 에서 인터럽트를 발생하여 내부 변수를 하나씩 증가하는 식으로 저장하면 된다. 회전 방향은 B상을 참조하면 되는데 예를 들어 시계방향으로 회전시에는 A상의 falling edge에서 B상이 1이고 반대 방향에서는 0이다. 이것으로 회전방향을 판단하면 된다. Z상의 펄스는 1회전에 한 번 발생하므로 이것으로 영점을 잡으면 된다. 즉 Z상의 펄스가 발생하면 카운트를 0으로 리셋시킨다.


 이제 아두이노Due 쪽의 프로그램을 살펴보면 Due는 다른 아두이노와 달리 고성능 보드 답게 모든 핀에 인터럽트를 설정할 수 있다. (다른 아두이노는 보통 2개 많아야 6개이다.) 필자는


  • 26번핀: A상

  • 28번핀: B상

  • 30번핀: Z상

이렇게 연결하였다. 한 가지 주의할 것은 autonics사이 이 엔코더는 datasheet에서 5V로 동작한다고 쓰여져 있는데 그렇다고 5V전원을 연결하면 신호선에 과전압이 걸려서 Due보드에 무리를 줄 수 있다.(Due보드는 동작 전압이 3.3V임) 다행히 3.3V를 연결해도 잘 동작하는 것 같았다. 테스트 프로그램은 다음과 같다.


int pA = 26; // phase-A of rotary encoder
int pB = 28; // phase-B of rotary encoder
int pZ = 30; // phase-B of rotary encoder

volatile signed long cnt = 0;
volatile signed char dir = 1;

void setup() {
   Serial.begin(115200);
   attachInterrupt(pA, encoderCount, FALLING);
   pinMode(pB, INPUT);
   attachInterrupt(pZ, encoderReset, FALLING);
}

void loop() {
   Serial.print(micros());  Serial.print(',');
   Serial.println(cnt);
}

void encoderCount() { // A상 신호의 폴링 에지에서 호출됨
   //B상의 값에 따라 방향을 결정한다.
   dir = (digitalRead(pB)==HIGH)? 1:-1;
   cnt += dir;
}
void encoderReset() { // Z상 신호의 폴링 에지에서 호출됨
   cnt = 0;
}


이렇게 구현하면 매우 간단하게 로터리 엔코더 신호를 읽어들일 수 있다.



Posted by 살레시오
,

 아두이노 제로(zero) 라는 보드가 출시 예정이다.

[그림 1] 아두이노 제로의 외형

아두이노와 Atmel사가 공동 개발한 것 같고 (뒷면에 그렇게 표기되어 있다.) 프로세서는 AVR 이 아니라 ATSAMD21 이라는 ARM 계열의 32비트 프로세서를 채용하였다. 48MHz 의 클럭 주파수로 동작하는데 (이것만 놓고 단순 비교하기는 무리지만) 성능이 아두이노 우노의 3배이다.

 클럭 속도 외에 우노와 비교하여 특이한 점은 0번과 1번을 제외한 모든 디지털 핀에서 PWM 기능을 사용할 수 있다는 점과 플래시메모리가 256KB 로서 우노의 8배 정도로 늘었다는 것이다. 개발 환경에서도 별도의 usb 통신 단자를 통해서 atmel 의 Embedded Debugger (EDBG) 기능을 사용할 수 있어서 디버깅에 별도의 장치가 필요없다는 점도 눈에 띈다.

 우노와의 차이점을 정리하면 다음과 같다.

  • 32비트 프로세서 채용

  • 3.3V로 동작

  • 48MHz의 클럭 주파수 (우노대비 4배)

  • 12개의 PWM핀 (우노의 PWM핀은 6개)

  • 256KB의 플래시메모리 (우노대비 8배)

  • 디버깅(EDBG)을 위한 별도의 usb 단자 내장

  • 12비트 ADC (우노는 10비트 ADC임)

문제는 가격인데 개인적인 짐작으로는 아두이노 우노에 비해서 많이 비쌀 것 같지는 않다.



Posted by 살레시오
,

 센서(sensor)란 자연계의 물리량을 그에 비려/반비례하는  전기적인 신호(전압, 저항값 등)로 변환시켜주는 소자이다. 아두이노를 이용한 실험에서 자주 사용되는 센서를 나열해 보면 다음과 같다.


초음파 센서

 초음파를 발생하여 반사되어 오는 시간을 측정하여 장애물과의 거리를 재는 센서이다. 이동 로봇의 거리 측정이나 차량의 후방 장애물 감지에 널리 사용되고 있다.

[그림 1] 초음파 센서 모듈


적외선 센서

 적외선(infrared rays)은 전자기파의 일종으로 가시광선보다는 파장이 길고 전자레인지에 사용되는 마이크로파보다는 파장이 짧다. 적외선을 발광하는 IRED (infrared ray emitting diode)는 일반적인 LED와 외형과 구동 회로면에서 유사하며 정방향 전압을 걸면 적외선이 나오는 전기소자이다. 흔히 사용되는 리모콘의 머리 부분에 달려있다.

[그림 2] 적외선 송수신 센서

광량 센서

 CdS 광전도 셀(CdS photoconductive cell) 또는 CdS셀은 황화카드뮴을 주성분으로 하는 광전도 소자로, 빛의 양에 따라 저항값이 변하는 일종의 가변 저항으로 생각할 수 있다. 빛의 양에 따라 내부 저항값이 변하는 특성이 있으므로 광 가변 저항기라고도 불리는데, 조사되는 빛의 양이 클수록 저항값이 낮아지지만 입사광이 거의 없으면 거의 절연체에 가까워질 정도록 저항값이 커지게 된다. 따라서 광량에 의해서 개폐되는 전기적인 스위치로도 생각할 수 있다.


[그림 3] CdS 광전도 센서

온도 센서

 온도에 비례하는 전압 발생하는 소자이다.

[그림 4] 온도 센서


기울기 센서

  기울기 센서는 기울기에 반응하는 센서이다. 일정 각도 이상 기울어지면 내부의 접점이 on되는 디지털식 센서와 각도에 비례하는 전기적인 신호를 발생하는 아날로그식 센서로 구분할 수 있다.

  

[그림 5] 디지털/아날로그 기울기 센서

 

터치 센서

 도전체나 피부의 접촉에 반응하는 센서이다.

[그림 6] 터치 센서 모듈


인체 감지 센서

인체감지센서 모듈은 적외선을 감지하는 센서를 장착하고 있으며 약 6미터 이내의 인간이나 동물의 움직임을 감지할 수 있다. 인체나 동물의 몸과 같이 열을 발산하는 곳에서 발생하는 적외선을 감지하는 원리이다.

[그림 7] 인체 감지 센서


이 밖에도 습도 센서, 소리에 반응하는 음향 센서, 각종 가스 센서, 등 많은 것들이 있다.





Posted by 살레시오
,

 오랜만에 아두이노 due 로 프로그래밍할 일이 있어서 새로 나왔다는 arduino ide 1.6.4 버전을 설치하고 프로그래밍을 시작하려는데 보드 메뉴에 새로운 보드들이 몇몇 추가된 것은 보이는데 정작 기존에 있던 arduino due 가 없어서 살짝 당황했다.


 조금 헤매다가 도구>보드>boards manger (맨 위에 있음) 옵션으로 타고 들어가니 다음과 같은 메뉴창이 뜬다.



위 그림처럼 Arduino SAM Boards 항목의 우하단에 설치 버튼이 있는데 그걸 눌러서 설치하면 보드 메뉴에 추가된다. (위 그림은 이미 인스톨한 후 캡쳐해서 설치 버튼이 안 보이는데 처음에는 install 버튼이 있다.



처음에 다운로드 받은 설치 프로그램 이전 버전보다 용량이 많이 줄어서 의아했었는데 이런 식으로 필요한 컴포넌트를 다운로드 받아서 설치하도록 구조가 바뀌어서 그런가 보다하는 짐작이 든다.




Posted by 살레시오
,

 초음파 센서로 다음과 같이 작동하는 아두이노 프로그램을 작성해 보자.

    ❶ 장애물이 30cm 밖에 있다면 부저는 울리지 않는다.

    ❷ 30cm 이내 10cm 바깥에 있다면 0,5초 주기로 삑삑거린다.

    ❸ 10cm보다 안쪽에 있다면 연속적인 삐~ 신호를 울린다.

마치 자동차의 후방 경고음과 같이 동작하게끔 하는 것이다.


 실험에서는 초음파 센서는 7번 핀에, 부저는 11번 핀에 달려있다고 가정한다. 다음 예제를 잘 분석해 보고 각자 응용해 보자.


#define ULTRA_SONIC_SENSOR  7
#define BUZ 11
void setup() {
 Serial.begin(9600);
 pinMode(BUZ, OUTPUT);
}
void loop() {
 float fDist = measureDist();
 int iTmBuzOn = 0;
 int iTmBuzOff = 0;
        
 if (fDist > 30) {
   iTmBuzOn = -1;
 } else if (fDist > 10) {
   iTmBuzOn = 50;
   iTmBuzOff = 450;
 }  else {
   iTmBuzOn = 0;
 }
 
 if (iTmBuzOn == 0) {
   digitalWrite(BUZ, HIGH);
 } else if (iTmBuzOn > 0) {
   digitalWrite(BUZ, HIGH);
   delay(iTmBuzOn);
   digitalWrite(BUZ, LOW);
   delay(iTmBuzOff);
 } else {
   digitalWrite(BUZ, LOW);
 }
   Serial.print(fDist);
   Serial.println(" cm");
}
#define MS_PER_CM           58.31f
float measureDist() {
pinMode(ULTRA_SONIC_SENSOR, OUTPUT); //(1)단계
digitalWrite(ULTRA_SONIC_SENSOR, LOW); //(2)단계
delayMicroseconds(2);
digitalWrite(ULTRA_SONIC_SENSOR, HIGH); //(3)단계
delayMicroseconds(5);
digitalWrite(ULTRA_SONIC_SENSOR, LOW); //(4)단계
pinMode(ULTRA_SONIC_SENSOR, INPUT);
// (5) 단계
unsigned long ulPulseTime = pulseIn(ULTRA_SONIC_SENSOR, HIGH);
if (ulPulseTime == 0 )
  return -1.0f;
else
  return ulPulseTime / MS_PER_CM;
}


여기서 measureDist()함수는 이전 포스트에서 작성한 함수를 그대로 사용하였다.


C++ 언어 전체 강좌 목록 >>>

Posted by 살레시오
,

 아두이노의 TWI 통신 (I2C라고도 한다)은 두 가닥의 선(SDA, SCL)만으로 여러 개의 디바이스와 통신을 할 수 있다는 장점을 가진다. 이것의 통신 속도는 마스터 기기에서 발생시키는 클럭(SCL)신호를 기준으로 정해지며 보통 많이 사용되는 표준 주파수는 100kHz 와 400kHz 이다. 단순하게 이론적으로 계산하면 100kHz 의 주파수라면 초당 100k 비트(바이트 아님)를 전송할 수 있으며 초당 약 12.5k 바이트를 전송할 수 있다.


 아두이노의 I2C 통신에 사용되는 클럭 주파수는 100kHz로 맞추어져 있다. 아두이노에서 쓰이는 AVR은 400kHz 의 주파수도 지원을 하며 대부분의 I2C 통신 기기들이 이 주파수를 지원한다. 그런데 아두이노 API에서는 이 클럭 주파수를 조절하는 함수나 메쏘드가 없다. 이것을 400kHz로 상향시키기 위해서는 다음과 같이 약간 번거로운 과정을 거쳐야 한다.


 아두이노 IDE 1.5.5 와 윈도즈를 기준으로 설명하도록 하겠다. 먼저 다음 파일을 연다

C:\Program Files \ Arduino \ hardware \ arduino \ avr \ libraries \ Wire \ utility \ twi.h

이 코드의 윗 부분에 보면 다음과 같은 상수가 있다.


#ifndef TWI_FREQ
#define TWI_FREQ 100000L
#endif


이름에서 알 수 있듯이 TWI_FREQ 상수가 I2C 통신의 클럭 주파수를 정의한 상수이다. 이 상수를 400000L 로 바꾸면 된다.


#ifndef TWI_FREQ
#define TWI_FREQ 400000L
#endif

이렇게 변경하고 저장한 후 한 가지 과정을 더 거쳐야 한다. 현재 프로젝트의 (과거에 100kHz 상수 값으로 생성되었던)오브젝트 파일들인 wire.cpp.o , twi.c.o 파일들을 제거해야 하는데 이것을 제거하지 않으면 과거에 컴파일된 오브젝트파일을 가지고 링크를 하기 때문에 변경 사항이 적용되지 않는다.

다음의 폴더를 열어보자. (윈도7의 경우임)

C:\ Users \ [user id] \ AppData \ Local \ Temp

이 폴더 하위에 많은 build***********.tmp 폴더는 아두이노 프로젝트가 컴파일되면서 생성되는 임시파일들을 저장하는 폴더이다. 이것들을 모두 삭제한 다음 다시 컴파일하면 변경된 속도가 적용된다. 만약  위와 같이 오브젝트 (임시)파일을 삭제하는 절차가 번거로우면 아예 프로젝트를 새로 생성해서 코드를 붙여넣은 후 컴파일하면 된다.



Posted by 살레시오
,

 아두이노의 TWI로 한 바이트나 문자열을 주고 받는 것은 Wire.read(), Wire.write() 함수를 사용하면 쉽게 수행할 수 있으므로 전혀 문제가 없다. 문제는 멀티 바이트로 구성된 short, long, float 등의 데이터를 주고 받는 것이다. 예를 들어서 signed short형 (아두이노에서는 2 bytes임) 데이터를 전송하려고 하면 바이트로 쪼개서 보낸 다음, 받는 곳에서 다시 이를 조합해서 원래의 데이터로 복구시켜야 하는데 무척 번거롭다.


 이런 경우에 union 이라는 자료형을 사용하면 문제를 쉽게 해결할 수 있다. 예를 들어서 다음과 같이 (signed) short 형 데이터를 저장하기 위한 union을 정의한다. volatile 은 인터럽트 루틴 안에서 사용되기 때문에 붙인 것이다.


union UShort{
   volatile short sVal;
   volatile byte byArr[2];
};


union자료형은 포함된 변수의 메모리를 공유하기 때문에 여기서 보면 byArr[0]은 short형 sVal 변수의 하위바이트를, byArr[1]은 상위 바이트를 가진다.


이제 이것을 이용해서 변수를 생성한다. volatile 을 붙인 이유는 이전과 같다.


volatile UShort uMtr; // pwm value to motor
volatile UShort usv; // encoder value


그러면 usv.sVal 변수를 보통의 short형 변수처럼 사용하다가 TWI로 전송할 때는 usv.byArr[0]과 usv.byArr[1] 두 바이트를 차례로 보내주면 된다. 예를 들어서 슬레이브의 TWI 전송함수는 다음과 같다.


void requestEvent()
{
   Wire.write( (const byte *)usv.byArr, 2);
   usv.sVal = 0;
}


주의할 것은 usv.byArr 을 반드시 (const byte *) 형으로 캐스트해줘야 한다는 점이다. 그렇지 않으면 컴파일 시에 에러가 발생한다.


 수신단에서도 동일한 union을 정의해 놓고, 받은 데이터를 바이트 배열 byArr 에 차례로 넣어주면 곧바로 sVal 변수를 통해서 short형 변수 값을 쓸 수 있다.


void receiveEvent(int howMany)
{
   uMtr.byArr[0] = Wire.read(); // 하위바이트 받음
   uMtr.byArr[1] = Wire.read(); // 상위 바이트 받음
   MotorInput( uMtr.sVal ); // 받은 short형 데이터를 바로 사용한다.
}


이 함수에서 보면 uMtr.byArr[0] 과 uMtr.byArr[1] 을 차례로 전송받은 후 바로 uMtr.sVal 변수값을 사용하였다. 이렇게 별도로 원래 데이터를 쪼개서 보내고, 받은 후에 (비트 연산자 같은 것을 사용해서)복구하는 과정이 전혀 필요가 없는 것이다.



Posted by 살레시오
,