본문 바로가기

avr 로터리 엔코더 사용하기 05

반응형

엔코더 모터를 사용하기 위해 로터리 엔코더를 사용해봤다.

http://blog.daum.net/_blog/BlogTypeView.do?blogid=04jfq&articleno=1906598


http://www.dx.com/p/bonatech-360-rotary-encoder-module-for-arduino-black-310402


내가 사용하는 Rotary Encoder이다.


보면은 CLK DT SW + GND라고 되어있다.


여기서 +와 GND는 전원핀

SW는 버튼 신호선

그리고 CLK랑 DT가 로터리 엔코더 펄스 핀이다.


첫 번째 사진이 로터리 엔코더를 돌렸을 때

발생하는 펄스 인데

각각 CLK랑 DT에서 발생된다.


내가 사용하는 로터리 엔코더는 1스탭당 CLK랑 DT펄스 1개씩 생성된다.



그래서 아두이노 우노에 달려있는 atmega328P-PU의 인터럽트 기능을 이용할 것이다.


회로를 편하게 하기 위해서 아래와 같이 회로를 구성했다.


위엄한 방법이지만 잠시동안만 사용할 것이라 문제는 없어보인다.


GND - GND

+ - D13 (PB5)

SW - D12 (PB4)

DT - D11 (PB3)

CLK - D10 (PB2)번에 연결했다.


atmega382p pu데이터 시트를 보면

PB4는 PCINT4

PB3는 PCINT3

PB2는 PCINT2라고 써있는데


이것은 PB4:2핀들이 Pin Change Interrupt기능이 가능하다는 소리이다.


이 사진은 atmega328에 있는 interrupt들인데


PCINT0 ~ 7까지는 4번째 인터럽트인 PCINT0을 이용한다.


위의 사진은 Pin Change Interrupt Control Register(PCICR)이다.


세 번째를 해석해보면

Bit 0 - PCIE: Pin Change Interrupt Enable 0

PCIE0 비트가 세트(1)로 되어있고 상태 레지스터(SREG)에 있는 I 비트가 세트(1)로 되어있다면,

pin change interrupt 0이 사용 가능해진다. 사용 가능한 PCINT7 ~ 0사이의 어떤 핀들의 상태 변화가

인터럽트를 발생시킬것이다. Pin Change Request에 해당하는 인터럽트는 PCI0인터럽트 벡터로 부터 실행된다.

PCINT7 ~ 0핀들은 PCMSK0레지스터에 의해 각각 사용 가능해진다.


여기서 SREG의 I비트는 sei();로 세트 시킬 수 있다.

그리고 PCICR레지스터의 PCIE0 비트를 세트(1)시켜야 하므로

int main(void)의 첫번째 줄에 PCICR |= (1 << PCIE0);를 추가하자


 그리고 위의 설명으로 보면 PCMSK0레지스터도 설정해야한다.


위의 사진은 PCMSK0에 관한 데이터 시트인데

해석하면

Bits 7 ~ 0 - PCINTn: Pin Change Enable Mask [n = 7 ~ 0]

각각의 PCINT 7 ~ 0 비트들은 pin change interrup가 해당하는 입/출력 핀에의해 사용 가능하게 되는지를

선택한다.

만약 PCINT7 ~ 0이 세트(1)되고 PCICR에 있는 PCIE0 비트가 세트(1)되어있다면,

pin change interrupt는 해당하는 입/출력 핀에의해  사용 가능해진다.

만약 PCINT7 ~ 0가 클리어(0)으로 되어있으면, 해당하는 입/출력 핀에 대한 pin change interrupt는 사용못한다.



쉽게 말하자면 우리는 PCINT4 ~ 2를 사용할 것이므로

PCINT4 ~ 2비트들을 세트(1)면 된다는 뜻이다.


아까 작성한 PCICR 밑에다가

PCMSK0 |= (1 << PCINT4) | (1 << PCINT3) | (1 << PCINT2);를 추가하자


그리고 우리는 PB4 ~ 2번 핀들을 입력으로 사용할 것이므로

DDRB &= ~((1 << PB4) | (1 << PB3) | (1 << PB2));를 추가한 다음


D13(PB5)가 +에 연결되어 있기 때문에

DDRB |= (1 << PB5);

