|
|
  |
MEGA+энкодер |
|
|
|
Jun 25 2008, 01:54
|
Участник

Группа: Участник
Сообщений: 55
Регистрация: 17-06-08
Из: Томск
Пользователь №: 38 346

|
Цитата Только вряд ли он выдержит вращения на высокой скорости. В ДШ пишут что 100 RPM (Operating), наверно связанно с тем что контакты будут подвисать при большей частоте вращения. Можно посоветовать порыться на сайте Chip&Dip там может чего и есть.
|
|
|
|
|
Jun 26 2008, 12:46
|
Местный
  
Группа: Участник
Сообщений: 246
Регистрация: 4-12-06
Пользователь №: 23 101

|
Цитата(wired @ Jun 23 2008, 10:39)  я правильно понял: по каждому изменению заполняем некую переменную, состояниями входов, while... { reg = (reg<<1)+A reg = (reg<<1)+B }
соответственно заполняется влево 013 00000111 132 00011110 320 00111000 201 00100001 вправо 023 00001011 231 00101101 310 00110100 102 00010010
отбросив старшие 5 бит reg = (reg & 0x07);
далее сравниваю влево reg = 1 001 reg = 7 111 reg = 6 110 reg = 0 000
вправо reg = 2 010 reg = 3 011 reg = 5 101 reg = 4 100 нет. Периодически принимаем состояние енкодера, сдвигаем в регистр. Если входы сидят, допустим, на битах 6 и 7 порта А, то: encoder: in tmp,pina rol tmp lsr reg rol tmp lsl reg ;reg содержит 4 последних состояния: aabbccdd, aa самое старое, dd самое новое andi reg,$3f ; убрали самое старое cpi reg,$2d ; bbccdd = 10 11 01? влево 231 breq toleft cpi reg,$1e ; bbccdd = 01 11 10? вправо 132 breq toright ret Почему 3 состояния. Было так. Колесо с оптическим датчиком (с гистерезисом, 2 канала, как на мыше) стоит на тросе. Трос стоит, счётчик бежит вперёд. Оказывается, трос мелко дрожит, и один сигнал (иногда!) быстренько меняется тоже. Какие к нему претензии? Построил граф состояний - понял.
Сообщение отредактировал Maik-vs - Jun 26 2008, 12:47
|
|
|
|
|
Jun 26 2008, 14:48
|

Участник

Группа: Участник
Сообщений: 28
Регистрация: 13-06-08
Из: KYIV
Пользователь №: 38 269

