본문 바로가기
아두이노

[Do it! 아두이노] 8. 아두이노 출력으로 아두이노 작동하기

by Lizardee 2023. 8. 19.
1. 펄스 폭 변조 이해하기

아두이노는 디지털 장치이므로, 입력받은 아날로그 신호를 그대로 처리하지 못한다. 그래서 아날로그 디지털 변환기(ADC, analog digital converter)로 입력값을 디지털 신호로 변환한 다음 처리한다.

그렇다면 출력값을 아날로그 신호로 보낼 때는 어떻게 할까? 이때 사용하는 방법이 바로 펄스 폭 변조(PWM, pulse width modulation)이다. PWM을 사용하면, 입력받은 디지털 신호를 마치 아날로그 신호처럼 변환해 출력할 수 있다.

 

▶ 펄스 폭 변조(PWM)란?

펄스(pulse)는 사각파(square wave) 모양을 가진 파형을 의미한다. 사각파는 디지털 신호 HIGH와 LOW를 반복해서 보내면 만들 수 있다. 이때 반복이 일어나는 시간 간격을 클럭 주기(clock perioud)라고 하며, 클럭 주기에서 HIGH 신호가 차지하는 비율을 듀티 사이클(duty cycle)이라고 한다. 즉, 듀티 사이클(duty cycle)이 100%이면 디지털 신호는 항상 HIGH이고, 듀티 사이클이 0%이면 디지털 신호는 항상 LOW이다.

듀티 사이클을 조절하면 사각파의 폭을 조절할 수 있다.

 

▶ PWM으로 아날로그 신호를 표현하는 원리

PWM으로 아날로그 신호를 표현하는 원리

다음은 LED가 1초 간격으로 깜빡이는 회로와 스케치 코드이다. 'LED 켜기 --> 1초 대기 --> LED 끄기 --> 1초 대기'를 반복해서 깜빡임 효과를 만든다.

이때 13번 핀에 들어왔다 나가는 디지털 신호는 사각파로 표현된다. HIGH일 때가 1,000ms, LOW일 때가 1,000ms이므로 주기는 2,000ms(2초)이다. 주기의 절반만큼 HIGH 신호를 유지하므로 듀티 사이클은 50%이다.

듀티 사이클 = 90%: 깜빡이는 속도가 매우 빨라서 LED 불빛은 항상 켜 있는 것처럼 보인다.
듀티 사이클 = 10%: LED가 어두워진다.

디지털 신호만으로는 1 또는 0, HIGH 또는 LOW, 켜짐 또는 꺼짐과 같이 두 가지 상태만을 표현할 수 있지만, PWM을 사용하면 다양한 상태를 표현할 수 있다. 이것이 PWM으로 디지털 신호를 아날로그 신호처럼 표현하는 방법이다. 이 방법으로 여러 색상과 밝기를 가진 불빛을 표현하거나 DC 모터의 회전 속도를 조절하는 등 다양한 분야에서 아두이노를 사용할 수 있다.

 

▶ PWM을 지원하는 디지털 핀

: 아두이노 우노에서 PWM을 지원하는 핀은 디지털 3, 5, 6, 9, 10, 11번 이렇게 여섯 개이다. 이들의 핀 번호 앞에는 ~ 기호가 표시되어 있다.

PWM을 지원하는 디지털 핀

 

Do it! 실습 8-1. 아날로그 출력으로 LED 밝기 조절하기

아날로그 출력으로 LED 밝기 조절하기

// C++ code
//
void setup()
{
  Serial.begin(9600);
}

void loop()
{
  for(int i=0;i<255;i++){ //0~255까지 256번 반복, LED가 점점 밝아진다.
    analogWrite(9, i);    //i값을 PWM이 가능한 디지털 9번 핀에 써서 LED 밝기 조절
    Serial.println(i);   
    delay(10);            //10ms 지연
  }
}
  • analogWrite(9, i): i값을 PWM이 가능한 디지털 9번 핀에 써서 LED 밝기 조절

 

