Skip to content
irmusy edited this page Oct 8, 2013 · 2 revisions

개요

encoder 예제는 Quadrature Encoder Interface(QEI)를 이용한 증분인코더(incremental encoder) 인터페이싱 예제입니다. 예전에는 증분 인코더를 사용하기 위해서 복잡한 카운터 회로가 필요했었고, 대부분 EPLD로 별도로 꾸며 넣어 사용하곤 했었습니다. 하지만 LM3S8962 MCU에는 이러한 인코더 카운터 회로가 2개 내장되어 있어 별도의 외부 회로 없이 인코더를 바로 MCU에 연결하여 값을 읽을 수 있습니다.

본 예제는 저가형 기계식 증분 인코더를 사용하여 인코더 값을 읽고, 노브 누름 동작 시 인코더 값을 초기화 하는 형태로 구성하였습니다. 인코더 값을 주기적으로 읽어 UART 채널로 전송합니다.

encoder 회로도

회로도는 PDF 파일로 다운로드 받으실 수도 있습니다.

사용된 인코더는 저가형 기계식 인코더로써 광학식 인코더와는 다른 점이 약간 있습니다. 축이 매끄럽게 회전하는 것이 아니라 1회전에 24개의 클릭감이 있습니다. 또한 기계식 스위치와 마찬가지로 채터링 노이즈가 있습니다. 본 예제에서는 간결한 회로 구성을 위해 별도의 디바운싱 회로를 사용하지 않았지만 제대로 사용하기 위해서는 최소한 캐패시터를 사용한 저역통과 필터를 사용하여 스위칭 노이즈를 필터링 해야 합니다.

본 예제에서는 A/B 상만을 이용하고 있습니다. 인코더 모델에 따라 Z상이 있는 경우 Z상은 별도의 GPIO 입력으로 연결하여 인터럽트로 읽어 목적에 맞게 처리할 수 있습니다.

보다 정확한 측정을 위해서는 광학식 인코더를 사용해야 하며, 인코더 출력단 회로에 따라 MCU와의 연결 회로가 달라질 수 있습니다. 많이 사용되는 autonics사의 광학식 인코더의 경우 토템폴, NPN 오픈컬렉터, 전압출력 출력단 형태에 5V 전원이면 별도의 회로 없이 MCU와 바로 연결해서 사용할 수 있습니다. Autonics 인코더와의 연결 회로도를 참조하시기 바랍니다. 라인드라이브 타입의 경우 제대로 사용하기 위해서는 별도의 리시버가 필요하지만 간단하게 점검하는 용도라면 A, B, Z상만 사용하고 각각의 역상 신호선은 연결하지 않으면 됩니다.

인코더는 바퀴 등에 많이 사용되다 보니 마찰에 의해 정전기가 많이 발생하는 곳입니다. 인코더와 MCU를 바로 연결하게 되면 정전기에 의해 MCU가 파손되는 경우가 많습니다. 안정적으로 사용하기 위해서는 정전기로 부터 MCU를 보호할 수 있는 회로가 필요합니다. 일반적으로 바리스터나 TVS 다이오드등을 많이 사용합니다.

관련 Peripheral

  • SysCtl
  • QEI
  • SysTick
  • UART(llio)
  • GPIO

소스 살펴보기

46 line:

static void SysTickIntHandler(void);
void SW_IntHandler(void);

SysTick 타이머 인터럽트 핸들러와 GPIO 입력 인터럽트 핸들러 원형을 선언합니다. SysTick 타이머는 주기적으로 인코더 값을 읽어 UART로 출력하기 위해 사용하며, GPIO 입력 인터럽트는 인코더 노브의 푸쉬 스위치 입력을 받아들이기 위해 사용합니다.

79 line:

    //
    // Set up QEI peripheral
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_QEI);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC);

    GPIOPinTypeQEI(GPIO_PORTC_BASE, GPIO_PIN_4 | GPIO_PIN_6);
    QEIConfigure(QEI0_BASE, (QEI_CONFIG_CAPTURE_A_B | QEI_CONFIG_NO_RESET |
                QEI_CONFIG_QUADRATURE | QEI_CONFIG_NO_SWAP), 0xffffffff);
    QEIVelocityConfigure(QEI0_BASE, QEI_VELDIV_1, SysCtlClockGet() / 10);

    QEIEnable(QEI0_BASE);
    QEIVelocityEnable(QEI0_BASE);

QEI 장치를 enable하고 초기화합니다.

다른 장치와 마찬가지로 **SysCtlPeripheralEnable()**을 이용해 해당 장치를 enable시키는 것부터 시작합니다. 그리고 PC4, PC6 핀을 QEI용으로 지정합니다. PC4는 QEI0의 A상 입력핀이며, PC6은 QEI0의 B상 입력핀입니다.

GPIOPinTypeQEI() 함수 내부를 살펴보면 해당 핀에 weak pull-up을 설정하는 것을 볼 수 있습니다. 이 기능 덕분에 기계식 인코더를 전원과 연결할 필요 없이 바로 연결해서 사용할 수 있게 됩니다.

