본문 바로가기
Arduino&RasberryPi/Arduino Uno

11. Arduino - 가변저항을 이용한 회로 구성과 map() 함수 사용법

by Rosmary 2019. 11. 2.
728x90
반응형

지금까지 획득한 지식을 바탕으로, 가변 저항이 들어간 회로를 하나 구성해보려고 한다. 회로는, 가변 저항의 크기 변화에 따라, 회로 위의 발광 다이오드(LED) 밝기도 변하도록 만들어보려고 한다. 그리고, 코드를 작성하는 과정에서, 번거로운 연산을 간편하게 진행할 수 있는 코드인 map() 함수의 사용법에 대해 알아보려고 한다.

 

 

 

1. 회로의 구성 및 동작 과정 확인

 

 

LED 밝기 변화는 크게 2가지 방법으로 진행할 수 있다. 첫 번째 방법은, LED와 가변 저항을 동일 회로에 위치하도록 만들고, 일정한 전원(3.3V나 5V Power 핀을 통해)을 회로에 공급한 뒤, 가변 저항의 크기를 조절하여 LED 밝기를 변화시키는 것이다. 나머지 하나는, 공급 전원의 크기가 가변 저항의 변화에 따라 비례하여 공급되도록 만드는 것이다.

 

 

필자는 먼저, 첫 번째 방법으로 회로를 구성하였다.

 

 

이 회로는, 따로 전원 공급과 관련된 코드를 작성하지 않기 위해, 직접 5V Power 핀을 회로로 연결시켰다. 이 회로는 LED와 220옴 저항을 지나 가변저항의 첫 번째 단자로 연결되도록 만들었다. 이후, 잉여 전압이 빠져나가는 길을 마련하기 위해, 가변저항의 가운데 단자를 GND 핀과 연결해 주었다.

 

위와 같이 회로를 구성하면, 수고스럽게 코드를 작성할 필요가 없다는 장점이 있다. 회로만 연결하더라도, 가변저항의 크기 변화에 따른 LED 밝기 변화를 즉시 확인할 수 있다. 아래의 GIF처럼.

 

 

 

그러나, 위와 같은 방법은, 회로에서 사용되는 전체 저항값과 그에 따른 전류량, 그리고 부품을 지나며 소비되는 전압값을 사전에 철저히 계산해야한다는 번거로움이 있다. 필자는 처음에, 빨간색 LED 부품을 저항도 없이 위와 같은 방법으로 회로를 만들어 작동시켰다가, 애꿎은 LED 하나를 날려먹었다. 빨간색 LED가 허용할 수 있는 범위 이상의 전압이 LED로 흘러 들어가면서, LED 내부 회로가 터져버린 것이다....(회로를 만들 때, 만일을 위해 저항은 꼭 설치하자)

 

 

*  아직 이유를 찾아내지 못했는데, 같은 LED라고 하더라도, LED 색에 따라 허용가능한 전압 범위는 각기 다른 듯 하다. 빛의 파장이 긴 녀석들이 일반적으로 조금 더 낮은 허용 전압을 가지고,  파장이 짧은 녀석들은 조금 더 높은 전압까지 버티는 듯 하다. 빛의 파장은 무지개를 떠올리면 되는데, 빨간색에 가까울수록 빛의 파장이 길고, 보라색으로 갈수록 빛의 파장이 짧아진다. 

 

 

이러한 단점을 해결할 수 있는 방법이 바로 두 번째 방법. 가변 저항을 마치 카오디오의 볼륨 조절기처럼 사용하여, 이 조절기를 통해 회로에 공급되는 전압 값을 변화시키는 방법이다. 이 방법을 사용하면, LED 전구의 허용 전압 범위 내의 전압만을 일정하게 공급할 수 있다는 장점이 생긴다. 당연하게도 이 회로의 단점은, LED 회로와 가변저항 회로가 분리되어 있고, 이 때문에 회로를 동작시려면 약간 복잡한 코드를 작성해야 한다는 것이다.

 

회로의 구성은 아래 사진과 같다.

 

 

회로는 크게 두 부분으로 구성되어 있다. 9번 핀에서 공급되는 전원을 받는 LED 회로와, 5V Power 핀으로부터 전원을 공급받는 가변저항 회로가 그것이다. 이 두 회로는 GND 핀으로 동시에 연결되어야하므로, (-) 버스 스트립을 공유한다.

 

 

 

그리고 코드는 다음과 같다.

 

int powerLED = 9;

int readResistance = A0;

int readValue;      //  가변저항으로부터 들어온 analogRead() 값을 읽음

int writeValue;     //  readValue 값으로부터 analogWrite() 함수의 두 번째 인자값을 도출한 결과를 저장하는 변수

int voltage_permit;        //  writeValue 값으로부터 허용 전압 범위 내의 값을 도출한 결과를 저장하는 변수

 

void setup()

{

    Serial.begin(9600);

    pinMode(powerLED, OUTPUT);      // 9번 핀으로 LED 회로에 전원 공급

    pinMode(readResistance, INPUT);   // A0 핀으로 가변저항 analog 값을 읽어들임.

}

 

void loop()