Do it! 실습 8-2. 가변저항으로 LED 밝기 조절하기

가변저항으로 LED 밝기 조절하기

// C++ code
//
void setup()
{
  Serial.begin(9600);
}

void loop()
{
  int readValue = analogRead(A0);
  
  //map() 함수를 이용해 입력값의 범위를 변경
  int convertedValue = map(readValue, 0, 1023, 0, 255);
  analogWrite(9, convertedValue);
  
  //직렬 모니터로 가변저항값과 범위가 변경된 값 출력
  Serial.print(readValue);
  Serial.print("\t");
  Serial.println(convertedValue);
}

▷ map() 함수

map() 함수는 출력값의 범위를 바꾸고 싶을 때 사용한다. 예를 들어, 입력값의 범위가 0~100일 때 출력값의 범위를 0~200이나 -100~50 등으로 변경할 수 있다.

map() 함수를 사용하는 이유는 analogRead() 함수로 읽는 값의 범위와 analogWrite() 함수로 쓰는 값의 범위가 다르기 때문이다. analogRead() 함수로 읽는 값의 범위는 10bit ADC를 거치므로 0~1023이지만, analogWrite() 함수로 쓰는 값의 범위는 8bit PWM을 거치므로 0~255이다. 따라서 map() 함수는 입력값의 범위 0~1023을 출력값의 범위 0~255로 변환하기 위해 사용한다.

 


2. 삼색 LED로 다양한 불빛 표현하기

▶ 삼색 LED란?

: 빛의 삼원색인 빨간색(red), 초록색(green), 파란색(blue)을 혼합하여 다양한 색상의 빛을 만들어 내는 전자 부품

삼색(RGB) LED

 

몇 가지 색을 출력할 수 있을까?

: digitalWrite() 함수는 HIGH와 LOW 값만 출력할 수 있으므로, 삼색 LED의 R, G, B 단자에 각각 연결했을 때 꺼진 상태를 제외하고 총 7가지 색을 표현할 수 있다.

삼색 LED의 디지털 출력 결과

 

Do it! 실습 8-3. 삼색 LED 회로 만들기

삼색 LED 회로 만들기