|
Цитата(Maik-vs @ Jun 26 2008, 15:46)  нет. Периодически принимаем состояние енкодера, сдвигаем в регистр. Если входы сидят, допустим, на битах 6 и 7 порта А, то:
encoder: in tmp,pina rol tmp lsr reg rol tmp lsl reg ;reg содержит 4 последних состояния: aabbccdd, aa самое старое, dd самое новое andi reg,$3f ; убрали самое старое cpi reg,$2d ; bbccdd = 10 11 01? влево 231 breq toleft cpi reg,$1e ; bbccdd = 01 11 10? вправо 132 breq toright ret Почему 3 состояния. Было так. Колесо с оптическим датчиком (с гистерезисом, 2 канала, как на мыше) стоит на тросе. Трос стоит, счётчик бежит вперёд. Оказывается, трос мелко дрожит, и один сигнал (иногда!) быстренько меняется тоже. Какие к нему претензии? Построил граф состояний - понял. все, "догнал"... в моем варианте отрабатьіваются 4 фазьі т.е. за 1 щелчок в идеале получаю 4 отчсета, что в принципе с практикой согласовьівается вьізьівается из основного цикла.. Код void rd_encoder(void) {int temp = 0; pinstate = (ENC_PIN & 0x06); // pind.1 pind.2 if (state != pinstate) //состояние изменилось pind.1 pind.2 { state = pinstate; //обновили state /* заполняю буфер */ encoder = encoder<<1; encoder = encoder+ENC_A; encoder = encoder<<1; encoder = encoder+ENC_B;
switch((encoder & 0x07)) //смотрим последние 3 бита { case 1:temp = 1; break; case 7:temp = 1; break; case 6:temp = 1; break; case 0:temp = 1; break; case 2:temp = -1; break; case 3:temp = -1; break; case 5:temp = -1; break; case 4:temp = -1; break; default: temp = 0; } твой вариант будет приблизительно таким буквально: Код void rd_encoder(void) {int temp = 0; pinstate = (ENC_PIN & 0x06); // pind.1 pind.2 if (state != pinstate) //состояние изменилось pind.1 pind.2 { state = pinstate; //обновили state /* заполняю буфер */ encoder = encoder<<1; encoder = encoder+ENC_A; encoder = encoder<<1; encoder = encoder+ENC_B;
switch((encoder & 0x3F)) //смотрим последние 6 бит { case 0x2D:temp = 1; break; case 0x1E:temp = -1; break; default: temp = 0; }
Сообщение отредактировал wired - Jun 26 2008, 14:55
|
|
|
|
|
Nov 12 2008, 09:51
|
Участник

Группа: Участник
Сообщений: 17
Регистрация: 24-10-08
Пользователь №: 41 157

|
мой вариант. энкодер опрашиваю в прерывании таймера. Код #define PORT_Enc PORTA #define PIN_Enc PINA #define DDR_Enc DDRA #define Pin1_Enc 1 #define Pin2_Enc 2
#define _0b00000011 3 #define _0b00111111 63 #define _0b00010010 18 #define _0b00100001 33
#define SBI(port, bit) port|= (1<<bit) #define CBI(port, bit) port&= ~(1<<bit)
//подпрограмма инициализации void Init_Encoder(void) { CBI(DDR_Enc, Pin1_Enc); //вход CBI(DDR_Enc, Pin2_Enc); CBI(PORT_Enc, Pin1_Enc);//вкл подтягивающий резистор CBI(PORT_Enc, Pin2_Enc); }
//подпрограмма опроса энкодера /*считывает значения выводов энкодера, если на обоих выводах единицы, то возвращает 0. если текущее состояние равно предыдущему, то возвращает 0. если состояние изменилось, то сдвигает регистр state_enc влево на 2 разряда и записывает 2 разряда текущего состояния. Проверяет получившуюся последовательность. если последовательность соответствует вращению влево - возвращает (-1), вправо - возвращает (1) */ unsigned char Read_Encoder(void) { unsigned char tmp,tmp2; static unsigned char state_enc; //хранит последовательность состояний энкодера tmp=0; if ((PIN_Enc&(1<<Pin1_Enc))!=0) {SBI(tmp,0);} else {CBI(tmp,0);} if ((PIN_Enc&(1<<Pin2_Enc))!=0) {SBI(tmp,1);} else {CBI(tmp,1);}
if (tmp==_0b00000011) {return 0;}
tmp2=(state_enc & _0b00000011); if (tmp==tmp2) {return 0;}
tmp2=state_enc<<2; state_enc=tmp2 | tmp;
tmp2=tmp2 & _0b00111111; if (tmp2==_0b00100001) {return 0x01;} if (tmp2==_0b00010010) {return 0xff;}
return 0; }
|
|
|
|
|
Nov 13 2008, 16:41
|

Профессионал
    
Группа: Участник
Сообщений: 1 620
Регистрация: 22-06-07
Из: Санкт-Петербург, Россия
Пользователь №: 28 634

