Перейти к содержанию
    

Только вряд ли он выдержит вращения на высокой скорости.

 

В ДШ пишут что 100 RPM (Operating), наверно связанно с тем что контакты будут подвисать при большей частоте вращения. Можно посоветовать порыться на сайте Chip&Dip там может чего и есть.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В ДШ пишут что 100 RPM (Operating), наверно связанно с тем что контакты будут подвисать при большей частоте вращения. Можно посоветовать порыться на сайте Chip&Dip там может чего и есть.

Есть-то есть, но как минмум 700 р за штуку. Хотелось бы дешевле. Но изделие если не разовое, то не высокотиражное - датчики из мышек пойдут. Лишь бы высокой вибрации не было в самодельных крепежных элементах, иначе могут быть помехи в измерениях мгновенной скорости и потеря пройденного пути. А в остальном полностью устраивают: мышек дохлых полно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

я правильно понял: по каждому изменению заполняем некую переменную, состояниями входов,

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

нет.

Периодически принимаем состояние енкодера, сдвигаем в регистр.

Если входы сидят, допустим, на битах 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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

мой вариант. энкодер опрашиваю в прерывании таймера.

 

#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;
}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ух ты... А тема ещй жива... Попробую предложиьть изящный (с моей точки зрения) способ реализации этого алгоритма. Используется с оптическими валкодерами, проблемы с подавлением дребезга механических датчиков в данном месте решал бы только с помощью 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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Добавлю немного в копилку. Знаю, не самый лучший вариант, писалось на скорую руку, но работает.

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Приложу свою первую подпрограмку для работы с Энкодером типа 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 //

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

оооо... заметил, что мой код можно сократить в этом месте

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);}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...