// C++ code
//
void setup()
{
  Serial.begin(9600);
  
  pinMode(11, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(9, OUTPUT);
}

void loop()
{
  int red = random(256);     //0~'매개변수-1' 사이의 수를 무작위로 반환
  int blue = random(0, 256);  //첫 번째 매개변수~'두 번째 매개변수-1' 사이의 수를 무작위로 반환
  int green = random(0, 256);
  
  analogWrite(11, red);       //임의의 값으로 색상 출력
  analogWrite(10, blue);
  analogWrite(9, green); 
  
  Serial.print("R:");         //직렬 모니터에 값 출력
  Serial.print(red);
  Serial.print("\tB:");
  Serial.print(blue);
  Serial.print("\tG:");
  Serial.print(green);
  
  delay(1000);
}
  • int red = random(256); 0~'매개변수-1' 사이의 수를 무작위로 반환
  • analogWrite(11, red); 임의의 값으로 색상 출력

random() 함수에 의해 R, G, B 값이 0~255 안에서 계속 바뀐다. R, G, B 값이 바뀔 때마다 삼색 LED 불빛의 색상도 달라진다.

 


3. 가변저항과 슬라이드 스위치로 DC 모터 제어하기

▶ DC(direct current) 모터란?

: 직류 전원으로 회전 운동을 하는 전자 부품

  • 아두이노 보드가 일정한 크기의 전압으로 작동하듯이, DC 모터 또한 일정한 전압에 작동한다.
    DC 모터는 바퀴에 연결해 라디오 컨트롤 자동차(RC카, radio control car)를 움직이거나 진동 드라이버를 좌우로 회전시키는 등 다양한 분야에 적용할 수 있다.

 

Do it! 실습 8-4. DC 모터의 속도 제어하기

DC 모터의 속도 제어하기

// C++ code
//
void setup()
{
  pinMode(9, OUTPUT);
}

void loop()
{
  //가변저항의 입력값 범위를 map() 함수로 변환
  int inputValue = analogRead(A0);
  int convertedValue = map(inputValue, 0, 1023, 0,  255); //범위를 0~1023에서 0~255로 변환
  
  //가변저항의 값에 따라 모터의 속도 조절
  analogWrite(9, convertedValue);
  
  delay(100);
}

 

Do it! 실습 8-5. DC 모터의 방향 제어하기

DC 모터의 방향 제어하기

// C++ code
//
void setup()
{
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  
  //스위치의 입력을 받기 위해 디지털 8번 핀을 입력 모드로 설정
  pinMode(8, INPUT);
}

void loop()
{
  //가변저항의 입력값 범위를 map() 함수로 변환
  int inputValue = analogRead(A0);
  int convertedValue = map(inputValue, 0, 1023, 0,  255); //범위를 0~1023에서 0~255로 변환
  
  //스위치의 입력값에 따라 DC모터의 방향 제어(LOW, HIGH)
  int inputSwitch = digitalRead(8);
  if(inputSwitch == LOW){
    analogWrite(9, convertedValue); //LOW값을 읽으면 9번 핀에 전원을 공급
    analogWrite(10, 0);
  }
  else{
    analogWrite(9, 0);
    analogWrite(10, convertedValue); //HIGH값을 읽으면 10번 핀에 전원을 공급
  }
}

--> 슬라이드 스위치로 DC 모터의 방향을 제어한다.

: 양수일 때는 시계 방향으로 회전하고, 음수일 때는 시계 반대 방향으로 회전한다.

 


도전: LED 색상을 직접 제어해 봐요!

: 가변저항과 푸시 버튼으로 삼색 LED의 RGB 색상 제어하기

가변저항과 푸시 버튼으로 삼색 LED의 RGB 색상 제어하기

int flag            = 0;	// 버튼의 릴리즈 상태를 구분
int colorSelection  = 0;	// 0:빨강(R), 1:초록(G), 2:파랑(B)
int redValue        = 0;	// 빨간색 값(0~255)
int greenValue      = 0;	// 초록색 값(0~255)
int blueValue       = 0;	// 파란색 값(0~255)

void setup()
{
  // 삼색 LED의 핀 모드 설정
  pinMode(11, OUTPUT);		// 디지털 11번 핀: 빨강(R)
  pinMode(9, OUTPUT);		// 디지털 9번 핀: 초록(G)
  pinMode(10, OUTPUT);		// 디지털 10번 핀: 파랑(B)
  
  // 푸시 버튼에 대한 입력모드 설정
  pinMode(8, INPUT);
  
  // 시리얼 모니터에 출력을 위한 초기화
  Serial.begin(9600);
}

void loop()
{
  // 푸시 버튼을 누를때마다 R, G, B의 상태 변경
  int inputValue = digitalRead(8);
  if (inputValue == HIGH) {	
    if (flag == 0)
      flag = 1;
  } else {
    if (flag == 1) {
      colorSelection++;
      Serial.println("Color is changed!!!");
      if(colorSelection > 2)
        colorSelection = 0;
      flag = 0;
    }
  }
  
  // 가변저항으로 변경한 값을 임시로 저장
  int readValue = analogRead(A0);
  int colorLevel = map(readValue, 0, 1023, 0, 255);
  
  if (colorSelection == 0)
    redValue = colorLevel;
  else if (colorSelection == 1)
    greenValue = colorLevel;
  else
    blueValue = colorLevel;
    
  // 가변저항으로 변경한 R, G, B 값을 삼색 LED에 적용
  analogWrite(11, redValue);
  analogWrite(9, greenValue);
  analogWrite(10, blueValue);
  
  // 시리얼 모니터에 R, G, B 값 출력
  Serial.print("R:");
  Serial.print(redValue);
  Serial.print("\tG:");
  Serial.print(greenValue);
  Serial.print("\tB:");
  Serial.println(blueValue);
  
  delay(1000);
}