PORTB |= (1 << PB5);도 추가하자


그리고 마지막으로 sei();를 추가하면 준비는 끝난다.(sei는 모든 인터럽트를 사용 가능하게 하는것)



그런 다음 펄스의 수를 카운팅 해서 회전수를 측정하기 때문에

volatile int pulse = 0;으로 pulse라는 변수를 만들어두자

그리고 추가적으로 volatile pA, pB;도 만들어두자


우리가 사용할 인터럽트는 PCINT0이므로

ISR(PCINT0_vect) {

}라고 만들어 두고


PCINT7 ~ 0사이의 어떠한 변화가 있으면 PCINT0인터럽트가 발생하여

위의 구문 안의 것들이 실행된다.


일단 clk(pb2)와 dt(pb3)의 값을 알아야 하므로 PINB를

따로 data라는 변수에 담았다.

int data = PINB;


그런다음 아까 선언한 pA는 clk의 전 상태이고

만약 전 clk상태와 지금의 clk상태가 다르다면 이라는 if 문을 만들어준다.


if(pA != (data & (1 << PINB2))) {

그리고 여기서 만약 clk가 0이라는 if문을 만들어준다.

if(!(data & (1 << PINB2))) {

여기서 2가지로 나뉘는데 

dt값도 0일때와 아닐 때로 나뉜다.


따라서 

if(!(data & (1 << PINB3)))와

else 를 추가하고 

각각에 pulse--;

pulse+;를 추가하자

그러면

1
2
3
4
5
6
if(pA != (data & (1 << PINB2))) {
    if(!(data & (1 << PINB2))) {
        if(!(data & (1 << PINB3))) pulse--;
        else pulse++;
    }
}
cs

이런 형식일 것이다.
그리고 }바로 위에다가 PINB2가 1일 때 if문을 만들고 내용은 반대로 한다.


1
2
3
4
5
6
7
8
9
if(pA != (data & (1 << PINB2))) {
    if(!(data & (1 << PINB2))) {
        if(!(data & (1 << PINB3))) pulse--;
        else pulse++;
    } else {
        if(!(data & (1 << PINB3))) pulse++;
        else pulse--;
    }
}
cs
또 이것을 PINB3의 상황에서의 if문을 만들고 부호도 바꿔주자

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
     if(!(data & (1 << PINB2))) {
        if(!(data & (1 << PINB3))) pulse--;
        else pulse++;
    } else {
        if(!(data & (1 << PINB3))) pulse++;
        else pulse--;
    }
}
 
if(pB != (data & (1 << PINB3))) {
    if(!data & (1 << PINB3))) {
        if(!(data & (1 << PINB2))) pulse++;
        else pulse--;
    } else {
        if(!(data & (1 << PINB2))) pulse--;
        else pulse++;
    }
cs

그리고 마지막에 pA랑 pB을 갱신하주는 코드를 추가해주면 끝난다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
ISR(PCINT0_vect) {
  data = PINB;
  if (pA != (data & (1 << PINB2))) {
    if (!(data & (1 << PINB2))) {
      if (!(data & (1 << PINB3))) pulse--;
      else pulse++;
    } else {
      if (!(data & (1 << PINB3))) pulse++;
      else pulse--;
    }
  }
  if (pB != (data & (1 << PINB3))) {
    if (!(data & (1 << PINB3))) {
      if (!(data & (1 << PINB2))) pulse++;
      else pulse--;
    } else {
      if (!(PINB & (1 << PINB2))) pulse--;
      else pulse++;
    }
  }
  pB = data & (1 << PINB3);
  pA = data & (1 << PINB2);
  button = (data & (1 << PINB4)) >> PINB4;
}
cs

button은 버튼 상태도 알고싶었기에 추가했다.

그런다음 pulse값을 보면 1스탭당 4씩 증가하고 0.5스탭이면 2

0.25스탭이면 1씩 증가하는 것을 볼 수 있다.

(나는 아두이노 우노로 했기 때문에 Serial.println(pulse);로 확인했다)


pin_change_interrupt.zip




질문은 댓글에 남겨주면 빠른 시간 안에 답변을 달것이다.

(비공개 말고 공개로 하면 다른 사람도 알아 가는 것이 있기 때문에 질문은 가능한 공개로 설정)

반응형