|
Ух ты... А тема ещй жива... Попробую предложиьть изящный (с моей точки зрения) способ реализации этого алгоритма. Используется с оптическими валкодерами, проблемы с подавлением дребезга механических датчиков в данном месте решал бы только с помощью RC-цепочки и триггера Шмидта. Начальное значение old_val - считанное значение из функции, дающей состояние битов прерываний. Входы прерываний программируются на срабатывание по любому перепаду. Ничего (кроме максимальной скорости вращения) не изменится, если функцию вызывать и из таймерного прерывания. Код static uint8_t old_val;
void spool_encinterrupt(void) { uint8_t new_val = hardware_get_encoder_bits();
// dimensions are: // old_bits new_bits const static signed char v [4][4] = { { +0, /* 00 -> 00 stopped */ -1, /* 00 -> 01 rotate left */ +1, /* 00 -> 10 rotate right */ +0, /* 00 -> 11 invalid combination */ }, { +1, /* 01 -> 00 rotate right */ +0, /* 01 -> 01 stopped */ +0, /* 01 -> 10 invalid combination */ -1, /* 01 -> 11 rotate left */ }, { -1, /* 10 -> 00 rotate left */ +0, /* 10 -> 01 invalid combination */ +0, /* 10 -> 10 stopped */ +1, /* 10 -> 11 rotate right */ }, { +0, /* 11 -> 00 invalid combination */ +1, /* 11 -> 01 rotate right */ -1, /* 11 -> 10 rotate left */ +0, /* 11 -> 11 stopped */ }, };
rotate += v [old_val][new_val];
old_val = new_val; } Код #if defined (CPUSTYLE_ATMEGA128)
ISR(INT4_vect) { spool_encinterrupt(); }
ISR(INT5_vect) { spool_encinterrupt(); }
#elif defined (CPUSTYLE_ATMEGA32)
ISR(INT0_vect) { spool_encinterrupt(); }
ISR(INT1_vect) { spool_encinterrupt(); } #else
#error Undefined processor
#endif Код /* получение накопленного значения прерываний от валкодера. накопитель сбрасывается */ uint_least16_t getRotateHiRes( uint_least32_t * jumpsize, uint_least16_t granulation) { #if ENCODER_HIRES #define BIGJUMPSIZE (10000UL / 4) static const uint8_t velotable [] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, }; #else #define BIGJUMPSIZE (10000UL) static const uint8_t velotable [] = { 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, }; #endif
div_t d, h; uint8_t ticks; uint16_t nrotate, hrotate; cli(); if (tickcount != 0) { ticks = tickcount; tickcount = 0; hrotate = rotate; rotate = 0; } else { ticks = 1; hrotate = 0; } sei();
/* Уменьшение разрешения валкодера в зависимости от установок в меню */ h = div(hrotate, hiresdiv);
cli(); rotate += h.rem; sei();
nrotate = h.quot;
d = div(nrotate + stepcount, ticks); stepcount += d.rem; /* остаток пригодится в следующий раз */
if (d.quot < 0) d.quot = - d.quot; if (d.quot < (sizeof velotable / sizeof velotable [0])) * jumpsize = (uint32_t) granulation * velotable [d.quot]; else if (noaccelerate != 0) * jumpsize = (uint32_t) granulation * velotable [(sizeof velotable / sizeof velotable [0]) - 1]; else * jumpsize = BIGJUMPSIZE;
return nrotate; } Код /* получение "редуцированного" количества прерываний от валкодера. * То что осталось после деления на scale, остается в накопителе */
int getRotateLoRes(void) { int nrotate; div_t d; cli(); nrotate = rotate; rotate = 0; sei();
d = div(nrotate, ROTATE_LORES_DIV);
cli(); rotate += d.rem; sei();
return d.quot; }
void encoder_initialize(void) { rotate = 0; stepcount = 0; tickcount = TICKCOUNT_MAX;
hardware_encoder_initialize();
#if ENCODER_MULTICLICK old_val = hardware_get_encoder_bits(); #endif // ENCODER_MULTICLICK } Проект лежит здесь - http://forum.cqham.ru/viewtopic.php?t=15274 и в аттаче.
Сообщение отредактировал Genadi Zawidowski - Nov 13 2008, 16:46
|
|
|
|
|
Nov 15 2008, 12:41
|
Участник

Группа: Свой
Сообщений: 37
Регистрация: 13-05-07
Из: Minsk, Belarus
Пользователь №: 27 694

|
Добавлю немного в копилку. Знаю, не самый лучший вариант, писалось на скорую руку, но работает. Код void check_encoder(void) { static uint8_t state=0; static uint8_t prevstate=0; static uint8_t prevcount=0; static uint8_t laststate=0; register uint8_t temp; temp=(PINA&0x60)<<1;
if (temp==prevstate) { if (prevcount<4) { prevcount++; } if (prevcount!=3) { return; } } else { prevcount = 0; prevstate = temp; return; }
if (laststate==prevstate) return;
laststate=prevstate;
switch (prevstate&0xc0) { case 0xc0: { if (state==4) { if (value>VALUEMIN) { value--; } } if (state==5) { if (value<VALUEMAX) { value++; } } state = 1; } break; case 0x00: { if ((state==1) || (state==3)) state++; } break; case 0x40: { if (state==1) state=3; if (state==2) state=5; } break; } } -- WBR, Andrew
|
|
|
|
|
Nov 15 2008, 14:14
|

Местный
  
Группа: Свой
Сообщений: 253
Регистрация: 28-12-07
Из: Украина г. Первомайск
Пользователь №: 33 716

|
Приложу свою первую подпрограмку для работы с Энкодером типа PEC-16 :
.def tmp = r16 .equ encod = PB2 .equ enc_2 = PB1
.org INT2addr // прерывание по внешнему сигналу INT2 для энкодера громкости rjmp Encoder // обработчик прерывания от энкодера
.CSEG // сегмент кода //Векторы прерываний: .org 0 // по адресу 0 вектор сброса rjmp RESET // перейти на метку ресет
RESET: in temp,MCUCR ori temp,(1 << ISC10) ;по спаду out MCUCR,temp in temp,GIMSK ori temp,(1 << INT2) ;бит INT2 в GIMSK равен 1 out GIMSK,temp ;разрешаем внешнее прерывание INT2 sei
// тут пишем всякую инициализацию //
// Подпрограмма Encoder: sbi PORTD,7 // контроль на PD7 внешнего прерывания push tmp // сохранение темп in tmp,SREG // читаем статус регистр push tmp // сохраняем его sbis pinb,encod // проверяем наличие прерывания rcall encoder_read // читаем энкодер pop tmp // извлекаем статус регистр out SREG,tmp // восст. SREG pop tmp // восст. temp cbi PORTD,7 // гасим светодиод контроля reti // выходим и разрешаем прерывания
Encoder_read: // чтение энкодера sbis pinb,1 // если установлен бит 1 в порте то пропуск след ком rcall plus // вызываем плюс sbic pinb,1 // если очищен бит 1 в порте то пропуск след ком rcall minus // вызываем минус reti // выход из прерывания
minus: // rcall lcd_sub // reti //
plus: // rcall lcd_add // reti //
|
|
|
|
|
Nov 21 2008, 06:43
|
Участник

Группа: Участник
Сообщений: 17
Регистрация: 24-10-08
Пользователь №: 41 157

|
оооо... заметил, что мой код можно сократить в этом месте Код tmp=0; if ((PIN_Enc&(1<<Pin1_Enc))!=0) {SBI(tmp,0);} else {CBI(tmp,0);} if ((PIN_Enc&(1<<Pin2_Enc))!=0) {SBI(tmp,1);} else {CBI(tmp,1);} и будет то же самое Код tmp=0; if ((PIN_Enc&(1<<Pin1_Enc))!=0) {SBI(tmp,0);} if ((PIN_Enc&(1<<Pin2_Enc))!=0) {SBI(tmp,1);}
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|