전자회로 실무 / 입력 회로·인터럽트

로터리 인코더(rotary encoder)의 회전·누름을 I²C로 읽습니다

손잡이가 여러 개인 조작판에서 기계식 인코더마다 A·B 접점과 누름 스위치를 호스트 제어기의 입력에 직접 연결하면 핀과 인터럽트 처리가 늘어납니다. 손잡이 가까이에 작은 보조 마이크로컨트롤러(MCU, microcontroller unit)를 두면, 접점 변화는 그곳에서 처리하고 호스트에는 값과 버튼 상태만 전달할 수 있습니다. 이 글에서는 이 구성을 만들 때 회전 방향 판별, I²C 읽기 순서, 초기화와 접점 검증을 어디까지 확인해야 하는지 설명합니다.

A·B 접점의 순서로 방향을 판단합니다

증분형 로터리 인코더(incremental rotary encoder)는 축을 돌릴 때 A와 B 두 접점의 상태가 어긋난 순서로 바뀌는 부품입니다. 두 상태가 변하는 차례를 보면 방향을, 유효한 상태 변화를 세면 이동량을 알 수 있습니다. 손잡이를 누르는 스위치는 회전 접점과 별개의 입력입니다. 아래 그림은 상태가 바뀌는 순서를 보여 주는 개념도이며, 특정 인코더의 시간 간격이나 한 클릭당 펄스 수를 나타내지 않습니다.

A와 B 접점의 상태가 어긋나게 바뀔 때 보조 MCU가 읽을 후보 지점을 차례로 강조합니다. 시간 간격과 클릭당 변화 수는 설명용입니다.
정지 그림 확대
A와 B 접점의 상태가 어긋나게 바뀔 때 보조 MCU가 읽을 후보 지점을 차례로 강조합니다. 시간 간격과 클릭당 변화 수는 설명용입니다. · 그림을 클릭하면 확대됩니다.

예시 구현에서는 인코더 A선의 변화가 발생했을 때 B선의 현재 상태와 앞선 상태를 함께 보고 내부 카운트를 갱신합니다. A선의 모든 에지가 무조건 한 클릭이 되는 방식으로 단순화하면 실제 클릭당 값이 달라질 수 있습니다. 각 접점에는 회로와 MCU의 입력 조건에 맞는 풀업(pull-up)을 마련하고, 부품을 실제로 천천히·빠르게·양쪽 방향으로 돌려 예상한 값의 변화와 누락 여부를 확인합니다.

손잡이 옆의 MCU가 접점을 I²C 레지스터로 바꿉니다

I²C(Inter-Integrated Circuit)는 SDA 데이터선과 SCL 클록선을 여러 장치가 함께 쓰는 통신 버스입니다. 여기서는 손잡이의 A·B·누름 접점이 보조 MCU로 들어가고, 호스트는 SDA·SCL로 보조 MCU의 레지스터를 읽습니다. 레지스터(register)는 통신 상대에게 보여 주는 작은 상태·설정 저장 공간입니다. 호스트는 원시 접점 에지를 직접 처리하지 않아도 되지만, 보조 MCU의 펌웨어가 어떤 에지를 세고 어떤 값으로 변환하는지는 여전히 설계·시험 대상입니다.

회전 A·B와 누름 스위치는 보조 MCU로, 해석된 값과 상태는 SDA·SCL로 호스트로 전달되는 개념 연결입니다. 전원·풀업·전압 조건은 실제 부품에 맞춰 확인합니다.
회전 A·B와 누름 스위치는 보조 MCU로, 해석된 값과 상태는 SDA·SCL로 호스트로 전달되는 개념 연결입니다. 전원·풀업·전압 조건은 실제 부품에 맞춰 확인합니다. · 클릭하면 확대됩니다.

보조 MCU의 전원 범위만으로 버스가 안전하다고 판단하지 않습니다. 호스트와 보조 MCU의 논리 전압, SDA·SCL 풀업 저항이 연결된 전압, 각 핀의 허용 전압을 같은 회로도에서 확인합니다. 여러 보조 MCU를 한 버스에 두면 각 장치의 7비트 주소가 달라야 합니다. 여기서 소개하는 펌웨어 예시의 주소는 0x36이며, 동일한 빌드를 여러 개 연결하면 주소가 충돌합니다. 주소는 펌웨어에서 바꾸고, 실제 버스에 연결된 다른 장치의 주소와 중복되지 않는지 점검합니다.

한 구현은 ATtiny202/212/402/412 계열을 보조 MCU로 사용하고 보드의 사용 전압을 2.7–5 V로 제시합니다. 이 수치는 모든 인코더 모듈의 보증 범위가 아닙니다. 사용하려는 MCU의 정확한 형번과 클록 설정, 버스에 연결한 다른 부품의 허용 전압을 데이터시트로 대조해야 합니다.

읽는 바이트 순서가 곧 인터페이스 규격입니다

이 구현의 상태·설정 영역은 총 10바이트입니다. 16비트 정수는 하위 바이트가 먼저 나오는 리틀 엔디언(little-endian) 순서이고, 값·최소·최대·증분은 부호가 있는 정수로 다룹니다.

