본문 바로가기

로봇 만들기 - AVR/마이크로 마우스

DC Mouse 2. 아두이노 나노로 DC 모터 2개 엔코더 값 입력 받기

마우스를 만들기 위해서는 DC 모터 2개를 제어해야 한다.

이때 모터의 회전 속도/회전 각도를 측정해야하므로 엔코더를 사용해야 하는데 일반적으로 외부 인터럽트를 사용한다.

그러나 아두이노 나노는 외부 인터럽트가 2핀 뿐이므로 PCINT (Pin Change Interrupt)를 사용하기로 했다.

PCINT 는 특정 핀의 상태 변화를 체크하는 것으로 외부 인터럽트와 유사하다.

다만, 외부 인터럽트는 핀의 상태 변화 (Edge : Low -> High or High -> Low) 혹은 상태를 지정할 수 있는데 반해서 PCINT 는 상태가 변할때만 인터럽트가 발생한다.

또한 한 핀당 하나의 ISR(인터럽트 서비스 루틴 : 인터럽트 발생시 수행되는 함수)이 있는 것이 아니라, 여러 개의 핀의 변화가 하나의 ISR을 동작시키게 된다.

따라서, ISR안에서 어떤 핀의 PCINT 인지 판단하고 처리해야 한다.

1. 하드웨어 연결 및 기본 변수 설정

   엔코더가 부착된 DC 기어드 모터를 사용하고 엔코더의 출력을 아두이노 나노의 9~12 핀에 연결한다.

   9~12번 핀이 PCINT 1~4이다.

   volatile은 왠지 모르지만 어딘가에서 썼길래 따라 써봄...

#define LEFT_ENCODER_A 10
#define LEFT_ENCODER_B 9
#define RIGHT_ENCODER_A 11
#define RIGHT_ENCODER_B 12

volatile long leftMotorEncoder = 0; //엔코더의 증감값
int leftEncoderA;   //왼쪽 모터 엔코더 A 신호 상태값
int leftEncoderB;   //B 상태값

volatile long rightMotorEncoder = 0; //엔코더의 증감값 
int rightEncoderA;   
int rightEncoderB;

  

2. 인터럽트 설정

    아두이노의 PCINT 를 따로 설정하는 함수가 있는지 모르겠지만 레지스터를 직접 제어해서 설정함

     PCICR은 PCINT 0번을 사용한다는 뜻이고, PCMSK는 PCINT 핀중 어떤 것을 사용할지 결정하는 것이다.

     나 같은 경우는 DC 모터 엔코더로 1~4번 PCINT, 로봇의 출발과 정지를 위한 택트 스위치를 위해 PCINT 5를 사용함

setup(){

    noInterrupts();
    PCICR |= (1 << PCIE0);
    PCMSK0 |= (1 << PCINT1 | 1 << PCINT2 | 1 << PCINT3 | 1 << PCINT4);  //모터 엔코더
    PCMSK0 |= (1 << PCINT5); // TACT_SWITCH
    interrupts();

}

 

3. 인터럽트 함수 (ISR)

   외부 인터럽트였다면 외부 인터럽트 한 핀당 ISR이 하나겠지만, PCINT 는 PCINT ISR 하나에 핀이 여러개이므로

   핀의 상태를 저장했다가, 실제로 변경된 핀이 무엇인지 보고 어떤 핀인지 결정하고 엔코더 값을 계산해야 한다.

    코드를 보면 아래와 같이 진행된다.

     현재 핀 상태를 읽음 -> 각 핀의 이전 상태와 비교하여 변화가 있는 핀에 따라 엔코더값을 계산 -> 변경된 엔코더 값을 업데이트 

ISR(PCINT0_vect)
{

    // 엔코더 제어
    int leftEncoderATemp = digitalRead(LEFT_ENCODER_A);
    int leftEncoderBTemp = digitalRead(LEFT_ENCODER_B);

    if (leftEncoderA != leftEncoderATemp) {    
        leftMotorEncoder += (leftEncoderATemp == leftEncoderBTemp)?1:-1;    
        leftEncoderA = leftEncoderATemp;
    }
    if (leftEncoderB != leftEncoderBTemp) {
        leftMotorEncoder += (leftEncoderATemp == leftEncoderBTemp)?-1:1;
        leftEncoderB = leftEncoderBTemp;
    }

    int rightEncoderATemp = digitalRead(RIGHT_ENCODER_A);
    int rightEncoderBTemp = digitalRead(RIGHT_ENCODER_B);

    if (rightEncoderA != rightEncoderATemp) {
        rightMotorEncoder += (rightEncoderATemp == rightEncoderBTemp)?1:-1;
        rightEncoderA = rightEncoderATemp;
    }
    if (rightEncoderB != rightEncoderBTemp) {
        rightMotorEncoder += (rightEncoderATemp == rightEncoderBTemp)?-1:1;
        rightEncoderB = rightEncoderBTemp;
    }
}

이렇게 했을때 PCINT를 이용하여 DC 모터의 엔코더를 측정하는데 문제가 없었고,

기본적인 P 제어를 이용한 각도 제어가 가능했음 (모터를 손으로 돌리면 원래 위치로 돌아감!!)

첨부 소스 코드 참조...

DC_Motor_Test_P_Control_Angle.ino
0.00MB