Professional Documents
Culture Documents
1장 아두이노 개요
1.1 아두이노란? ············································································································2
1.2 아두이노의 종류 ·····································································································5
1.3 아두이노 우노 R3 ································································································10
1.4 개발 환경 ·············································································································12
2장 디지털 입출력
2.1 디지털 포트의 개념 ·····························································································20
2.2 LED 깜박이기 ······································································································23
2.3 부저를 이용한 실험 ·····························································································29
2.4 터치센서 ···············································································································33
2.5 택스위치 ···············································································································35
2.6 인체감지센서 ········································································································38
3장 시리얼 통신
3.1 시리얼통신 개요 ···································································································42
3.2 시리얼 통신으로 초음파 센서값 받기 ·······························································48
4장 인터럽트
4.1 인터럽트(interrupt)의 개념 ················································································54
4.2 터치센서를 이용한 인터럽트 실험 ·····································································55
4.3 택스위치와 디바운싱 ···························································································56
5장 아날로그핀
5.1 아날로그 투 디지털 변환기 ················································································62
5.2 온도센서를 이용한 아날로그 입력 실험 ····························································65
5.3 광량센서를 이용한 아날로그 입력 실험 ····························································67
6장 아날로그 출력
6.1 아날로그 출력 개요 ·····························································································72
6.2 아두이노의 아날로그 핀 ······················································································73
6.3 고휘도 LED ··········································································································74
6.4 (초)소형 DC모터 실험 ·························································································76
6.5 중형 DC모터 실험 ·······························································································80
6.6 PWM 주파수 변경 ·······························································································82
7장 아두이노 라이브러리
7.1 Servo 라이브러리 ································································································89
7.2 LiquidCrystal 라이브러리 ···················································································91
7.3 저항 하나로 터치센서 구현 ················································································97
7.4 I2C (two wire) 통신 ·························································································101
8장 사용자 라이브러리
8.1 다운로드 받은 라이브러리 설치법 ···································································113
8.2 사용자 라이브러리 작성법 ················································································114
9장 바퀴 로봇 제어
9.1 makeblock사의 로봇 키트 ················································································122
9.2 조향 제어 ···········································································································125
10장 다른 아두이노들
10.1 아두이노 프로 미니 ·························································································134
10.2 아두이노 두에(due) ·························································································137
10.3 갈릴레오 보드 ··································································································140
01
아두이노 개요
2 - 아두이노 개요
1.1 아두이노란?
아두이노는 AVR이라는 마이크로 컨트롤러를 기반으로 한 오픈소스 원보드 마이컴이라고 할
수 있다. 예전에는 이런 시스템을 설계하고 다루기 위해서는 전기/전자와 관련된 전문적인 기술
이나 지식이 필요했다. 이 보드의 특징은 그러한 어려움을 극복하고자 기술 숙련도가 낮은 디자
이너 혹은 예술가들이 쉽게 사용할 수 있도록 설계된 것이다. 따라서 비전공자들도 손쉽게 익히
고 사용할 수 있다는 큰 장점을 가지며 이러한 장점으로 인해 널리 사용되고 있다.
1.2 아두이노의 종류
아두이노(arduino)보드는 하드웨어와 소프트웨어 구조가 모두 개방된 오픈소스 플랫폼이기
때문에 정품뿐만 아니라 수많은 변종 보드들이 존재한다. 여기에서는 동일한 Arduino IDE를 사
용해서 개발할 수 있고 기본적으로 많이 사용되는 보드들 위주로 설명하도록 하겠다.
1.2.7 기타 변종 아두이노들
Freeduino
Funduino
Seeeduino
Korduino
Meduino (make block)
10 - 아두이노 개요
Paperduino
Boraduino
Roboduino
Femtoduino (가장 작은 아두이노 호환 복제품)
Ruggeduino (I/O 보호 기능이 내장된 아두이노 보드)
pcDuino (갈릴레오보드와 비슷한 개념)
Teensyduino
1.3 아두이노 우노 R3
아두이노 우노 R3는 ATmega328P 라는 AVR 8-bit 마이크로콘트롤러를 사용한다. 우노는 이
탈리아어로 하나(one)의 의미이며 이름에서 알 수 있듯이 아두이노 보드들 중 가장 널리 사용되
는 표준 보드라고 할 수 있다. (이탈리아어로 uno, due, tre .. 는 일, 이, 삼 ... 이다.)
이 보드는 PC와 USB로 연결할 수 있으며 이것만으로도 전원 공급과 프로그램 다운로드 그리
고 PC와의 시리얼 통신을 수행한다.
1.3.1 전원 연결
1.3.6 그 외의 기능들
아두이노 우노 보드는 3.3V의 전압도 공급할 수 있다. 이는 USB만 연결한 경우도 마찬가
지이다. AREF 핀은 아날로그 핀의 기준 전압을 설정하는 용도로 사용된다.
다른 기기와의 통신 기능은 다음과 같다.
∙ 시리얼 통신 : 0번, 1번 핀
∙ SPI 통신 : ICSP 헤더핀
∙ TWI (I2C) 통신 : A0, A1 핀
1.4 개발 환경
아두이노를 프로그래밍 하는데 C/C++언어가 사용된다. 만약 사용자가 C언어나 C++언어에 익
숙하다면 물론 좋겠지만 그렇지 않더라도 라이브러리가 사용하기 편하게 잘 갖추어져 있으므로
익히는 시간이 그리 많이 걸리진 않는다. 사실 사용하는 언어는 C++ 이지만 (백그라운드에서는
이것을 C코드로 변환하여 컴파일한다.) 잘 모른다고 미리 겁먹을 필요는 없다. 어차피 아두이노
라는 플랫폼 자체가 비전공자(디자이너, 예술가 등)들이 깊은 전공 지식 없이 개발을 할 수 있도
록 설계가 된 것이기 때문이다.
아두이노 개요 - 13
아두이노 IDE는 1.0.x 버전과 1.5.x버전이 존재한다. 1.5.x 버전의 차이점은 ARM 기반의
아두이노도 지원한다는 점이다.
1.4.2 아두이노 하프
전절에서 소개한 아두이노 IDE는 개발에 필요한 거의 최소한의 기능을 제공한다. 아두이
노 프로그래밍을 하다 보면 이러한 기능상의 제약이 무척 아쉽다. 이에 다양한 개발자들이
개발 편의성을 증대시켜주는 아두이노용 개발 환경을 만들어서 배포하고 있다. AVR을 개발
한 경험이 있다면 Atmel Studio 같은 전용 개발툴로도 아두이노 프로그래밍을 할 수 있으
므로 검토해 보는 것도 좋다.
필자도 새로운 윈도우용 아두이노 IDE를 교육용으로 개발하였고 이것을 아두이노 하프
(harp)라고 이름을 붙였다. 다음 그림은 아두이노 하프의 외형이다.
아두이노 개요 - 17
02
디지털 입출력
(Digital Input/Output)
20 - 디지털 입출력
포트를 출력으로 사용하는 경우는 스위치를 생각하면 간단히 이해할 수 있다. 건전지와
연결된 전구사이에 스위치가 있는 간단한 실험 장치를 생각해 보면 된다. 다음 그림의 스위
치를 손가락으로 눌러서 연결(on 되었다고 한다)되면 전구에 전류가 흐로게 되므로 불이 켜
질 것이고 손가락을 떼면 (off되었다고 한다.) 전구가 꺼지게 된다.
22 - 디지털 입출력
아두이노 우노보드에는 실습용 LED가 실장되어 있는데 이것은 13번 핀과 연결되어 있다.
24 - 디지털 입출력
// give it a name:
int led = 13;
// the setup routine runs once when you press reset:
void setup() {
// initialize the digital pin as an output.
pinMode(led, OUTPUT);
}
// the loop routine runs over and over again forever:
void loop() {
// turn the LED on (HIGH is the voltage level)
digitalWrite(led, HIGH);
delay(1000); // wait for a second
// turn the LED off by making the voltage LOW
digitalWrite(led, LOW);
delay(1000); // wait for a second
}
╘══════════════════════════════════
이 예제의 구조를 살펴보면 먼저 눈에 띄는 것이 있는데 setup() 함수와 loop() 함수이다.
이 두 함수는 아두이노 프로그램의 전체적인 구조를 잡아주는 역할을 한다.
❶ setup() 함수
• 아두이노 프로그램이 실행될 때 맨 처음에 단 한 번 호출되어 수행된다.
• 여기에는 각종 장치를 초기화하거나 초기값을 설정하는 코드가 오게 된다.
❷ loop() 함수
• setup()함수 실행 후 수행되면 계속 반복 수행된다.
• 아두이노에 연결된 장치들을 구동시키는 코드가 위치한다.
• pinMode(pinNumber, INPUT/OUTPUT) 함수
• pinNumber 핀을 입력(INPUT) 혹은 출력(OUTPUT)으로 사용할 지를 지정한다.
• pinNumber는 우노의 경우 0,1,2, … 13, A0, A1, ...A6 중 하나이다.
게 된다.
• digitalWrite(pinNumber, HIGH/LOW) 함수
• pinNumber 핀이 출력일 경우에 사용한다.
• 1값을 내보낼지 (HIGH) 0값을 내보낼지(LOW)를 지정한다.
• delay(time) 함수
• time ms 만큼 지연 (아무런 일도 안 하고 멈춰 있음)
2.2.2 신호등 실험
╒══════════════════════════════════
#define RED 12
#define YELLOW 11
#define GREEN 10
void setup() {
pinMode(RED, OUTPUT);
pinMode(YELLOW, OUTPUT);
pinMode(GREEN, OUTPUT);
}
void loop() {
digitalWrite(RED, HIGH);
digitalWrite(YELLOW, HIGH);
digitalWrite(GREEN, HIGH);
delay(500);
digitalWrite(RED, LOW);
digitalWrite(YELLOW, LOW);
digitalWrite(GREEN, LOW);
delay(500);
}
╘══════════════════════════════════
╒══════════════════════════════════
#define RED 12
#define YELLOW 11
#define GREEN 10
#define TIME 1000 // 1 sec
void setup() {
pinMode(RED, OUTPUT);
pinMode(YELLOW, OUTPUT);
pinMode(GREEN, OUTPUT);
}
void loop() {
digitalWrite(RED, HIGH);
digitalWrite(YELLOW, LOW);
digitalWrite(GREEN, LOW);
delay(TIME);
digitalWrite(YELLOW, HIGH);
delay(TIME);
digitalWrite(RED, LOW);
digitalWrite(YELLOW, LOW);
digitalWrite(GREEN, HIGH);
delay(TIME);
}
╘══════════════════════════════════
주의할 점은 다이오드와 마찬가지로 부저는 극성이 있으며 다리가 긴쪽이 (+)이다. 여기에 전압
을 걸면 삐~~ 하는 전자음이 발생한다.
실험을 위해서 다음 그림과 같이 아두이노의 7번 핀에 부저의 (+)를, GND핀에 (-)를 연결했
다.
2.3.1 첫 번째 실험
╒══════════════════════════════════
#define BUZ 7
#define TIME 50
void setup() {
pinMode(BUZ, OUTPUT);
}
void loop() {
digitalWrite(BUZ, HIGH); // 부저가 울리기 시작한다.
디저털 입출력 - 31
delay(TIME);
digitalWrite(BUZ, LOW); // 부저가 멈춘다
delay(1000-TIME);
}
╘══════════════════════════════════
2.3.2 두 번째 실험
╒══════════════════════════════════
#define BUZ 7
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
pinMode(BUZ, OUTPUT);
}
void loop() {
digitalWrite(LED_BUILTIN, HIGH);
digitalWrite(BUZ, HIGH);
delay(50);
digitalWrite(BUZ, LOW);
delay(200);
digitalWrite(LED_BUILTIN, LOW);
delay(250);
digitalWrite(LED_BUILTIN, HIGH);
delay(250);
digitalWrite(LED_BUILTIN, LOW);
delay(250);
}
╘══════════════════════════════════
2.3.3 세 번째 실험
╒══════════════════════════════════
#define RED 12
#define YELLOW 11
#define GREEN 10
#define BUZZER 7
void setup() {
pinMode(RED, OUTPUT);
pinMode(YELLOW, OUTPUT);
pinMode(GREEN, OUTPUT);
pinMode(BUZZER, OUTPUT);
}
void loop() {
leds(HIGH, LOW, LOW, 6000);
leds(HIGH, HIGH, LOW, 3000);
leds(LOW, LOW, HIGH, 3000);
2.4 터치센서
이번 절에서는 터치센서를 이용하여 디지털 입력 값을 받는 실험을 진행해보도록 하겠다. 시
중에서 구할 수 있는 터치센서 모듈의 외형은 다음 그림과 같다. 금속판을 도전체로 (인체도 가
능함) 터치할 때 출력 신호가 바뀐다. 즉, 터치시에 HIGH를 아닐 때는 LOW 신호를 내보낸다.
이 모듈의 동작은 간단하다. 터치가 안 되어 있다면 신호선으로 LOW 신호가 나오고 터치가 되
었다면 HIGH 신호가 나온다. 따라서 이 디지털 신호를 읽으면 터치 패널에 현재 터치가 되었는
34 - 디지털 입출력
지 아닌 지를 판별할 수 있다.
2.4.1 첫 번째 예제
╒══════════════════════════════════
#define TS 3 // 터치센서에 연결된 핀 번호
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
pinMode(TS, INPUT);
}
void loop() {
int iTouched = digitalRead(TS);
digitalWrite(LED_BUILTIN, iTouched);
}
╘══════════════════════════════════
╒══════════════════════════════════
#define TS 3 // 터치센서에 연결된 핀 번호
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
pinMode(TS, INPUT);
}
void loop() {
digitalWrite(LED_BUILTIN, digitalRead(TS));
}
╘══════════════════════════════════
디저털 입출력 - 35
2.5 택스위치
택스위치(tact switch)는 기계적인 접점을 가지는 스위치로서 누르면 접점이 닫히고 누르지
않은 상태에서는 접점이 열려있는 소자이다.
∙ digitalRead(2); // 2번 핀의 값을 읽는다
2.4.1 스위치 입력 예제
첫 예제로 버튼을 누르고 있으면 내장된 LED가 켜지는 간단한 기능을 구현해 보자. 누르
고 있는 동안에만 켜져 있고 누르지 않고 있다면 꺼지도록 하는 것이다.
╒══════════════════════════════════
#define SW 2
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
pinMode(SW, INPUT_PULLUP); // 내부 풀업 저항을 연결한다.
}
void loop() {
digitalWrite(LED_BUILTIN, !digitalRead(SW));
}
╘══════════════════════════════════
1
2.6 인체감지센서
인체감지센서 모듈은 적외선을 감지하는 센서를 장착하고 있으며 약 6미터 이내의 인간이나
동물의 움직임을 감지할 수 있다. 인체나 동물의 몸과 같이 열을 발산하는 곳에서 발생하는 적
외선을 감지하는 원리이다.
[그림 2.6.1] 인체 감지 센서 모듈
2.6.1 첫 번째 예제
이 예제를 실행해 보면서 인체가 감지되는 거리 범위와 LED가 켜지는 시간을 파악하고 조
절하여 보자.
40 - 디지털 입출력
연습문제
1. 아두이노에 적색, 황색, 녹색 LED와 부저 그리고 택스위치 하나, 터치센서 하나가 연결되어
있다고 가정하고 다음과 같은 동작을 하는 프로그램을 작성하라.
03
시리얼 통신
USART
42 - 시리얼 통신
3.1 시리얼통신 개요
시리얼 통신(serial communication)은 기기들 사이에 데이터를 주고받는 방법 중 하나인데 병
렬 통신 (parallel communication) 방식에 비해서 통신선의 개수가 적다는 장점이 있다. 아두이
노는 UART (universal asynchronous receiver and transmitter), SPI, I2C 방식의 시리얼 통신
을 지원하는데 이중 UART는 주로 아두이노와 PC간의 통신에 사용된다. 아두이노는 하나 이상
의 UART 통신기가 내장되어 있는데 라이브러리와 IDE에 내장된 터미널(terminal, 주고 받는
데이터를 확인할 수 있는 프로그램)을 이용하면 손쉽게 PC와의 통신을 수행할 수 있다.
아두이노 우노의 경우 0번과 1번핀이 시리얼 통신에 사용된다. 이 핀들은 내부적으로 USB통
신을 담당하는 칩과 연결되어서 USB신호로 변환된 후 PC에 전달된다. 반대로 PC에서 보내지는
USB신호는 이 칩에서 시리얼 통신 신호로 변환되어 아두이노의 AVR에 전달된다. 따라서 만약
아두이노가 PC와의 통신을 수행하고 있다면 이 핀들을 다른 용도로 사용하면 안된다. 그리고 통
신을 수행할 때에는 TX, RX라고 마킹된 LED가 깜빡인다. 일단 사용자는 아두이노와 PC간에
USB케이블로 연결하면 통신 실습을 할 준비가 끝나게 된다.
3.1.1 첫 번째 예제
■ long Serial.print(val) 함수
• 입력값을 ASCII값으로 변환하여 PC쪽으로 출력한다.
• 전송된 데이터의 바이트 수를 리턴한다. (잘 사용되지 않음)
• 비동기 통신 방식이므로 데이터가 전송되기 전에 리턴된다.
• 입력 변수 val은 어떤 데이터 타입도 가능하다. 예를 들면
✱ 보레이트(baud rate) 란?
3.1.2 두 번째 예제
두 번째 예제로 아두이노 디지털핀에 연결된 버튼을 누르면 메세지를 PC쪽에 보내는 예
제를 작성해 보자. 택스위치가 2번 핀에 연결되어 있다고 가정하고 그 버튼이 눌려졌다면
“pressed!”라는 문자열이 터미널에 표시되도록 해보자. Serial.print() 함수를 이용하면 매우
간단하게 작성할 수 있다.
╒══════════════════════════════════
#define SW 2
void setup() {
Serial.begin(9600);
pinMode(SW,INPUT_PULLUP);
}
void loop() {
if (digitalRead(SW)==LOW)
Serial.print("pressed!");
}
╘══════════════════════════════════
이 예제를 실행시키면 버튼을 누르고 있드면 “pressed!”라는 문자열이 연속적으로 PC로 전
송되게 된다.
3.1.3 세 번째 예제
세 번째 예제로 이번에는 PC에서 문자 하나를 받아서 그것이 ‘0’이면 LED를 끄고 ‘1’이면
LED를 켜는 프로그램을 작성해 보자. 이 경우 아두이노는 데이터가 사용자로부터 들어올
때 까지 대기 상태로 있다가 데이터가 읽히면 거기에 맞는 동작을 수행해야 한다.
╒══════════════════════════════════
#define LED 13
void setup() {
pinMode(LED, OUTPUT);
Serial.begin(9600);
}
void loop() {
if ( Serial.available() ) {
char command = Serial.read();
if (command == '0') {
digitalWrite(LED, LOW);
Serial.println("LED off.");
}
else if (command == '1') {
digitalWrite(LED, HIGH);
Serial.println("LED on.");
}
else {
Serial.print("Wrong command :");
Serial.println(command);
}
}
}
╘══════════════════════════════════
46 - 시리얼 통신
■ int Serial.available() 함수
• 전송되어 내부버퍼(64바이트)에 저장된 데이터의 개수를 반환한다.
• 입력인수는 없다.
■ int Serial.read() 함수
• 전송되어 내부 버퍼(64바이트)에 저장된 데이터 중 가장 첫 번째 데이터(ASCII코드)
를 읽어서 반환한다.
• 이 함수가 수행되면 내부 버퍼의 크기는 하나가 줄어든다.
• 내부 버퍼가 비었다면 -1을 반환하다.
■ int Serial.println() 함수
• 출력 문자열의 끝에 줄바꿈 기호 ‘\r\n’ 가 자동으로 붙는다는 점 외에는
Serial.print()함수와 동일한 동작을 수행한다.
if ( Serial.available() ) { 명령어들 }
3.1.4 네 번째 예제
전원핀,
GND핀,
출력핀, 세 개로 동작한다.
2cm 부터 3m 범위를 측정할 수 있다.
❶ 핀을 출력으로 설정한다.
❷ LOW신호를 2micros 동안 유지한다.
❸ HIGH 신호를 5micros동안 유지한다.
❹ LOW로 신호를 내보냄과 동시에 핀을 입력으로 설정한다.
❺ pulseIn() 함수를 이용하여 펄스가 HIGH인 구간 시간을 micros 단위로 받는다.
시리얼 통신 - 49
초음파 모듈 시간은 초음파를 송신함과 동시에 핀을 HIGH로 올리고 물체에 반사된 초음파를
수신하면 LOW로 내린다. 따라서 핀이 HIGH로 유지되는 경과 시간은 초음파가 나가서 물체에
반사되어 되돌아온 시간인 것이다.
3.2.1 첫 번째 프로그램
이제 전술한 설명을 바탕으로 장애물까지의 거리를 반환해주는 함수를 작성해 보자. 함수의
이름은 measureDist() 이고 출력은 float형 값이다.
――――――――――――――――――――――――――――――――――――――――――
#define MS_PER_CM 58.31f
#define ULTRA_SONIC_SENSOR 2
void setup() {
Serial.begin(9600);
}
void loop() {
float fDist = measureDist();
if (fDist < 0) {
Serial.println("No obstable or no sensor.");
} else {
Serial.print("Distance: ");
Serial.print(fDist);
Serial.println(" cm");
}
delay(100);
}
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,
50 - 시리얼 통신
HIGH);
if (ulPulseTime == 0 )
return -1.0f;
else
return ulPulseTime / MS_PER_CM;
}
――――――――――――――――――――――――――――――――――――――――――
위 코드에서 measureDist() 함수를 실행하면 위에서 설명한 각 단계를 거쳐서 거리까지 계산한
float 값을 반환한다. 만약 pulseIn()함수의 반환값이 0이라면 -1.0f 를 반환하여 장애물이 감지
되지 않은 것임을 표시한다.
3.2.2 두 번째 예제
╒══════════════════════════════════
#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 (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");
}
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;
}
╘══════════════════════════════════
프로그램을 살펴보면 부저를 울리는데 iTmBuzOn 과 iTmBuzOff 라는 변수를 이용하였
다는 것을 알 수 있다. iTmBuzOn 변수가 –1 이라면 부저를 울리지 않고 0이라면 연속적
으로 울리게끔 작성되어 있다. 만약 0도 아니고 –1도 아니라면 저장된 시간 만큼 부저를
울리고 나머지 시간동안 끄도록 되어 있다.
Chapter
04
인터럽트
( Interrupt )
54 - 인터럽트
4.1 인터럽트(interrupt)의 개념
4.2.1 첫 번째 실험
터치센서와 다르게 기계적인 접점을 갖는 스위치는 점점이 붙거나 떨어지는 순간에 바운싱
(bouncing) 현상이 있다. 이것은 접점이 붙거나 떨어지는 그 짧은 순간에 접점이 고속으로 여러
번 on/off 되는 현상을 말하며 기계적인 스위치라면 반드시 발생하는 현상이다. 이것은 짧은 순
간(약 100ms 이내임)에 여러 번 접점이 붙었다가 떨어지는 것을 반복하므로 의도하지 않게 인
인터럽트 - 57
<- 디바운스된 신호
void isr() {
_delay_ms(80);
if (digitalRead(SW)==HIGH) return;
<실제 인터럽트 처리 루틴>
,,,
}
∙ delay()
∙ delayMicrosecond()
∙ millis()
∙ micros()
#include<util/delay.h>
#include<util/delay.h>
#define SW 2
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
pinMode(SW, INPUT_PULLUP);
attachInterrupt(INT0, toggleLed, FALLING);
}
void loop() {
}
void toggleLed() {
_delay_ms(80);
if (digitalRead(SW)==HIGH) return;
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
}
void isr() {
_delay_ms(80);
if (digitalRead(SW)==LOW) return;
…
<실제 인터럽트 처리 루틴>
}
60 - 인터럽트
만약 <그림 2>와 같이 회로를 꾸미고 체인징 에지, 즉 falling과 rising edge 모두에서
인터럽트가 걸리도록 설정되었다고 하자.
void isr() {
_delay_ms(80);
<실제 인터럽트 처리 루틴>
….
}
05
아날로그핀
62 - 아날로그핀
ananlogRead(pin)
아날로그핀 - 63
입력 인수로 0(또는 A0), 1(또는 A1), … 5(또는 A5)를 주고 리턴값은 int형으로서 앞에서
언급한 대로 0~1023 값이다. 변환 시간은 100micro-sec 으로서 이론상으로는 1초에 10000
번 정도 변환이 가능하다. 아날로그 핀은 디지털 핀과 달리 기본적으로 입력으로 설정되어
있으므로 별도로 입력을 설정하는 과정 없이 바로 위의 함수를 이용할 수 있다.
#define A0 14
#define A1 15
…
#define A5 19
pinMode(A0, OUTPUT);
digitalWrite(A0, HIGH); 혹은 digitalWrite(A0, LOW);
64 - 아날로그핀
ananlogReference(type)
analogReference(EXTERNAL
);
이제 이것을 함수로 구현하여 1초마다 한 번씩 온도를 감지하여 PC로 시리얼 통신을 통해서 표
시해주는 예제는 다음과 같다.
void setup() {
Serial.begin(9600);
}
void loop() {
Serial.println(getTemp());
delay(1000);
}
float getTemp()
{
short sVal = analogRead(A0);
float voltage = sVal*5.0/1024; // 실제 전압값을 구한다.
return voltage*100;
}
pinMode(A0, INPUT_PULLUP);
5.3.1 첫 번째 예제
void setup() {
pinMode(A0, INPUT_PULLUP);
Serial.begin(115200);
}
void loop() {
Serial.println(analogRead(A0));
delay(100);//1초에 약 10번 정도 출력한다.
}
아날로그핀 - 69
5.3.2 두 번째 예제
void setup() {
pinMode(A0, INPUT_PULLUP);
pinMode(13, OUTPUT);
}
void loop() {
if ( analogRead(A0) > 100 ) {
digitalWrite(13, HIGH);
} else {
digitalWrite(13, LOW);
}
}
Chapter
06
아날로그 출력
(PWM)
72 - 아날로그 출력
6.1 아날로그 출력 개요
PWM은 진정한 의미의 아날로그 출력은 아니고 흉내를 내는 것인데 그 원리는 다음 그림과 같
다. (출처 : arduino.cc)
(a) 0V
(b) 1.25V
(c) 2.5V
(d) 3.75V
(e) 5V
∙ analogWrite(pin, value)
pin : 3, 5, 6, 9, 10, 11 중 하나 (아두이노 우노의 경우)
value : 0에서 255 사이의 정수.
일반적인 LED와 외형은 큰 차이가 없지만 밝기가 획기적으로 개선된 것이고 전력 효율이 높아
서 가정용 조명이나 신호등, 차량의 전조등 등으로 폭 넓게 사용된다.
구동 회로는 일반 LED와 다르지 않게 저항을 직결하여 전원이 연결하면 되는데 문제는 저항값
으로 어떤 값을 사용하는가이다. 데이터쉬트를 살펴보면 최대 허용 전류는 20mA 이고 (전류가
클수록 더 밝다.) 이 전류가 흐를 때 다이오드 양단의 전압 강하는 3.0~3.6V 이다. 따라서 만약
5V 전원을 사용하고 최대 전류를 흘릴 때 전압강하가 3.0V라고 가정하면 저항값은 오옴의 법칙
에 의해서 다음과 같이 간단히 계산할 수 있다.
6.3.1 첫 번째 실험
void loop() {
for (int k=0; k<256; k++) {
analogWrite(HLED,k);
delay(15);
}
for (int k=255; k>=0; k--) {
analogWrite(HLED, k);
delay(15);
}
}
╘══════════════════════════════════
위 프로그램에서 delay(15) 함수를 이용하여 서서히 밝아지거나 서서히 어두워지는 효과를
내었다.
6.3.2 두 번째 실험
두 번째로 스위치를 누르면 고휘도 LED가 서서히 켜지는 프로그램을 작성해 보자. 완전
히 꺼진 상태에서 최고 밝기로 켜지는 시간은 4초로 설정한다. 버튼을 떼면 그 즉시로 고휘
도 LED가 꺼져야 한다.
╒══════════════════════════════════
#define HLED 6
#define SW 3
void setup() {
pinMode(HLED,OUTPUT);
pinMode(SW,INPUT_PULLUP);
}
int iL=0;
void loop() {
if (digitalRead(SW)==LOW) {
analogWrite(HLED, iL++);
if (iL>255) iL = 255;//❶
delay(15);
} else {
digitalWrite(HLED,LOW);
iL = 0;
}
}
╘══════════════════════════════════
전술한 바와 같이 analogWrite()함수의 두 번째 인수의 범위는 0~255이다. 따라서 ❶에서
이 범위를 넘으면 (즉 256이 되면) 255값을 계속 가지도록 if 문으로 처리했음을 유의해서
보자.
6.3.3 세 번째 실험
추가해 보자. 꺼지는 속도는 완전히 켜졌을 때에서 완전히 꺼질 때까지의 시간이 4초 정도
되게 설정한다.
╒══════════════════════════════════
#define HLED 6
#define SW 3
void setup() {
pinMode(HLED,OUTPUT);
pinMode(SW,INPUT_PULLUP);
}
int iL=0;
void loop() {
if (digitalRead(SW)==LOW) {
analogWrite(HLED, iL++);
if (iL>255) iL = 255; //❶
delay(15);
} else {
analogWrite(HLED, iL--); //❷
if (iL<0) iL = 0;
delay(15);
}
}
╘══════════════════════════════════
이 프로그램의 동작은 스위치를 누르고 있으면 서서히 켜지고 떼면 다시 서서히 꺼지게 된
다. analogWrite()함수의 두 번째 인수의 범위는 0~255이다. 따라서 ❶과 ❷에서 이 범위를
넘으면 그 한계값을 계속 가지도록 if 문으로 처리했음을 유의해서 보자.
6.3.4 네 번째 실험
6.4.1 소형 DC모터의 제어
#define A_IN 5
#define B_IN 6
void setup() {
pinMode(A_IN, OUTPUT);
pinMode(B_IN, OUTPUT);
}
void loop() {
digitalWrite(A_IN, HIGH); // 정회전
digitalWrite(B_IN, LOW);
delay(1000);
digitalWrite(A_IN, LOW); // 역회전
digitalWrite(B_IN, HIGH);
delay(1000);
}
#define A_IN 5
#define B_IN 6
void setup() {
Serial.begin(9600);
Serial.setTimeout(100);
}
void loop() {
if (Serial.available()) { // 만약 시리얼 버퍼에 데이터가
있다면
// 정수로 해석하여 읽어들인다.
short motorSpeed = Serial.parseInt();
if (motorSpeed >= 0){
analogWrite(A_IN, motorSpeed);
analogWrite(B_IN, 0);
} else {
analogWrite(A_IN, 0);
analogWrite(B_IN, -motorSpeed);
}
}
}
6.5 중형 DC모터 실험
아두이노 쉴드 중에 DC모터를 제어할 수 있는 것이 있는데 2014년 현재 R3버전이 시판중이
다.
∙ 동작전압 : 5V to 12V
∙ 모터제어IC: L298P (두 개의 DC모터 혹은 1개의 스테핑모터 제어 가능)
∙ 최대 전류 :채널당 2A 혹은 4A(두 채널을 병렬로 연결시)
∙ 전류 센싱 : 1.65V/A
∙ 강제 정지(brake) 기능
아날로그 출력 - 81
모터의 전원은 반드시 별도로 연결해 주어야 하는데 아두이노의 2.1파이 DC잭에 연결해도 되
고 쉴드의 스크류단자에 전선으로 연결해도 된다. 그런데 사용 설명에는 모터 전압이 9V 이상
이면 아두이노와 쉴드의 전원(Vin)을 분리하는 것이 바람직하며 '쉴드 뒷면의 "Vin Connect" 점
퍼를 절단하면 된다'고 설명되어 있다. 이 경우 쉴드에 직접 인가할 수 있는 전압의 최대값은
18V이다.
사용되는 핀은 다음과 같다.
기능 채널A 채널B 기능
방향 D12 D13 회전 방향 제어
PWM D3 D11 속도 제어
브레이크 D9 D8 HIGH일 때 강제 정지
전류 감지 A0 A1 전류 감지
핀 번호 PWM 주파수
5, 6 980Hz
3, 9, 10,11 490Hz
pwm.h 라이브러리
pwm.h 라이브러리 다운로드 링크
에 복사하여 붙여넣는다.
C:\Program Files\Arduino\libraries
#include <PWM.h>
함수명 기능
[표 6.6.3] pwm.h의 두 함수 그룹 비교
함수 그룹 특징 장단점 적용 핀
InitTimers() 3,
millis(), micros(), delay(),
SetPinFrequenc timer0 5,
delayMicroseconds() 함수들이 정상
y() 초기화 9,
동작하지 않는다.
10
InitTimersSafe
timer0 millis(), micros(), delay(), 3,
()
초기화 delayMicroseconds() 함수들이 정상 9,
SetPinFrequenc
안함 동작한다. 10
ySafe()
07
아두이노 라이브러리
88 - 아두이노 라이브러리
표 7.2 특수 라이브러리
라이브러리명 기능 비고
Audio SD카드에 저장된 오디오파일 재싱.
Scheduler 다중 non-blocking 태스크 관리. Due
USBHost 마우스와 키보드 같은 USB주변장치와의 통신.
Esplora Esplora 보드의 센서와 작동기를 쉽게 접근하도록 함. Esplora
Keyboard 연결된 컴퓨터에 키보드 입력 전송. Leonardo,
Mouse 연결된 컴퓨터의 마우스 커서 제어. Micro,Due,Esplora
7.1.1 첫 번째 예제
╒══════════════════════════════════
#include <Servo.h>
Servo sm;
void setup() {
sm.attach(9); //9번 핀에 서보모터의 신호선을 연결
sm.write(90); //90도(중심각) 각도로 회전
}
void loop() {
}
╘══════════════════════════════════
7.1.2 두 번째 예제
void loop() {
sm.write(0); //0도로 회전
delay(2000);
sm.write(180); //180도로 회전
delay(2000);
}
╘══════════════════════════════════
#include <LiquidCrystal.h>
- 생성자 : LCD 모듈과 아두이노의 연결 방식에 따라서 생성자를 사용하면 된다. 다음과 같은
네 가지 함수가 오버로드되어 있다.
- 공용 멤버함수
7.2.1 첫 번째 예제
}
void loop() {
// 커서의 위치를 0열, 1행(두 번재 행)으로 설정한다.
lcd.setCursor(0, 1);
// 초를 표시한다.
lcd.print(millis()/1000);
}
╘══════════════════════════════════
7.2.2 두 번째 예제
void loop() {
lcd.noBlink();
delay(3000);
lcd.blink();
delay(3000);
}
╘══════════════════════════════════
7.2.3 세 번째 예제
delay(3000);
}
╘══════════════════════════════════
위 예제어서 noDisplay()함수가 실행되면 화면에 표시된 문자열이 사라지지만 그 내용은 내
부 메모리에 여전히 남아있다. 따라서 display()함수가 호출되면 되살아나는 것이다. 또한
이 함수들을 이용하면 화면 전체를 깜박이는 효과를 줄 수도 있다.
7.2.4 네 번째 예제
void setup() {
lcd.begin(16, 2);
lcd.cursor();
}
void loop() {
// ‘m’ 이 표시되면 방향을 바꾼다.
if (thisChar == 'm') {
lcd.rightToLeft();
}
lcd.write(thisChar);
delay(1000);
thisChar++;
}
╘══════════════════════════════════
7.2.5 다섯 번째 예제
╒══════════════════════════════════
#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
void setup() {
lcd.begin(16, 2);
Serial.begin(9600);
}
void loop() {
// 문자가 전송되면
if (Serial.available()) {
// 전체 문자열이 들어올 때까지 잠시 기다린다.
delay(100);
lcd.clear();
// 전송된 모든 문자를 표시한다.
while (Serial.available() > 0) {
lcd.write(Serial.read());
}
}
}
╘══════════════════════════════════
7.2.6 여섯 번째 예제
void setup() {
lcd.begin(16, 2);
lcd.print("hello, world!");
delay(1000);
}
void loop() {
// 왼쪽으로 스크롤
for (int cnt = 0; cnt < 13; cnt++) {
lcd.scrollDisplayLeft();
delay(150);
}
// 오른쪽으로 스크롤
for (int cnt = 0; cnt < 29; cnt++) {
lcd.scrollDisplayRight();
delay(150);
}
// 다시 왼쪽으로 스크롤하여 제자리로
for (int cnt = 0; cnt < 16; cnt++) {
lcd.scrollDisplayLeft();
delay(150);
}
delay(1000);
}
╘══════════════════════════════════
아두이노 라이브러리 - 97
7.3.1 기본 개념과 구성 회로
[그림 7.3.2] 실제 응용 예
#include <CapacitiveSensor.h>
void reset_CS_AutoCal()
7.3.3 저항값의 선택
7.3.4 실제 예제 프로그램
#include <CapacitiveSensor.h>
#define LED 13
아두이노 라이브러리 - 101
void setup() {
pinMode(LED, OUTPUT);
cs23.set_CS_AutocaL_Millis(0xFFFFFFFF);
Serial.begin(9600);
}
void loop() {
long start = millis();
long total1 = cs23.capacitiveSensor(30);
(total1>10) ? digitalWrite(LED, HIGH):digitalWrite(LED, LOW);
}
#include <Wire.h>
그리고 통신을 하는 기기들 간에 SDA, SCL, 그리고 GND 끼리 연결하고 전압 레벨이 맞는지 반
드시 확인해야 한다. 즉, 5V 기기는 5V 끼리, 3.3V기기는 3,3V 기기 상호간에 연결해야 하면
3.3V와 5V를 연결하려면 레벨 컨버터를 통해야 한다.
연결된 기기는 하나의 master와 다수의 slave로 구분하고 slave들은 7bit 혹은 8bit 아이디(숫
자)로 서로를 구분한다. 모든 통신 주도권은 master가 가진다. slave에서 데이터를 읽어올 때도
master에서 요구하고 slave로 데이터를 보낼 때도 마찬가지로 master에서 보낸다. 마스터에서
는 다음과 같이 초기화 함수를 호출한다.
슬레이브 쪽에서는 마스터에서 데이터를 보냈을 때 호출되는 이벤트 함수를 등록해 두면 데이터
가 보내졌을 때 이 함수가 호출되어 알맞게 처리를 해주면 된다.
...
union UShort{
volatile short sVal;
volatile byte byArr[2];
};
void requestEvent()
{
Wire.write( (const byte *)usv.byArr, 2);
usv.sVal = 0;
}
#include <PWM.h>
#include <Wire.h>
union UShort{
volatile short sVal;
volatile byte byArr[2]; //uint8_t
};
#define ID_TWI 2
// encoder constants
#define phaseA 0 // phase-A of rotary encoder to *INT0*
#define phaseB 7 // phase-B of rotary encoder to D7
void setup() {
//(pwm.h)initialize all timers except for 0, to save time keeping
106 - 아두이노 라이브러리
functions
InitTimersSafe();
//(pwm.h)sets the frequency for the specified pin
bool success = SetPinFrequencySafe(PWM_A, frequency);
usv.sVal = 0;
uMtr.sVal = 0;
// TWI setup
Wire.begin(ID_TWI); // join i2c bus as SLAVE with
address #4
Wire.onRequest(requestEvent); // register event to send sensor
value
Wire.onReceive(receiveEvent); // register event to receive motor
pwm value
MotorForcedStop();
#ifdef _DEBUG
//////////////////////////////////////////////////////
Serial.begin(115200); // working!!! others: 115200, 250000,
460800
# e n d i f
//////////////////////////////////////////////////////////////
}
#ifdef _DEBUG
void ReadEncoder()
{
Serial.print("en:");
Serial.println(usv.sVal);
usv.sVal = 0;
}
#endif
void loop() {
#ifdef _DEBUG /////////////////////////////////////////////////
short sTmp = 0;
if (Serial.available())
{
sTmp = Serial.parseInt();
while(Serial.available()) Serial.read(); // empty buffer
Serial.print("Received: ");
Serial.println(sTmp);
MotorInput(sTmp);
}
delay(50);
ReadEncoder();
아두이노 라이브러리 - 107
#endif /////////////////////////////////////////////////////////
}
void EncoderCount() {
usv.sVal += (1 - digitalRead(phaseB)*2); // LOW: +1, HIGH: -1
}
if (sIn >= 0)
{
digitalWrite(DIR_A, HIGH);
pwmWrite(PWM_A, sIn); // pwm.h
}
else if (sIn < 0)
{
digitalWrite(DIR_A, LOW);
pwmWrite(PWM_A, -sIn); // pwm.h
}
}
void MotorForcedStop()
{
digitalWrite(BRAKE_A, HIGH); // setting brake LOW disable
motor brake
// pwmWrite(PWM_A, 0); // pwm.h <- this HINDERS motor from
forcing stop
}
void requestEvent()
{
Wire.write((const byte *)usv.byArr, 2);
#ifdef _DEBUG
////////////////////////////////////////////////////
Serial.print("enc : ");
Serial.println(usv.sVal);
# e n d i f
///////////////////////////////////////////////////////////
usv.sVal = 0;
}
#ifndef TWI_FREQ
#define TWI_FREQ 100000L
#endif
#ifndef TWI_FREQ
#define TWI_FREQ 400000L
#endif
08
사용자 라이브러리
112 - 사용자 라이브러리
{내문서}\Arduino (폴더)
libraries (폴더) : 여기에 사용자 라이브러리를 저장한다.
MyLib (폴더)
examples (폴더)
example1 (폴더)
example1.ino
example2 (폴더)
example2.ino
MyLib.h
MyLib.cpp
[그림 8.0.1] 아두이노 라이브러리 폴더의 구조
이 메뉴를 선택한 후 OneWire.zip 파일을 선택하면 자동으로 사용자 라이브러리 폴더에 압축이
해제되서 복사해 들어가게 된다.
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);
}
class Morse
{
public:
Morse(int pin);
void dot();
void dash();
private:
int _pin;
};
클래스에는 사용되는 함수와 변수의 선언이 있으며 외부에서 호출해야 하는 함수는 public 으
로, 외부에 궂이 노출시킬 필요가 없는 함수/변수는 private 으로 선언한다. 클래스에는 생성자
(constructor)라는 것이 있는데 이것은 클래스의 인스턴스가 생성되는 시점에서 호출되는 함수
이다. C++의 문법 상 생성자는 클래스와 이름이 같으며 출력형식은 지정해주지 않는다.
#include "Arduino.h“
#ifndef Morse_h
#define Morse_h
#endif
/*
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
#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);
}
/*
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);
}
118 - 사용자 라이브러리
{내문서}\Arduino (폴더)
libraries (폴더) : 여기에 사용자 라이브러리를 저장한다.
Morse(폴더)
Morse.h
Morse.cpp
#include <Morse.h>
Morse morse(13);
void setup()
사용자 라이브러리 - 119
{
}
void loop()
{
morse.dot(); morse.dot(); morse.dot();
morse.dash(); morse.dash(); morse.dash();
morse.dot(); morse.dot(); morse.dot();
delay(3000);
}
Morse morse(13);
이것이 실행되면 Morse 클래스의 생성자가 실행되며 13이라는 핀번호를 넘겨주게 된다. 이 생
성자 안에서 pinMode()함수를 실행했으므로 setup() 함수에서는 그럴 필요가 없다. 그리고
dot(), dash() 함수를 호출하기 위해서는 이 클래스 인스턴스를 매개로 해야 한다. 예를 들어서
13번 핀과 12번 핀에 LED가 연결되어 있다면 다음과 같이 하면 된다.
Morse morse(13);
Morse morse2(12);
8.2.1 멤버 함수 하이라이트 기능
Morse KEYWORD1
dash KEYWORD2
dot KEYWORD2
8.2.2 예제 파일 제공
#include “MyLib.h”
#include “MyLib.h”
....
#ifndef MYLIB_H
#define MYLIB_H
class MyLib {
...
};
#endif
09
바퀴 로봇 제어
122 - 바퀴 로봇 제어
9.1 makeblock 사의 로봇 키트
makeblock 의 robot kit v2 를 구해서 조립해 보고 구성품을 좀 살펴보았다. 레고의 치수에
맞추어진 깔끔한 알루미늄 기구부가 일단 인상적이다. 표면 처리도 잘 되어있고 무엇보다 원하
는 형태로 자유자재로 결합되도록 설계가 되어있다. 이것으로는 ❶무환궤도 방식과 ❷)바퀴 방
식의 로봇을 만들 수 있었는데 그중 무한궤도 방식을 선택했다.
───────────────────────────────
#define PWM_RIGHT 3
#define DIR_RIGHT 12
#define BRAKE_RIGHT 9
#define PWM_LEFT 11
#define DIR_LEFT 13
#define BRAKE_LEFT 8
void setup() {
124 - 바퀴 로봇 제어
pinMode(PWM_LEFT, OUTPUT);
pinMode(DIR_LEFT, OUTPUT);
pinMode(BRAKE_LEFT, OUTPUT);
pinMode(PWM_RIGHT, OUTPUT);
pinMode(DIR_RIGHT, OUTPUT);
pinMode(BRAKE_RIGHT, OUTPUT);
analogWrite(PWM_LEFT,0);
analogWrite(PWM_RIGHT,0);
}
void loop() {
digitalWrite(DIR_LEFT, LOW);
digitalWrite(DIR_RIGHT,LOW);
analogWrite(PWM_LEFT, 255);
analogWrite(PWM_RIGHT, 255);
delay(2000);
digitalWrite(DIR_LEFT, HIGH);
digitalWrite(DIR_RIGHT, HIGH);
analogWrite(PWM_LEFT, 255);
analogWrite(PWM_RIGHT, 255);
delay(2000);
}
───────────────────────────────
바퀴 로봇 제어 - 125
[그림 9.1.1] 실험 장면
9.2 조향 제어
먼저 하나의 모터(왼바퀴에 연결된 모터)를 제어하는 함수를 다음과 같이 작성한다.
}
126 - 바퀴 로봇 제어
오른쪽 모터에 대해서도 똑같은 함수를 작성한 후 모터의 정지와 전후진을 담당하는 함수를 다
음과 같이 작성하였다.
void stopAll() {
leftMotorControl(0);
rightMotorControl(0);
}
(a) 전진 (b) 후진
이 함수들도 회전 속도와 지속 시간을 인수로 주는데 시간이 음수값이면 계속 회전하는 상태로 놔두게
된다.
#define PWM_RIGHT 3
#define DIR_RIGHT 12
#define BRAKE_RIGHT 9
#define PWM_LEFT 11
#define DIR_LEFT 13
#define BRAKE_LEFT 8
void setup() {
pinMode(PWM_LEFT, OUTPUT);
pinMode(DIR_LEFT, OUTPUT);
pinMode(BRAKE_LEFT, OUTPUT);
pinMode(PWM_RIGHT, OUTPUT);
pinMode(DIR_RIGHT, OUTPUT);
pinMode(BRAKE_RIGHT, OUTPUT);
analogWrite(PWM_LEFT,0);
analogWrite(PWM_RIGHT,0);
}
void loop() {
runForward(255, 1000);
delay(1000);
turnLeft(255, 600);
delay(1000);
}
#define PWM_LEFT 11
#define DIR_LEFT 13
#define BRAKE_LEFT 8
void setup() {
pinMode(PWM_LEFT, OUTPUT);
pinMode(DIR_LEFT, OUTPUT);
pinMode(BRAKE_LEFT, OUTPUT);
130 - 바퀴 로봇 제어
pinMode(PWM_RIGHT, OUTPUT);
pinMode(DIR_RIGHT, OUTPUT);
pinMode(BRAKE_RIGHT, OUTPUT);
analogWrite(PWM_LEFT,0);
analogWrite(PWM_RIGHT,0);
}
void loop() {
runForward(255, 1000);
delay(1000);
turnLeft(255, 600);
delay(1000);
}
void stopAll() {
leftMotorControl(0);
rightMotorControl(0);
}
delay(iTime);
stopAll();
}
if (iSpd > 0) {
digitalWrite(BRAKE_RIGHT, LOW);
digitalWrite(DIR_RIGHT, LOW);
analogWrite(PWM_RIGHT, spd);
}
else if (iSpd < 0) {
digitalWrite(BRAKE_RIGHT, LOW);
digitalWrite(DIR_RIGHT, HIGH);
132 - 바퀴 로봇 제어
analogWrite(PWM_RIGHT, spd);
}
else // if (spd == 0)
digitalWrite(BRAKE_RIGHT, HIGH);
}
Chapter
10
다른 아두이노들
134 - 부 록
10.1 아두이노 프로 미니
아두이노 우노는 크기가 상대적으로 크고 빵판에 꼽아서 실험하기 불편하기 때문에 이런 단점
을 보완한 소형 제품들이 몇 가지 마련되어 있다. 아두이노 정품 중에서 가장 작은 크기를 가지
는 보드로 프로 미니(pro mini)가 있다. 동작 전압에 따라 두 가지 모델이 분리되어 있다. (5V
동작 모델/ 3.3V동작 모델)
전원을 인가할 때 한 가지 주의할 점은 정전압 (즉, 정류된 정확한 3.3V/5V 전압) 은 VCC핀에
연결해야 한다는 것이다. 정류되지 않은 전압(즉 정확히 3.3V/5V 가 아니라면)은 RAW핀에 연
결해야 하는데 내장된 레귤레이터가 필요한 전압으로 바꿔준다. 그리고 VCC 와 RAW 핀 두 개
에 동시에 전원을 인가하면 안된다. 보드가 상할 수도 있다고 한다.
핀아웃 다이어그램은 그림[7.1.2]와 같다. 아두이노 우노와의 차이점은 핀 배치가 틀리므로
표준 쉴드(shield)를 사용할 수 없다는 것과, 크기를 줄이기 위해서 USB인터페이스부가 생략되
어 있다는 점이다. 따라서 프로그램을 다운로드 하려면 별도의 UART to USB 변환기를 사용해
야 한다. ([그림 7.1.3] 참조) 따라서 이 제품은 다량의 완성품을 만들어야 하는 경우에 하나의
변환기로 여러 개의 보드를 프로그램할 수 있으므로 단가를 낮출 수 있어 유리하다.
부 록 - 135
ㆍ아두이노 우노 (uno)
ㆍ아두이노 나노 (nano)
ㆍ아두이노 미니 (mini)
ㆍ아두이노 프로미니 (promini)
Microcontroller AT91SAM3X8E
Operating Voltage 3.3V
Input Voltage (recommended) 7-12V
Input Voltage (limits) 6-16V
Digital I/O Pins 54 (of which 12 provide PWM output)
Analog Input Pins 12
Analog Outputs Pins 2 (DAC)
Total DC Output Current on all
130 mA
I/O lines
DC Current for 3.3V Pin 800 mA
DC Current for 5V Pin 800 mA
Flash Memory 512 KB all available for the user applications
SRAM 96 KB (two banks: 64KB and 32KB)
Clock Speed 84 MHz
-----------------------------------------------
void setup() {
// put your setup code here, to run once:
pinMode(2, OUTPUT);
}
void loop() {
// put your main code here, to run repeatedly:
while(1) {
140 - 부 록
digitalWrite(2, HIGH);
digitalWrite(2, LOW);
}
}
-------------------------------------------
10.3 갈릴레오 보드
약간의 구글링 결과 제조사 홈페이지에서 확인한 바에 의하면 I2C 통신의 경우 갈릴레오 보드는
마스터로만 사용할 수 있고 100kHz의 속도만을 지원한다. 또한 GPIO핀의 신호를 변화시키는데
2ms 가 소요되고 실제 오버헤드를 고려한다면 230 Hz 정도가 최대이다. 생각보다 성능이 높지
는 않은 것 같다.
전 절에서 두에에서 했던 것과 똑같은 실험을 수행해 보았다. 즉 다음과 같은 코드를 실행시킨
다음 2번 핀의 주파수를 측정해 보는 것이다.
void setup() {
// put your setup code here, to run once:
pinMode(2, OUTPUT);
}
void loop() {
// put your main code here, to run repeatedly:
while(1) {
digitalWrite(2, HIGH);
digitalWrite(2, LOW);
}
}
구형파의 한
보드 프로세서 동작 클럭 주파수
주기 시간
아두이노 우노 (uno) ATmega328 16 MHz 5 us 200,000 Hz (200 kHz)
아두이노 두에 (due) AT91SAM3X8E 87 MHz 8 us 125,000 Hz (125KHz)
인텔 갈릴레오 intel Quark 400 MHz 8.5 ms 118 Hz
부록 A. C++ 의 클래스(class) 문법
아두이노의 프로그래밍 언어는 C++이다. 물론 C언어의 기초적인 문법만 이해하고 있어도 스
케치 작성에 무리가 없을 정도로 표준 함수나 라이브러리가 잘 만들어져 있기는 하지만 C++의
기본적인 문법을 알고 있다면 이해의 폭이 더 넓어질 것은 자명하다.
부록 A에서는 C언어와 확연히 구별되는 C++의 특징들을 위주로 기술하도록 하겠다. 먼저 함
수 중복, 디폴트 매개변수에 대해서 설명하고 C++의 핵심인 클래스(class)의 개념과 기본적인
문법들에 대해서 설명한다. 그리고 중간 중간에 아두이노에 적용할 수 있는 예제를 들어서 이해
를 돕도록 하겠다.
A.1.1 함수의 중복
A.1.2 디폴트 입력 인수
void func() {
int iA=0;
/// 함수 본체
....
}
void func(int iA = 0);
⇒
void func(iA) {
/// 함수 본체
....
}
이것을 살펴보면 디폴트 입력 인수 기능이 프로그램을 상당히 간결히 해주는 유용한 기능임
을 알 수 있다.
send(10);
send(10, “Hi”);
send();
send(“Hi”);
// 클래스 선언부
class Rect {
public:
int width, height;
int getArea();
};
// 클래스 구현부
double Rect::getArea() {
return width*height;
}
class Rect {
public:
int width, height;
int getArea(){
return width*height;
}
};
일견 후자의 경우가 더 간결해 보이고 실제로 함수 본체의 길이가 짧다면 별 문제가 없다. 하지
만 멤버 함수의 구현이 길어지고 이러한 멤버 함수가 여러 개라면 클래스의 전체적인 구조를 한
눈에 파악하기가 어려워지고 가독성이 떨어지게 된다. 따라서 전자와 같이 클래스의 선언부와
멤버함수의 구현부를 분리하는 것이 더 일반적이다.
A.2.2 클래스 멤버
class Rect {
public:
int width = 10, height = 20;
int getArea();
};
int getArea();
A.2.3 접근 지정자
double Rect::getArea() {
return width*height;
}
A.3 객체 생성 및 활용
A.3.1 객체 생성
Rect rect1;
이것을 실행하면 rect1 이라는 Rect 클래스를 구현한 객체가 생성되며 이것을 클래스의 인
스턴스(instance)라고 칭한다. 클래스의 인스턴스의 이름은 일반적인 변수 규칙에 따른다.
인스턴스가 생성되었다면 public 으로 지정한 멤버들은 닷(.) 접근자로 접근할 수 있다.
인스턴스의 이름 바로 뒤에 닷을 찍고 멤버의 이름을 써주면 된다. 예를 들면 다음고 같다.
rect1.width = 10;
rect1.height = 15;
부 록 - 153
A.3.2 객체의 멤버 접근
myRect yourRect
int weight : 10 int weight : 30
int height : 20 int height : 40
int getArea() int getArea()
154 - 부 록
A.4 생성자
A.4.1 생성자란?
class Rect {
Rect(); // 생성자1 선언
Rect(int w, int h); //생성자2 선언
....
};
// 생성자 구현
Rect::Rect() { // 입력 인수 없는 생성자
width = 0;
height = 0;
}
Rect::Rect(int w, int h) { // 입력 인수가 두 개인 생성자
width = w;
height = h;
}
Rect::Rect() { // 입력 인수 없는 생성자
width = 0;
height = 0;
return 0;
}
Rect();
부 록 - 155
Rect(int x);
Rect(double y);
Rect(int x, int y);
rect1.getArea();
rect2.getArea();
A.4.3 기본 생성자
class Rect{
Rect(); // 기본 생성자
.....
};
156 - 부 록
class Rect {
Rect(int w, int h);
....
};
Rect rect;
A.5 소멸자
Rect::~Rect() {
.....
}
class Rect {
.....
};
Rect rect1;
void func() {
Rect rect2;
Rect rect3;
.....
}
int main() {
.....
func();
.....
}
프로그램 시작
158 - 부 록
A.7 접근 지정자와 사용 예
결할 수 있으므로 Led 클래스는 연결된 핀 번호를 갖는 멤버가 있어야 한다. 따라서 다음과
같이 작성할 수 있다.
class Led {
public: byte pin;
};
class Led {
public:
byte pin;
Led(int);
};
Led::Led(int dp) {
pin = dp;
pinMode(pin, OUTPUT);
}
Led ledRed(12);
ledRed.pin = 11;
class Led {
public:
Led(int);
private:
byte _pin;
};
Led::Led(int pin) {
_pin = pin;
pinMode(_pin, OUTPUT);
}
class Led {
byte _pin;
public:
160 - 부 록
Led(int);
};
Led::Led(int pin) {
_pin = pin;
pinMode(_pin, OUTPUT);
}
class Led {
public:
Led(int);
void turnOn();
void turnOff();
private:
byte _pin;
};
Led::Led(int pin) {
_pin = pin;
pinMode(_pin, OUTPUT);
}
Led::void turnOn() {
digitalWrite(_pin, HIGH); // 멤버 함수는 _pin을 사용할 수 있다.
}
Led::void turnOff() {
digitalWrite(_pin, LOW); // 멤버 함수는 _pin을 사용할 수 있다.
}
만약 12번 핀에 빨간색 LED가 연결되어 있다면 다음과 같이 Led 객체를 사용할 수 있다.
Led ledRed(12);
....
부 록 - 161
ledRed.turnOn();
delay(500);
ledRed.turnOff();
delay(500);
....
ledBlue.turnOn();
class Button {
public:
Button(byte, boolean); // 생성자
boolean isPushed(); // 버튼이 눌렸다면 true를 반환하는 멤버 함
수
private:
byte _pin; // 연결된 핀
boolean _internalPullup; //내부에 풀업이 되었는가
};
// 생성자의 구현
Button::Button(byte pin, boolean internalPullUp = true) {
_pin = pin;
_internalPullup = internalPullUp;
if (_internalPullup)
pinMode(_pin, INPUT_PULLUP);
else
pinMode(_pin, INPUT);
}
// 멤버 함수의 구현
boolean Button::isPushed() {
return (digitalRead(_pin) == LOW)? true:false;
}
void setup() {
pinMode(13, OUTPUT);
}
void loop() {
if (btn1.isPushed()) // 만약 버튼이 눌렸다면
부 록 - 163
digitalWrite(13, HIGH);
else // 그렇지 않다면 (눌리지 않았다면)
digitalWrite(13, LOW);
}
A.8 인라인 함수
컴파일러는 인라인 함수를 호출하는 곳에 인라인 함수의 코드를 그대로 삽입하여 함수의 호출이
일어나지 않도록 한다. 이렇게 되면 함수를 호출할 때 드는 비용이 없어지기 때문에 속도가 더
올라간다. 반대급부로 프로그램의 길이는 더 길어지게 된다.
class Button {
public:
Button(byte, boolean); // 생성자
boolean isPushed(); // 버튼이 눌렸다면 true를 반환하는 멤버 함
수
private:
byte _pin; // 연결된 핀
boolean _internalPullup; //내부에 풀업이 되었는가
};
// 생성자의 구현
inline Button::Button(byte pin, boolean internalPullUp = true) {
_pin = pin;
_internalPullup = internalPullUp;
if (_internalPullup)
pinMode(_pin, INPUT_PULLUP);
else
pinMode(_pin, INPUT);
}
// 멤버 함수의 구현
inline boolean Button::isPushed() {
return (digitalRead(_pin) == LOW)? true:false;
}
class Button {
public:
Button(byte, boolean); // 생성자
boolean isPushed();{
return (digitalRead(_pin) == LOW)? true:false;
}
boolean isPushed(); // 버튼이 눌렸다면 true를 반환하는 멤버 함
수
private:
byte _pin; // 연결된 핀
boolean _internalPullup; //내부에 풀업이 되었는가
};
// 생성자의 구현
inline Button::Button(byte pin, boolean internalPullUp = true) {
_pin = pin;
_internalPullup = internalPullUp;
if (_internalPullup)
pinMode(_pin, INPUT_PULLUP);
부 록 - 165
else
pinMode(_pin, INPUT);
}
struct Name {
private:
int iA;
void func1();
public:
double dA;
void func2();
protected:
short sA;
};
C++의 구조체의 인스턴스 생성은 클래스와 동일한데 C언어의 경우에는 struct 키워드를 붙여
야 하지만 C++에서는 그렇지 않다.
Name ins1;
struct Name {
int iA;
double dA;
};
166 - 부 록
class Name {
public:
int iA;
double dA;
};
A.10 정적 멤버
A.10.1 정적 멤버 변수
class Led {
public:
Led();
void turnOn();
void turnOff();
static int iCount; // 정적 멤버 변수
private:
byte _pin;
};
class Led {
public:
Led();
void turnOn();
void turnOff();
static int iCount = 0; // 오류 발생
private:
byte _pin;
};
int Led::iCount = 0;
A.10.2 정적 멤버 함수
class Util {
public:
int iA;
static double dA;
void getA() {
iA += (int)dA; // 정적과 비정적 변수를 모두 사용가능
return iA;
}
static void getB() {
return dA; //정적 변수만 사용 가능
}
};
1. setup() 함수
2. loop() 함수
1. pinMode(pin, INPUT/OUTPUT) 함수
2. digitalWrite(pin, HIGH/LOW) 함수
3. digitalRead(pin) 함수
1. analogWrite(pin, value)
170 - 부 록
2. analogRead(pin)
3. ananlogReference(type)
2. noTone(pin)
B.5 시간 관련 함수
1. millis()
2. micros
3. delay(time)
• 지연 수행 (아무런 일도 안 하고 멈춰 있음)
• time 은 밀리세컨드 단위로 입력 (예: 500은 0.5초)
4. delayMicroseconds(time)
• 지연 수행 (아무런 일도 안 하고 멈춰 있음)
• time 은 마이크로 세컨드 단위로 입력 (예: 500은 0.0005초)
• 입력의 최대값은 16383임.
B.6 난수
1. randomSeed(seed)
• 난수 발생기를 초기화함.
• 아두이노에서는 아날로그 입력값을 이용하여 초기화.
• 전형적인 사용 예: randomSeed(analogRead(A0))
2. random(max), random(min,max)
발행인 박 장 현
발행일 2015년 2월 27일
발행처 국립목포대학교
전라남도 무안군 청계면 영산로 1666
인 쇄 중앙기획(Tel. 061-276-0276)