지금까지 우리는 디지털 핀을 pinMode(pin번호, OUTPUT), 출력으로만 사용해봤다.
OUTPUT이 있으면 INPUT도 있다. 디지털 핀을 입력으로 사용하는 대표적인 예가 스위치다.
스위치를 사용해 LED를 제어해보자.
아두이노에서 OUTPUT,INPUT의 의미
OUTPUT : 아두이노로부터 전격전압을 받음. 스케치 코드에 따라 5V(상태1) or 0V(상태0)를 단자로 전달
데이터가 아두이노 -> 단자
INPUT : 단자가 아두이노한테 전격전압을 줌.
데이터가 단자 - > 아두이노
회로상에서 사용자가 스위치의 버튼을 누르고 떼는 행위 등을 통해
INPUT핀에 5V(1)이 전달되면 HIGH, 0V(0)이 전달되면 LOW가 된다.
이를 소스코드에서 digitalRead(INPUT핀번호)로 읽을 수 있다.
ex ) : if (digitalRead(5)==HIGH){}
스위치 종류
4핀스위치,2핀 스위치가 있다.
2핀스위치에서 보듯이 A type은 누르고 있을 때 연결된다. B type은 반대로 떼고 있을 때 연결된다.
4핀 스위치는 2핀스위치 2개를 합친 것.
4핀 스위치 사용
내부적으로 버튼을 누르지않아도 (1,3), (2,4)는 서로 연결되어있다.
따라서 항상 1,4 2,3이 연결되도록 하기 위해선 대각선으로 연결하도록 하자.
플로팅
'입력' 부분이 아두이노 입력핀이라 생각해보자.
위의 경우에 HIGH가 입력되고 아래경우에는 LOW가 입력이 될 것처럼 보인다.
하지만 스위치를 눌렀을때는 정상적으로 HIGH가 입력되지만
스위치를 뗏을 때는 흐르던 전류가 갈곳을 잃어 주변 자기장에 영향을 끼쳐 곧바로 LOW가 되지 않는다.
이를 값이 0이되지않고 붕 떠있다고 해서 floating현상이라고 합니다.
이를 해결하기 위해 저항을 연결해 전류의 흐름을 통제하는 방법인
풀업 또는 풀다운 저항을 이용합니다.
풀업,풀다운은 내부, 외부이 있습니다.
내부는 pinMode(pin번호, INPUT_PULLUP)와 같이 소스코드에서 PULLUP,PULLDOWN 선언만 해주면 돼서 간단합니다.
외부는 외부에 저항을 연결해 더 복잡하지만 안정적입니다.
외부풀업, 풀다운에 대해 알아봅시다.
풀업 : 스위치를 기준으로 입력전압쪽에 저항 연결
풀업 스위치 열렸을 때
4번 핀의 전위는 5V이기때문에 HIGH(1)
풀업 스위치 닫혔을 때
4번 핀의 전위는 0V (GND) 이기때문에 LOW(1)
풀다운 :스위치를 기준으로 GND 쪽에 저항 연결
풀다운 스위치 열렸을 때
4번 핀의 전위는 0V (GND) 이기때문에 LOW(1)
풀다운 스위치 닫혔을 때
4번 핀의 전위는 5V이기때문에 HIGH(1)
우리는 풀다운으로 실습해보자.
실습1. 외부 풀다운 방식으로 Switch를 누르고있으면 점등, 떼면은 소등이 되도록 해보자.
const int led=13;
const int btn=5;
void setup() {
pinMode(13,OUTPUT);
pinMode(btn,INPUT);
}
void loop() {
if(digitalRead(btn)==LOW){
digitalWrite(led,LOW);
}else{
digitalWrite(led,HIGH);
}
}
실습2. 외부 풀다운 방식으로 Switch를 한번 누를 때마다 led상태가 변화하도록 해보자.
int btn=5;
int red=13;
boolean btnCurrent=LOW; //현재 버튼 상태
void setup() {
pinMode(red,OUTPUT);
pinMode(btn,INPUT) ;
}
void loop() {
//현재버튼 읽음
btnCurrent=digitalRead(btn);
if( btnCurrent==HIGH){ // 버튼 누름
if(digitalRead(red)==LOW){ //현재 꺼져있으면
digitalWrite(red,HIGH); //킨다
}else{
digitalWrite(red,LOW); //끈다
}
}
}
실습2의 경우 누를 때마다 바뀌긴 하는데 조금 이상하게 바뀐다면 매우 정상입니다.
한번 누를때마다 여러변 반짝이는 듯한 현상은 바로 채터링때문입니다.
채터링
스위치를 한번 누를 때 디지털신호가 한번에 ON이 되는게 아니라 저 Bounce구간을 거쳐 ON이 된다.
이 Bounce 구간때문에 채터링이 발생. 이를 해결하는 것을 Debouncing이라고 한다.
Debouncing에는 HW방식, SW방식으로 해결할 수 있지만 HW방식으로 해결하기 위해서는 캐패시터가 필요.
결국 SW방식으로 해결해야 한다.
SW방식 디바운싱
1.이전 버튼 상태와 현재 버튼 상태를 저장(초기상태는 모두 LOW)
2. 현재 버튼상태를 읽음 ( 현재버튼상태= digitalRead(스위치핀번호);)
3. 현재 버튼 상태가 이전 버튼상태와 다른 경우 5ms 대기
4. 5ms 후 버튼 상태를 다시 읽어 현재 버튼 상태로 저장
5. 이전 버튼 삳태가 LOW이고 현재 버튼 상태가 HIGH 인 경우 버튼 입력 발생
6. 현재 버튼 상태를 이전 버튼 상태로 대입
7. 2번부터 반복
실습 2_2 . 실습2의 debouncing을 적용해 채터링 현상 제거해서
한번 누를 때마다 스위치 상태가 제대로 변화하도록 해보자.
int btn=5; // switch를 누르면 HIGH, 안누르면 LOW
int red=13;
boolean lastBtn=LOW; //전 버튼 상태
boolean currentBtn=LOW; //현재버튼 상태
#define DEBOUNCING_DELAY 5 //const int debouncingDelay=5;
void setup() {
// put your setup code here, to run once:
pinMode(red,OUTPUT);
pinMode(btn,INPUT);
}
//현재버튼과 전 버튼 비교
//같으면 암것도 안함, 다르면 5ms 기다림
//기다리고 버튼상태를 현재버튼으로 저장, 현재버튼이 HIGH, 전버튼이 LOW이면 버튼 입력 발생 , 현재버튼상태를 이전버튼으로 변경
boolean buttonInputChattering(){
boolean current=digitalRead(btn);
if(current!=lastBtn){ //현재읽은에 전상황이랑 다르면.....
delay(DEBOUNCING_DELAY);
current=digitalRead(btn); //5ms 뒤 다시 읽음
}
return current; //다시 읽은 입력상태 return
}
void loop() {
currentBtn=buttonInputChattering(); //현재 btn은 5ms 후의 읽은 값이 현재 버튼 상태
if(currentBtn==HIGH && lastBtn==LOW){ // 전버튼은 low, 현재버튼 HIGH ==> 버튼 누른것으로 간주
if(digitalRead(red)==HIGH ){
digitalWrite(red,LOW);
}else if(digitalRead(red)==LOW ){
digitalWrite(red,HIGH);
}
}
lastBtn=currentBtn;
}
문제1. 스위치 누를 때마다 3개의 led가 순차적으로 켜지도록. ( 한 번에 한개씩만 켜지게)
int btn=5;
int red=13;
int green=12;
int blue=11;
boolean lastBtn=LOW;
boolean currentBtn=LOW;
#define DEBOUNCING_DELAY 5
void setup() {
// put your setup code here, to run once:
pinMode(red,OUTPUT);
pinMode(green,OUTPUT);
pinMode(blue,OUTPUT);
pinMode(btn,INPUT);
}
boolean buttonInputChattering(){
boolean current=digitalRead(btn);
if(current!=lastBtn){ //현재읽은게 전상황이랑 다르면.....
delay(DEBOUNCING_DELAY);
current=digitalRead(btn); //5ms 뒤 다시 읽음
}
return current; //다시 읽은 입력상태 return
}
void onlyOneLedOn(int led){
if(led==red){
digitalWrite(red,HIGH);
digitalWrite(green,LOW);
digitalWrite(blue,LOW);
}else if(led==green){
digitalWrite(red,LOW);
digitalWrite(green,HIGH);
digitalWrite(blue,LOW);
}else if(led==blue){
digitalWrite(red,LOW);
digitalWrite(green,LOW);
digitalWrite(blue,HIGH);
}else if(led==0){
digitalWrite(red,LOW);
digitalWrite(green,LOW);
digitalWrite(blue,LOW);
}
}
int count=0;
void loop() {
currentBtn=buttonInputChattering(); //현재 btn은 5ms 후의 읽은 값이 현재 버튼 상태
if(currentBtn==HIGH && lastBtn==LOW){ // 전버튼은 low, 현재버튼 HIGH ==> 버튼 누른것으로 간주
count++;
switch (count%4){
case 1 : onlyOneLedOn(red);break;
case 2 : onlyOneLedOn(green);break;
case 3 : onlyOneLedOn(blue);break;
default :onlyOneLedOn(0);break;
}
}
lastBtn=currentBtn;
}