**QEIConfigure()**함수를 사용해 QEI 장치를 설정하고, **QEIVelocity()**함수로 인코더 속도 파라메터를 설정합니다. 속도 파라메터는 일반적인 용도에서는 사용할 일이 거의 없습니다.

마지막으로 QEI 장치를 enable 시켜주면 됩니다.

95 line:

    //
    // Set up GPIO for encoder push switch
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC);
    GPIOPinTypeGPIOInput(GPIO_PORTC_BASE, GPIO_PIN_5);
    GPIOPadConfigSet(GPIO_PORTC_BASE, GPIO_PIN_5, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);
    GPIOIntTypeSet(GPIO_PORTC_BASE, GPIO_PIN_5, GPIO_FALLING_EDGE);
    GPIOPortIntRegister(GPIO_PORTC_BASE, SW_IntHandler);
    GPIOPinIntEnable(GPIO_PORTC_BASE, GPIO_PIN_5);

인코더 노브에 내장된 푸쉬 스위치 입력을 받아들이기 위해 GPIO 디지털 입력과 인터럽트를 설정합니다. 본 예제에서는 노브 스위치가 눌려졌을 때 메시지를 출력하고 인코더 카운터를 0으로 초기화 하는 동작을 하고 있습니다.

108 line:

    //
    // Set up the period for the SysTick timer.
    //
    SysTickPeriodSet(SysCtlClockGet() / 10);   // 10Hz SysTick timer

    //
    // Enable the SysTick Interrupt.
    //
    SysTickIntEnable();
    SysTickIntRegister(SysTickIntHandler);

    //
    // Enable SysTick.
    //
    SysTickEnable();

10Hz 주기로 SysTick 타이머를 설정합니다. 초당 10번씩 인코더 값을 읽어 그 결과를 UART로 출력하기 위해 SysTick 타이머를 사용합니다.

133 line:

        SysCtlSleep();              // sleep here till interrupt

인터럽트가 발생할 때 까지 이곳에서 sleep 하며 대기합니다. 본 예제에는 SysTick 타이머 인터럽트와 GPIO 입력 인터럽트 두 가지가 사용됩니다. 두 인터럽트 중 어느 것이라도 발생하면 sleep에서 깨어납니다.

135 line:

        LED_TOGGLE();

        dir = QEIDirectionGet(QEI0_BASE);
        vel = QEIVelocityGet(QEI0_BASE);
        pos = QEIPositionGet(QEI0_BASE);
        printf("%d,%d,%d\r\n", dir, vel, pos);

인터럽트가 발생하면 sleep에서 깨어나 LED를 토글하고 현재 인코더의 회전 방향(정회전/역회전), 회전 속도(시간 주기당 펄스 수), 인코더 카운터 값을 읽어 UART로 출력합니다. UART로의 출력에는 llio가 사용되었습니다.

145 line:

void SysTickIntHandler(void)
{

}

10Hz 주기로 설정된 SysTick 타이머 인터럽트 핸들러입니다. 별다른 하는 일이 없습니다. 이 예제에서는 10Hz 주기로 sleep에서 깨우기 위해 SysTick 타이머 인터럽트를 사용하고 있습니다.

150 line:

void SW_IntHandler(void)
{
    GPIOPinIntClear(GPIO_PORTC_BASE, GPIO_PIN_5);
    printf("Switch PUSH\r\n");

    QEIPositionSet(QEI0_BASE, 0);

}

인코더 노브의 누름 스위치 입력에 대한 인터럽트 핸들러입니다. 노브가 눌려졌을 때 인터럽트 flag를 clear시키고 스위치가 눌려졌음을 UART 메시지로 출력한 다음 인코더 카운터 값을 0으로 초기화합니다.

실행 방법 안내

  1. 제안된 회로도와 같이 인코더를 myCortex-LM8962 보드와 연결합니다. 다른 인코더의 경우 별도의 회로가 필요할 수 있습니다.
  2. 펌웨어를 빌드하여 다운로드한 후 리셋 스위치를 1회 누릅니다.
  3. PC에서 ComPortMaster를 시작하고 Stellaris-JTAG의 가상 시리얼 포트를 open합니다.
  4. 초당 10줄의 메시지가 출력됨을 확인합니다.
  5. 인코더 노브를 손으로 회전시키면서 인코더 방향, 속도, 위치 값이 변하는 것을 확인합니다.
  6. 인코더 노브를 눌렀을 때 "Switch PUSH"메시지가 출력되며 동시에 위치 값이 0으로 초기화 되는 것을 확인합니다.
  7. 본 예제에서 사용한 기계식 인코더의 경우 별도의 디바운싱 회로가 없다면 1 클릭 회전에 펄스가 1보다 더 많이 증가할 수 있습니다. 이는 기계적인 채터링 노이즈 때문입니다.

Clone this wiki locally