AVR Micro 컨트롤러와 함께 로터리 인코더 사용
-
09-06-2019 - |
문제
AVR 마이크로 컨트롤러에서 로터리 인코더가 제대로 작동하도록 하는 데 문제가 있습니다.인코더는 기계식입니다. ALPS 인코더, 그리고 나는 사용하고 있습니다 Atmega168.
설명
핀 소리를 듣기 위해 외부 인터럽트를 사용해 보았지만 너무 느린 것 같습니다.핀 A가 하이가 되면 인터럽트 절차가 시작된 다음 핀 B가 하이인지 확인합니다.아이디어는 핀 B가 하이인 경우 핀 A가 하이가 된 순간 시계 반대 방향으로 회전한다는 것입니다.핀 B가 로우이면 시계 방향으로 회전합니다.그런데 AVR이 Pin B를 확인하는 데 시간이 너무 오래 걸리는 것 같아서 항상 high로 읽혀집니다.
또한 핀 B나 핀 A가 변경될 때까지 단순히 차단하는 프로그램을 만들어 보았습니다.하지만 인코더가 회전할 때 너무 많은 소음이 발생할 수 있습니다. 이 역시 작동하지 않기 때문입니다.나의 마지막 시도는 마지막 8개 값을 버퍼에 저장하고 값이 낮은 값에서 높은 값으로 이동하는지 확인하는 타이머를 갖는 것이었습니다.이것도 작동하지 않았습니다.
인코더 범위 지정을 시도했는데 첫 번째 핀 변경부터 다른 핀 변경까지 2~4ms를 사용하는 것 같습니다.
해결책
다음과 관련된 웹페이지가 있습니다. 로터리 엔코더 및 사용 방법, 유용할 수도 있습니다.
안타깝게도 추가 정보가 없으면 귀하의 특정 문제를 해결할 수 없습니다.
인코더에 어떤 마이크로컨트롤러 핀이 연결되어 있으며 현재 펄스를 디코딩하는 데 사용하고 있는 코드는 무엇입니까?
좋아, 당신은 몇 가지 다른 문제를 다루고 있습니다. 첫 번째 문제는 이것이 기계식 인코더이므로 스위치 소음(바운스, 채터링)을 처리해야 한다는 것입니다.그만큼 데이터 시트 부품이 튀는 것을 멈추고 잘못된 출력을 생성하는 데 최대 3mS가 걸릴 수 있음을 나타냅니다.
디바운스 루틴을 생성해야 합니다.가장 간단한 방법은 A가 높아지는지 지속적으로 확인하는 것입니다.그렇다면 타이머를 시작하고 3ms 후에 다시 확인하세요.여전히 높으면 B를 확인할 수 있습니다. 높지 않으면 스퓨리어스 펄스를 무시하고 계속해서 A 하이를 찾습니다.B를 확인하면 보고, 3ms 동안 타이머를 시작한 다음 다시 B를 봅니다.두 번 모두 동일했다면 해당 값을 사용할 수 있습니다. 3ms 내에 변경되면 다시 수행해야 합니다(B를 읽고 3ms를 기다린 다음 다시 읽고 일치하는지 확인).
atmega는 느린 클럭 속도를 실행하지 않는 한 이러한 검사가 느리게 진행되는 것에 대해 걱정할 필요가 없을 만큼 충분히 빠릅니다.
기계적 소음을 처리한 후에는 적절한 그레이 코드 루틴을 살펴보고 싶을 것입니다. B가 낮아질 때 A가 높아지면 이를 감소시키지 않으면 따르는 알고리즘이 작동하지 않습니다.일반적으로 사람들은 두 입력의 마지막 값을 저장한 다음 이를 두 입력의 새 값과 비교하고 이를 기반으로 작은 함수를 사용하여 늘리거나 줄입니다.(위에서 표에 대해 언급한 웹사이트에서 "고해상도 판독"이라는 제목을 확인하세요.)두 판독값을 4비트 숫자로 결합하고 간단한 배열을 사용하여 카운터를 늘리거나 줄일지 알려주지만 훨씬 더 발전된 솔루션이 있으며 코드 크기, 속도 또는 코드 유지 관리 용이성에 맞게 최적화됩니다.
다른 팁
아날로그 저역 통과 필터를 추가하면 신호가 크게 향상됩니다.저역 통과 필터를 사용하면 AVR의 코드가 정말 간단해졌습니다.
_________
| |
| Encoder |
|_________|
| | |
| | |
100n | O | 100n
GND O-||-+ GND +-||-O GND
| |
\ /
3K3 / \ 3K3
\ /
| |
VCC O-/\/-+ +-\/\-O VCC
15K | | 15K
| |
O O
A B
아, ASCII 예술의 경이로움 :p
다음은 AVR의 프로그램입니다.AVR의 입력 PORTB에 A와 B를 연결합니다.
#include <avr/io.h>
#define PIN_A (PINB&1)
#define PIN_B ((PINB>>1)&1)
int main(void){
uint8_t st0 = 0;
uint8_t st1 = 0;
uint8_t dir = 0;
uint8_t temp = 0;
uint8_t counter = 0;
DDRD = 0xFF;
DDRB = 0;
while(1){
if(dir == 0){
if(PIN_A & (!PIN_B)){
dir = 2;
}else if(PIN_B & (!PIN_A)){
dir = 4;
}else{
dir = 0;
}
}else if(dir == 2){
if(PIN_A & (!PIN_B)){
dir = 2;
}else if((!PIN_A) & (!PIN_B)){
counter--;
dir = 0;
}else{
dir = 0;
}
}else if(dir == 4){
if(PIN_B & (!PIN_A)){
dir = 4;
}else if((!PIN_A) & (!PIN_B)){
counter++;
dir = 0;
}else{
dir = 0;
}
}else if(PIN_B & PIN_A){
dir = 0;
}
PORTD = ~counter;
}
return 0;
}
이 코드는 인코더를 매우 빠르게 회전시키지 않는 한 작동합니다.그런 다음 한두 단계를 놓칠 수 있지만 인코더를 사용하는 사람은 인코더를 몇 단계 돌렸는지 알 수 없기 때문에 중요하지 않습니다.
속도는 문제가 되지 않습니다.대부분의 모든 기계식 스위치에는 디바운스 루틴이 필요합니다.인터럽트로 이 작업을 수행하려면 인터럽트가 트리거될 때 인터럽트를 끄고 몇 ms 후에 다시 켜는 타이머를 시작하십시오.프로그램을 폴링 없이 유지합니다 >:)
정확히 어떤 문제가 있나요?귀하가 제공한 Farnell 페이지에 링크된 기술 사양에 따라 인코더의 핀을 PIC에 연결할 수 있다고 가정합니다. 그러면 데이터를 읽는 데 문제가 있습니까?인코더에서 데이터를 얻지 못합니까?반환되는 데이터를 해석하는 방법을 모르시나요?
/* into 0 service rutine */
if(CHB)
{
if(flagB)
Count++;
FlagB=0;
}
else
{
if(FlagB)
count--:
FlagB=0:
}
/* into 1 service rutine */
FlagB=1;
/* make this give to you a windows time of 1/4 of T of the encoder resolution
that is in angle term: 360/ (4*resolution)
*/