아두이노 로터리 인코더 HC-020K 사용하기
로터리 인코더는 모터 축의 각 위치나 회전 속도를 측정하는 센서입니다. 로터리 인코더를 이용하여 모터 축의 위치나 회전 속도를 측정하게 되면, 이를 바탕으로 모터를 이용한 로보 장치 등의 속도와 방향을 계산하는 용도로 사용할 수 있습니다. 이 장치를 이용하여 아두이노로 만든 자동차의 움직임을 제어하는 프로그램을 만들 예정입니다.
로터리 인코더는 측정할 수 있는 데이터의 종류에 따라 절대 인코더와 상대 인코더로 분류할 수 있습니다. 절대 인코더는 모터 축의 현재 위치를 정확히 수치로 나타낼 수 있는 인코더를 의미합니다. 그에 반하여 상대 인코더는 축의 정확의 위치는 알지 못하지만 현재 위치에서 상대적으로 이동한 양을 측정할 수 있습니다. 또한, 인코더는 측정 방법에 따라 자석을 이용한 인코더, 광학 센서를 사용한 인코더, 전도량을 이용한 인코더 등으로 나눌 수 있습니다.
HC-020K에 대하여
이 프로젝트에 사용에 HC-020K는 광학 인코더입니다. 광학 인코더란 한 쪽에 IR 송신기가 있고 다른 쪽에 IR 수신기가 위치하였으며, 그 사이에는 구멍이 뚤린 디스크가 있는 형태의 인코더를 말합니다. IR 신호가 디스크의 구멍을 통과하면 수신기에 많은 양의 빛이 들어오고, 디스크에 막히면 수신기에 적은 양이 들어온다는 것을 이용하여, 디스크가 회전할 때 HIGH와 LOW 신호를 내도록 설계되어 있습니다.
(출처: aliexpress)
HC-020K에는 5V, GND, OUT 핀이 나와 있습니다. 5V는 아두이노의 5V에, GND는 아두이노의 GND에 연결하여 전력을 공급하고, OUT은 외부로 HIGH, LOW 신호를 내보내는 역할을 합니다.
센서를 설치하는 방법은 아두이노 RC카 만들기 첫번쨰 - 차체 조립하기 편을 참고하세요.
HC-020K 스펙
동작 접압 | 4.5V ~ 5.5V |
측정 주기 | 100 KHz |
디스크 지름 | 24 mm |
인코더 해상도 | 20 라인 |
아두이노 interrupt
interrupt가 뭐지?
인코더의 로직 신호가 바뀔 때 마다 이것을 인지하여 계산을 할 수 있는 방법이 필요합니다. 아두이노는 원래 작동하면서 두가지 프로그램를 동시에 실행할 수 없습니다. 특별한 이벤트가 생겨났을 때 이를 처리하는 매커니즘이 바로 interrupt 입니다.
이러한 interrupt는 사용하는 아두이노 보드에 따라 인터럽트를 지원하는 핀들이 달라지는데요, 대표적인 아두이노 보드들이 사용할 수 있는 핀들은 다음과 같습니다.
아두이노 종류 | 사용할 수 있는 디지털 핀 |
Uno | 2, 3 |
Mega | 2, 3, 18, 19, 20, 21 |
Leonardo | 0, 1, 2, 3, 7 |
interrupt service routine 이란? 특징 및 유의사항
Interrupt service routine(ISR)이란 interrupt가 걸렸을 때 실행되는 함수를 의미합니다. 이러한 함수에는 몇몇 유의사항이 존재하는데요, 가장 중요한 것은 ISR 내용은 최대한 간결히 작성해야 한다는 것입니다. Interrupt는 아두이노에서 실행 중이던 프로그램을 멈추고 잠시 새로운 함수를 실행시키는 과정입니다. 때문에, 원래 프로그램에 최대한 영향을 주지 않기 위해 ISR 속 내용은 짧게 작성하여 빠른 시간 내에 작동하도록 해야 합니다.
다음으로 한 ISR 내에서는 다른 interrupt와 관련되어있는 함수를 사용할 수 있습니다. 예를 들어 가장 대표적인 interrupt에는 타이머 interrupt가 있는데요, 바퀴 회전수를 세는 ISR 안에서는 delay()
등의 타이머와 관련되어있는 함수를 사용할 수 없는 것입니다.
interrupt service routine 사용 방법
그럼 인터럽트에 ISR을 붙이기 위해서는 어떻게 해야 할까요? 답은 attachInterrupt()
함수를 사용하는 것입니다. 아래 형식을 사용하도록 권고하고 있습니다.
attachInterrupt(digitalPinToInterrupt(pin), ISR, mode);
attachInterrupt()
함수는 interrupt 번호, interrupt가 걸리면 실행할 ISR, 그리고 어떨 떄 interrupt를 걸 것인지를 결정하는 모드를 지정해야 합니다.
위에 정리한 것과 같이 아두이노 보드에 따라 핀에 연결된 인터럽트 번호가 다릅니다. 따라서 인터럽트 번호를 직접 적어주는 것 보다는 digitalPinToInterrupt(pin) 함수를 사용하는 것이 더욱 효과적입니다. 이렇게 하면 다른 보드를 사용하더라도 코드를 수정하지 않고도 계속 함수를 바꾸지 않고 사용할 수 있기 때문입니다.
모드는 크게 5가지가 있습니다.
- LOW: 핀이 LOW에 있을 때 interrupt 실행
- HIGH: 핀이 HIGH애 있을 때 interrupt 실행
- CHANGE: 핀이 HIGH에서 LOW로 가거나 LOW에서 HIGH로 갈 때 interrupt 실행
- RISING: 핀이 LOW에서 HIGH로 갈 때 interrupt 실행
- FALLING: 핀이 HIGH에서 LOW로 갈 때 interrupt 실행
회로도
코드
아래 코드는 1초에 한 번씩 지금까지 회전한 디스크 홀의 숫자를 출력하는 프로그램입니다. 아래 코드에서 두 가지 점에 유의하여야 합니다.
첫째, setup()
에서 인터럽트 핀으로 사용할 디지털 필의 모드를 INPUT_PULLUP
으로 지정하였습니다. 이렇게 해야 각 핀이 floating 상태가 되는 것을 방지할 수 있습니다.
pinMode(ENCODER_A, INPUT_PULLUP);
pinMode(ENCODER_B, INPUT_PULLUP);
둘째, ISR 안에서 사용하는 변수의 형을 지정할 때 volatile
을 추가하였습니다.
volatile int encoderA = 0;
volatile int encoderB = 0;
ISR에서 사용하는 변수는 종종 컴파일러의 최적화 등으로 인해 잘못된 값을 가질 수 있게 됩니다. 변수의 형을 지정할 떄 volatile
을 붙이면 컴파일러의 최적화를 피하여 삭제되거나 cache 되는 것을 막을 수 있습니다.
실행결과와 오류 해결
이 코드를 업로드하고, 손으로 바퀴를 한바퀴 굴렸습니다. 정상적이라면 한바퀴가 굴러갔으므로 20이 출력되어야 합니다. 놀랍게도 한바퀴를 돌렸음에도 불구하고 50 이상의 값이 출력되었습니다. 다른 바퀴를 움직여도 마찬가지였습니다. 우리가 사용한 로터리 인코더가 제대로 동작하지 않은 것입니다!
오류의 원인 및 해결 방법
오류의 원인을 찾기 위해 여러 가지 실험을 하였지만 정확한 원인을 찾지 못하였는데, 아래 페이지에서 이러한 오류의 원인을 자세히 알 수 있었습니다. 스페인어로 작성된 페이지인데 구글 번역기로 영어로 번역하면 어느 정도 읽을 수 있습니다.
정리하자면, 언뜻 보기에는 인코더가 내는 신호가 완벽한 디지털 신호로 보이지만, 인코더 일부 회로 설계에 결함이 있어서, 신호가 LOW에서 HIGH로 바뀔 때와 HIGH에서 LOW로 바뀔 때 많은 잡음이 발생하는 것을 알 수 있습니다. 아두이노는 이러한 잡음 중 일부를 로직 신호가 바뀐 것으로 인식하여 인터럽트를 발생하기 때문에 실제보다 더 많은 회전수가 나타나게 된 것입니다.
수정된 회로도
센서의 잡음을 없애기 위해 OUT과 GND 사이에 회로도에 일정량의 전류를 저장하여 잡음을 없에주는 역할을 하는 캐피시터를 추가해줬습니다. 위의 기사와는 달리 0.1uF 세라믹 캐피시터를 사용하였습니다.
실행결과
손으로 바퀴를 움직이면서 적절한 값이 출력되는지를 확인하였는데, 캐피시터를 추가하면 잡음이 사라지면서 정확한 양의 바퀴 회전수를 측정하는 것을 볼 수 있었습니다.