칩두이노에 스위치 입력 삽질기.
우선 테스트 영상.
첫 번째 실습부터 삽질이다.
칩 두이노에 LED 와 스위치를 연결하여 스위치를 한 번 누르면 LED 가 켜지고 다시 한 번 누르면 깜빡 거리고 다시 한 번 누르면 더 빨리 깜빡거리고 마지막으로 꺼지는 것을 만들고자 하였다. 마치 자전거용 전조등처럼 말이다. 추후에 자전거 전조등 자작을 염두에 둔 실습이었는데, 스위치에 좋지 않은 문제가 하나 있었다. 바로 기계식 스위치가 갖고 있는 문제인 바운스 현상이라 하는데, 스위치 내부에 스프링위에 올라간 접점이 붙거나 떨어질때 탄성으로 인하여 한 번 더 붙거나 하는 현상이 다. 아래 그림을 보면 이해가 쉬울 것 이다.
스위치 바운스.
처음에는 본인 직업인 소프트웨어 개발자 답게 코딩으로 딜레이를 주는 방식으로 해결해볼려고 했는데 아무래도 물리적인 문제를 논리적으로 해결하려고 하는 것은 눈가리고 아웅하는 것 같아 근본적인 해결책을 찾아 인터넷을 뒤져 보았다.
인터넷에 돌아다니는 디바운스 회로등을 참고하여 아래와 같이 10KOhm (풀다운)저항과 함께 0.1uf 세라믹 콘덴서를 하나 붙었는데 스위치를 누르는 순간 칩두이노가 리셋이 되었다. ㅠ_ㅠ
왜 그런가 곰곰히 생각해보고 검색해보니 아마도 버튼을 누르는 순간 커패시터, 즉 콘덴서에 갑자기 전류가 흘러 들어가면서 과전류가 일어나 리셋이 되는 것 같았다. 게다가 USB 허브에 위에 회로를 연결하면 리셋이 100% 되지만 PC 본체에 직접 연결하면 좀처럼 리셋이 되지 않는다. 그리고 회로를 임의대로 아래와 같이 수정하였다.
이렇게 하면 맞는지 확신은 안 들지만, 일단 회로는 리셋없이 잘 동작한다. 실제로 값을 확인해 보기 위하여 안드로이드용 회로 시뮬레이션을 설치하여 나름대로 테스트를 하고 문제가 없는지 확인하였다. 이런식으로 하면 버튼을 눌러도 급작스러운 과전류가 일어나지 않는다.
아래는 안드로이드용 회로 시뮬레이터로 테스트한 화면. 이게 맞는지는 모르겠지만, 결과는 실제와 유사할 것이라고 생각된다.
(너무 야매 아니야? )
좌측은 처음에 언급한 문제가 되었던 회로 테스트 화면. 사진이 작아서 잘 안 보이는데 스위치를 연결하는 순간 배터리에서 소비되는 전류가 순간적으로 올라간다. 우측은 수정한 회로. 스위치를 빠르게 뗏다 붙였다 하면서 테스트 하였다. 1.5mA 이상으로 올라가지 않는다. (입력 포트 역할을 하는 LED 는 5V 에 1mA 값을 줬다.)
실제 회로 구성 사진. 전압 테스트를 위하여 아날로그 포트를 하나 물렸다.
디버깅은 시리얼 통신을 통하여 하는데 굉장히 편하다.ㅎ
최종적인 회로도는 이렇게 된다고 보면 된다.
(ps. 저는 마침 10uF 탄탈 콘덴서가 부품함에 돌아다녀서 사용했는데, 전해 콘덴서 같이 극성있는 다른 부품 사용해도 상관 없습니다.)
코드 : 아날로그 포트로 전압 체크하여 시리얼 통신으로 로그 출력하는 버전.
아래 코드에서 전압을 체크하기 위하여 5v 를 기준으로 하였다. VCC 에 입력된 전압인 5v 에 1024(아날로그 입력이 0~1024 값을 갖고 있으므로) 를 나누어서 얻은 결과(1024 / 5 = 0.0048828... ) 를 아날로그 입력 값에 곱하여 전압을 구한다. 만약 VCC 에 들어오는 값이 5V 가 아니고 다른 값이라면 그 값에 1024 를 나눈 값을 아날로그 입력 단자를 통하여 들어온 수치에 곱하여 전압을 구해야 한다.
#define MODE_OFF 0 #define MODE_ON 1 #define MODE_BLINK_SLOW 2 #define MODE_BLINK_FAST 3 #define MODE_SIZE 4 #define PIN_SWITCH 10 #define PIN_LED 11 long lastMillis = 0; long currentMillis = 0; int mode = MODE_OFF; int lastMode = MODE_OFF; int lineCount = 0; float voltage = 0; boolean isPush = false; boolean isReady = false; boolean isLEDOn = false; void setup() { pinMode(PIN_SWITCH, INPUT); pinMode(PIN_LED, OUTPUT); Serial.begin(9600); } void loop() { readyIndecate(); currentMillis = millis(); voltage = analogRead(4); voltage = voltage * 0.0048; if(digitalRead(PIN_SWITCH) == HIGH && !isPush) { isPush = true; lineCount++; Serial.print(lineCount); Serial.print(" :: \tPush on"); Serial.print("\tvolt : "); Serial.println(voltage); } else if(digitalRead(PIN_SWITCH) == LOW && isPush) { isPush = false; lineCount++; Serial.print(lineCount); Serial.print(" :: \tPush on"); Serial.print("\tvolt : "); Serial.println(voltage); mode = ++mode % MODE_SIZE; } if(voltage > 0.1) { lineCount++; Serial.print(lineCount); Serial.print(" :: \tvolt chek : "); Serial.println(voltage); } if(mode == MODE_OFF) { digitalWrite(PIN_LED, LOW); } else if(mode == MODE_ON) { digitalWrite(PIN_LED, HIGH); } else { int delayMillis = (mode == MODE_BLINK_FAST)?30:100; if(currentMillis - lastMillis > delayMillis) { isLEDOn = !isLEDOn; digitalWrite(PIN_LED, isLEDOn); lastMillis = currentMillis; } } if(mode != lastMode) { lastMode = mode; lineCount++; Serial.print(lineCount); Serial.print(" :: \tmode : "); Serial.println(mode); } } // 초기화가 완료되면 LED를 5번 깜빡여준다. void readyIndecate() { if(isReady == false) { for(int i = 0; i < 5; ++i) { isLEDOn = !isLEDOn; digitalWrite(PIN_LED, isLEDOn); delay(50); } isReady = true; digitalWrite(PIN_LED, LOW); isLEDOn = false; } }
코드 : 전압 체크하지 않는 버전.
#define MODE_OFF 0 #define MODE_ON 1 #define MODE_BLINK_SLOW 2 #define MODE_BLINK_FAST 3 #define MODE_SIZE 4 #define PIN_SWITCH 10 #define PIN_LED 11 long lastMillis = 0; long currentMillis = 0; int mode = MODE_OFF; int lastMode = MODE_OFF; boolean isPush = false; boolean isReady = false; boolean isLEDOn = false; void setup() { pinMode(PIN_SWITCH, INPUT); pinMode(PIN_LED, OUTPUT); } void loop() { readyIndecate(); currentMillis = millis(); if(digitalRead(PIN_SWITCH) == HIGH && !isPush) { isPush = true; } else if(digitalRead(PIN_SWITCH) == LOW && isPush) { isPush = false; } if(mode == MODE_OFF) { digitalWrite(PIN_LED, LOW); } else if(mode == MODE_ON) { digitalWrite(PIN_LED, HIGH); } else { int delayMillis = (mode == MODE_BLINK_FAST)?30:100; if(currentMillis - lastMillis > delayMillis) { isLEDOn = !isLEDOn; digitalWrite(PIN_LED, isLEDOn); lastMillis = currentMillis; } } if(mode != lastMode) { lastMode = mode; } } // 초기화가 완료되면 LED를 5번 깜빡여준다. void readyIndecate() { if(isReady == false) { for(int i = 0; i < 5; ++i) { isLEDOn = !isLEDOn; digitalWrite(PIN_LED, isLEDOn); delay(50); } isReady = true; digitalWrite(PIN_LED, LOW); isLEDOn = false; } }