바이트 위치내용호스트가 확인할 점
0–1현재 인코더 값, 16비트하위 바이트 다음 상위 바이트를 결합합니다.
2누름 스위치 상태, 8비트0은 놓음, 1은 누름입니다.
3범위를 넘을 때 되감기 옵션, 8비트0은 끝에서 멈춤, 1은 반대 끝으로 이동합니다.
4–5최소값, 16비트호스트가 초기화한 범위와 일치해야 합니다.
6–7최대값, 16비트최소값·증분과 함께 검증합니다.
8–9한 번에 바뀌는 증분, 16비트방향과 부호를 시험합니다.

이 펌웨어는 주소가 맞는 I²C 거래가 시작될 때 읽기·쓰기 위치를 0으로 되돌리고, 바이트마다 다음 위치로 이동합니다. 따라서 현재 값은 시작 위치에서 2바이트를 읽어 조합하고, 스위치까지 읽으려면 3바이트를 한 번에 읽어 앞의 2바이트를 버린 뒤 세 번째를 사용합니다. 일반적인 센서처럼 ‘레지스터 주소 2를 먼저 쓰고 한 바이트를 읽는다’는 절차를 적용하면 안 됩니다. 이 구현에서는 보낸 바이트가 위치 지정 명령이 아니라 설정 데이터로 받아들여질 수 있습니다.

설정할 때는 값 2바이트, 스위치 자리 1바이트, 되감기 옵션 1바이트, 최소·최대·증분 각 2바이트를 정해진 차례로 보냅니다. 스위치 자리는 호스트가 누름을 명령하는 공간으로 생각하지 말고, 전체 설정 형식을 맞추기 위한 자리로 다룹니다. 전원을 켠 직후의 기본 범위와 증분을 임의로 가정하지 말고 호스트가 필요한 설정을 보낸 뒤 읽어서 확인합니다. 전원 재인가와 I²C 오류 뒤에도 이 초기화 순서를 반복할지 정해야 합니다.

접점 바운스와 읽기 시점을 따로 검증합니다

접점 바운스(contact bounce)는 금속 접점이 닫히거나 열릴 때 짧은 시간 동안 여러 번 붙었다 떨어지는 현상입니다. 인코더 접점의 채터링과 손잡이를 누르는 스위치의 바운스는 서로 다른 입력에서 발생합니다. 위 구현의 공개 코드에서 누름 스위치 상태 바이트는 메인 반복문이 핀을 그대로 읽어 갱신합니다. 따라서 이 회로를 쓴다고 해서 누름 상태에 디바운스(debounce, 흔들리는 접점을 안정된 한 번의 입력으로 판정하는 처리)가 자동으로 적용됐다고 말할 수 없습니다. 필요한 경우 호스트나 보조 MCU에서 실제 스위치 특성에 맞는 안정 시간과 판정 규칙을 추가합니다.

호스트가 값을 읽는 동안 손잡이가 움직일 수 있으므로, 한 번의 2바이트 값을 읽은 뒤 급격한 방향 전환과 최대 속도에서 기대한 변화량을 비교합니다. 값과 스위치 상태를 별도 거래로 읽으면 두 결과의 시각이 정확히 같다고 가정하지 않습니다. 같은 거래로 3바이트를 읽더라도 공개 펌웨어가 모든 상태를 고정한 원자적 스냅샷(atomic snapshot)을 보장한다고 단정하지 않습니다. 동시성이 중요한 장치라면 연속 읽기 일치 검사를 하거나 상태 고정 규칙을 펌웨어에 정의해야 합니다.

실제 보드에서는 이 순서로 확인합니다

  1. 인코더의 A·B·공통·누름 핀을 데이터시트와 도통 검사로 식별하고 보조 MCU 입력에 연결합니다. 전원·접지·SDA·SCL·풀업의 전압을 먼저 확인합니다.
  2. 호스트에서 버스 주소를 확인합니다. 여러 손잡이를 쓸 때는 각 펌웨어의 주소를 다르게 정하고 한 개씩 연결해 통신을 검사합니다.
  3. 전원 투입 뒤 값·최소·최대·증분·되감기 옵션을 초기화하고 값을 다시 읽습니다. 이 구현의 읽기 포인터가 거래 시작마다 0으로 돌아간다는 점을 호스트 코드에 반영합니다.
  4. 손잡이를 한 방향으로 천천히 돌린 뒤 반대로 돌립니다. 빠른 회전, 방향 급전환, 최소·최대 끝값과 되감기 동작을 각기 확인합니다. 접점 바운스로 중복되거나 건너뛴 값이 생기는지도 기록합니다.
  5. 누름과 해제를 여러 속도로 반복해 스위치 상태가 얼마나 흔들리는지 측정합니다. 필요한 디바운스 처리는 시험 결과에 맞춰 정합니다. 전원을 껐다 켠 뒤에도 설정값을 다시 보내 예상한 상태로 시작하는지 확인합니다.

이 방식의 핵심은 회전 해석을 손잡이 옆으로 옮기되, 호스트와 보조 MCU 사이의 바이트 순서를 명확한 규격으로 다루는 것입니다. 버스가 응답한다고 해서 회전 누락, 버튼 바운스 또는 전원 복귀 후 설정 상태까지 검증된 것은 아닙니다.