{

    readValue = analogRead(readResistance);   //  A0 핀에서 읽은 AnalogRead 값

    writeValue = readValue * ( 255. / 1023. ) ;   //  AnalogRead 값을 AnalogWrite 인자값으로 변경

    voltage_permit = writeValue * ( 4./ 5.);      //  LED에 공급할 전압 값을 0 ~ 4 볼트 내로 지정.

    analogWrite(powerLED, voltage_permit);

    Serial.print("공급 전원: ");

    Serial.println(int(voltage_permit * 4. / (255 * 4. / 5.). ));

}     

    

 


 

*  LED 의 허용 전압 범위는 1.8V 에서 2V 사이다. 하지만 필자는 0에서 4V 사이 값으로 범위를 조정했는데, LED 기본 허용 전압 값 내에서는 밝기 변화가 극명하게 보이지 않기 때문이다. 조금 더 넓은 허용 전압 범위를 사용하여 회로를 동작시키기 때문에, 만일을 대비하여 저항 하나를 회로에 추가한 것이다.

 

 

*  변수값 계산 시, 나눗셈 피연산자의 뒤에 점(.)이 찍혀 있는데, 이는 해당 연산을 수행한 결과를 소수값으로 출력하기 위한 것이다. 왜냐하면, 해당 연산의 결과값을 받는 변수의 자료형이 정수형이기 때문이다. 아래의 예시를 보자

 

int writeValue;   ->  정수형 변수

writeValue = analogRead(readResistance) *  (255 / 1023);

              = analogRead(readResistance) * 0   =  0               

 

->   255 / 1023의 결과는 0이다. 왜냐하면 정수 / 정수 연산으로 정수 값이 반환되기 때문이다.  따라서 나눗셈 피연산 숫자 뒤에 점(.)을 찍어줌으로써, 이 연산은 (실수) / (실수)임을 Arduino에게 알려주며, 이 연산의 결과값을 실수로 반환하도록 만들어준다. 실수로 반환된 결과값은, 정수형 변수에 들어갈 때 자동으로 정수형으로 변환된다. 참고로 (실수)/(정수)나 (정수)/(실수)도 연산 결과가 (실수)로 반환된다. 자세한 내용은 C 언어의 연산 관련 부분을 참고하면 된다.

 


 

 

위 회로에서, 필자가 가변 저항의 스위치를 오른쪽으로 돌리면, LED 밝기가 밝아지고, 왼쪽으로 돌리면 반대로 빛의 세기가 줄어든다. 위 작업의 결과는 다음과 같다.

 

 

 

용량 때문에, 작은 사이즈의 GIF 파일밖에 올릴 수 없어서 화면이 잘 보이지 않는다. 하지만, Serial Monitor에는 가변 저항 변화와 함께 LED 회로에 공급되는 전원이 시시각각 변하며 출력되는 것을 확인할 수 있다.

 

 

2. map() 함수의 사용

 

위에서 복잡한 회로의 작동 코드를 작성하는 과정에서, 꽤 번거로운 수식들이 몇 가지 있었다. analogRead() 값의 범위인 0~1023을 analogWrite() 값 범위인 0~255로 변환하는 과정과, 0~ 4V에 해당하는 analogWrite() 값인 0 ~ 204를 다시 0~4로 변환하는 과정이었다.

 

=========================================

writeValue = readValue * ( 255. / 1023. ) ;

Serial.println(int(voltage_permit * 4. / (255 * 4. / 5.). ));

==========================================

 

Arduino는 이렇게 연관되는 함수들이 사용하는 범위값이 제각각이라, 변환과정의 번거로움을 없애기 위해, map() 이라는 함수를 사용한다. 이 함수를 사용하면, 서로 다른 범위의 값을 가진 자료라도 선형 비례관계를 가진다면 복잡한 수식을 사용하지 않고 쉽게 값을 변환할 수 있다. map() 함수의 포멧은 아래와 같다

 

=====================================================================

map ( 변환할 값,  변환 전 범위A 최소값, 변환 전 범위A 최대값, 변환 후 범위B 최소값, 변환 후 범위B 최대값);

=====================================================================

 

전부 다 예시를 들기에는 내용이 많아지니, writeValue 값 하나만을 예로 들어서 사용해보겠다.

 

writeValue는 analogRead() 값인 readValue를 analogWrite() 값으로 변환한 결과를 저장한 변수다. 따라서 analogRead() 값은 변환 전 범위 A에 포함되며, analogWrite() 값은 변환 후 범위 B에 포함된다. 이 범위의 최소값과 최대값을 map() 함수 내에 대입하면 다음과 같이 나타낼 수 있다.

 

=================================

writeValue = map(readValue, 0, 1023, 0, 255);

=================================

 

이 코드는 맨 처음 작성한 "writeValue = readValue * ( 255. / 1023.);"과 동일한 결과를 나타낸다.

map() 함수의 결과값은 인자의 자료형에 상관없이 전부 정수로만 출력된다. 피연산 숫자의 자료형에 따라 결과값의 자료형이 달라지던 일반 연산과는 조금 다른 결과다. map() 함수가 정의된 코드를 열어봐야 알겠지만, 아마도 함수의 return 값이 int 형태로 지정되어 있지 않나라는 생각이 든다.

 

 

 

FIN.

반응형

댓글