Pic Мк. Эксперимент 20.1. Энкодер: метод опроса состояний.

Я тут сидел ковырял будущую прошивку для усилителя и понял, что такой процент ошибочного определения направления вращения энкодера меня совсем не устраивает. Поэтому было решено внедрить таки процедуру, основанную на опросе состояний.

Приведу картинки из прошлой статьи:

Если на них внимательно посмотреть, то можно определить, что в зависимости от вращения состояния A и C образуют цифры.

Для одного направления : 0 1 3 2 0 1 3 2 …

Для другого : 0 2 3 1 0 2 3 1 0 …

Таким образом, сравнивая текущее состояние двух линий с предыдущим мы можем определять направление вращеня. Осталось только завести таймер на опрос. Чтобы не было прострелов можно установить время опроса поменьше, я так и поступил – у меня опрос через каждую милисекунду.

Вот что примерно вышло:

#include <htc.h>
#include <stdio.h>
#include "usart.h"

#define _XTAL_FREQ 4000000

#define left RB1                                     // пин энкодера А
#define right RB2                                    // пин энкодера С

volatile unsigned char EncData;                      //глобальная переменная для сохранения текущего состояния линий энкодера
__CONFIG(WDTDIS & UNPROTECT & LVPDIS & HS);

void main()
{
unsigned char OldEncData = 3;                        //сохраням старое значение линий, инициализируем с 3
unsigned char upcount = 0;
unsigned char downcount = 0;

TRISB1 = 1;
TRISB2 = 1;
init_comms();                                       //инициализируем уарт для дебага
OPTION = 0b11010001;                                //настраиваем таймер 0, на 1 мс
TMR0 = 0;
T0IE = 1;
GIE = 1;

while (1) {

if (OldEncData != EncData) {                       //если новое значение отличается от старого
    switch (OldEncData) {
             case 0 : if (EncData == 1) {upcount++; downcount=0; }
                      if (EncData == 2) {downcount++; upcount = 0; }
                      break;
             case 1 : if (EncData == 3) {upcount++; downcount=0; }
                      if (EncData == 0) {downcount++; upcount = 0; }
                      break;
             case 2 : if (EncData == 0) {upcount++; downcount=0; }
                      if (EncData == 3) {downcount++; upcount = 0; }
                      break;
             case 3 : if (EncData == 2) {upcount++; downcount=0; }
                      if (EncData == 1) {downcount++; upcount = 0; }
                      break;
                       }
  OldEncData = EncData;                           //текущее значение = старое значение
}

if (upcount >= 4) {                              //флаг поворота направо
printf("\r\n UP");
upcount = 0;
}
if (downcount >= 4 ) {                          //флаг поворота налево
printf("\r\n Down");
downcount = 0;
}

}

}

interrupt isr() {

if (T0IF) {
TMR0 = 0;
EncData = PORTB & 0b00000110;
EncData >>= 1;
T0IF = 0;
}

}

Сначала я пытался ловить флаг по увеличению переменных upcount или downcount на 1. Однако тестовое сообщение вываливалось 4 раза, за один поворот, естественно я решил ждать пока переменная не будет равна 4.

Результат работы этого кода меня просто поразил. !Ни одного! прострела, все работает идеально, даже на моем старом разболтанном энкодере.

 

Исходники!

Pic Мк. Эксперимент 20.1. Энкодер: метод опроса состояний.: 52 комментария

  1. Пытаюсь сделать отработку энкодера на основании вашего проекта на PIC16F873A. кварц 8МГц. В протеусе худо бедно работает, а в железке нет. Как будто из-за дребезга. Если быстро крутить энкодер вообще реакции ни какой если медленно иногда очень редко срабатывает. Где косяк не пойму. Вот код делаю в MIKROC

    /*
     * Project name:
         ADC_on_LCD (Displaying ADC result on LCD)
     * Copyright:
         (c) Mikroelektronika, 2005.
     * Description:
         This code demonstrates how to use library function ADC_read, and library
         procedures and functions for LCD display (4 bit interface).
     * Test configuration:
         MCU:             PIC16F873A
         Dev.Board:       EasyPIC3
         Oscillator:      HS, 08.0000 MHz
         Ext. Modules:    LCD
         SW:              mikroC v6.0
     * NOTES:
         None.
    */
    #define left RB1                                     // пин энкодера А
    #define right RB2                                    // пин энкодера С
    #include"built_in.h"
    unsigned short temp_E=0,temp1_E=0;
    unsigned char d1,d2,d3,d4,d5,d6, temp=0,temp1=0;
    const char character[] = {0,14,17,17,17,10,27,0};
    
    
    
    unsigned int im,i,i1;
    long tlong,u1,u,o,o1;
    unsigned short j;
    volatile unsigned char EncData;
    char smenu=0,z1=0;
    
    void rr (unsigned char adc_input)
    {
    char k;
    u1 = ADC_read(adc_input);
    tlong = u1 *1000;
     asm {                            //   and fill the upper two bytes manually
        MOVF STACK_2,W
        MOVWF _tlong+2
        MOVF STACK_3,W
        MOVWF _tlong+3
        }
     tlong = tlong &gt;&gt;10;
     tlong =0;
     for(k=0;k&lt;6;k++)
     tlong+= u1;
     tlong/=6;
     return;
     }
    void CustomChar(char pos_row, char pos_char) {
      char i;
        LCD_Custom_Cmd(64);
        for (i = 0; i&lt;=7; i++) LCD_Custom_Chr_Cp(character[i]);
        LCD_Custom_Cmd(LCD_RETURN_HOME);
        LCD_Custom_Chr(pos_row, pos_char, 0);
    }
    void main()
     {
      unsigned char OldEncData = 3;
      unsigned char upcount = 0;
      unsigned char downcount = 0;
    
    
      INTCON = 0b10100000;                        // disable all interrupts
      Lcd_Custom_Config(&amp;PORTB,3,4,5,6,&amp;PORTC,0,0,5);  // Инициализировать LCD на PORTD
      LCD_Custom_Cmd(LCD_CURSOR_OFF);           // send command to LCD (cursor off)
      LCD_Custom_Cmd(LCD_CLEAR);                // send command  to LCD (clear LCD)
    
    
    
    
    
      OPTION_REG  = 0b11010010;          //настраиваем таймер 0, на 1 мс
      ADCON1     = 0x82;                 // configure VDD as Vref, and analog channels
      TRISA      = 0xFF;                 // designate porta as input
      PORTC = 0x06;           // Установка PORTC в 0xFF
      TRISC = 0;              // PORTC - выход
      PORTB=0;
      TRISB  = 0x07;
      TMR0  = 0;
    
    
    
       Pwm_Init(8000);         // Инициализация модуля PWM1
       Pwm_Start();          // запуск PWM
    
    
       while (1) {
    
        rr(0);
        u = tlong ;
        u = u*3;
        d1     = u / 1000;
        if(u&lt;=999)
        LCD_Custom_Chr(1,1,0x20);
        else
        LCD_Custom_Chr(1,1,48+d1);
        d1    = (u / 100) % 10;
        LCD_Custom_Chr(1,2,48+d1);
        LCD_Custom_Chr_CP(&#039;.&#039;);
        d1    = (u / 10) % 10;
        LCD_Custom_Chr_CP(48+d1);
        LCD_Custom_Chr_CP(&#039;V&#039;);
        Delay_ms(1);
    
    
        rr(1);
        im = tlong;
        im = im;
        if(im&lt;=999)
        {
        d2     = im / 1000;
        if(im&lt;=999)
        LCD_Custom_Chr(1,11,0x20);
        else
        LCD_Custom_Chr(1,11,48+d2);
        d2    = (im / 100) % 10;
        if(im&lt;=99)
        LCD_Custom_Chr(1,12,0x20);
        else
        LCD_Custom_Chr(1,12,48+d2);
        d2    = (im / 10) % 10;
        if(im&lt;=9)
        LCD_Custom_Chr(1,13,0x20);
        else
        LCD_Custom_Chr(1,13,48+d2);
        d2    = im % 10;
        LCD_Custom_Chr_CP(48+d2);
        LCD_Custom_Chr_CP(&#039;m&#039;);
        LCD_Custom_Chr_CP(&#039;A&#039;);
        }
        else
        {
        rr(3);
        i = tlong;
        i = i;
        d3     = i / 1000;
        if(i&lt;=999)
        LCD_Custom_Chr(1,11,0x20);
        else
        LCD_Custom_Chr(1,11,48+d3);
        d3    = (i / 100) % 10;
        LCD_Custom_Chr(1,12,48+d3);
        LCD_Custom_Chr_CP(&#039;.&#039;);
        d3    = (i / 10) % 10;
        LCD_Custom_Chr_CP(48+d3);
        d3    = i % 10;
        LCD_Custom_Chr_CP(48+d3);
        LCD_Custom_Chr_CP(&#039;A&#039;);
        }
        Delay_ms(1);
        
        if(im&lt;=999)
        {
        if(im&lt;=0)
        {
        o = 0;
        };
        o = u*100/im;
        }
        else
        {
        if(i&lt;=0)
        o=0;
        else
        o = u*10/i;
        }
        if(o&lt;=9999)
        o=o*1;
        else
        o=0;
        d4     = o / 1000;
        if(o&lt;=999)
        LCD_Custom_Chr(2,1,0x20);
        else
        LCD_Custom_Chr(2,1,48+d4);
        d4    = (o / 100) % 10;
        if(o&lt;=99)
        LCD_Custom_Chr(2,2,0x20);
        else
        LCD_Custom_Chr(2,2,48+d4);
        d4    = (o / 10) % 10;
        LCD_Custom_Chr(2,3,48+d4);
        LCD_Custom_Chr_CP(&#039;.&#039;);
        d4    = o % 10;
        LCD_Custom_Chr_CP(48+d4);
        CustomChar(2,6);
    
        
        if(im&lt;=999)
        o1=u*im/10000;
        else
        o1=u*i/1000;
        d5     = o1 / 1000;
        if(o1&lt;=999)
        LCD_Custom_Chr(2,11,0x20);
        else
        LCD_Custom_Chr(2,11,48+d5);
        d5    = (o1 / 100) % 10;
        if(o1&lt;=99)
        LCD_Custom_Chr(2,12,0x20);
        else
        LCD_Custom_Chr(2,12,48+d5);
        d5    = (o1 / 10) % 10;
        LCD_Custom_Chr(2,13,48+d5);
        LCD_Custom_Chr_CP(&#039;.&#039;);
        d5    = o1 % 10;
        LCD_Custom_Chr_CP(48+d5);
        LCD_Custom_Chr_CP(&#039;W&#039;);
    
        
        //i1=1234;
    
        d6    = (i1 / 100) % 10;
        if(i1&gt;= 1;
    if (INTCON.T0IF) {
    TMR0 = 0;
    EncData = PORTB &amp; 0b00000110;
    EncData &gt;&gt;= 1;
    
    }
    if (OldEncData != EncData) {
    
    switch (OldEncData) {
     case 0 : if (EncData == 1) {upcount++; downcount=0; }
              if (EncData == 2) {downcount++; upcount = 0; }
              break;
     case 1 : if (EncData == 3) {upcount++; downcount=0; }
              if (EncData == 0) {downcount++; upcount = 0; }
              break;
     case 2 : if (EncData == 0) {upcount++; downcount=0; }
              if (EncData == 3) {downcount++; upcount = 0; }
              break;
     case 3 : if (EncData == 2) {upcount++; downcount=0; }
              if (EncData == 1) {downcount++; upcount = 0; }
              break;
     }
    
    OldEncData = EncData;
    }
    /* Конец процедуры обработки вращения энкодера */
    
    if(z1==2)
    {
    if (upcount &gt;= 2) {
      if(temp1==255)
      temp1=255;
      else
      temp1++;
      upcount = 0;
    }
    if (downcount &gt;= 2 ) {
      temp1--;
      if(temp1==255)
      temp1=0;
      downcount = 0;
    }
    }
    if(z1==1)
    {
    if (upcount &gt;= 2) {
      if(temp==255)
      temp=255;
      else
      temp++;
      upcount = 0;
    }
    if (downcount &gt;= 2 ) {
      temp--;
      if(temp==255)
      temp=0;
      downcount = 0;
    }
    }
    
    if (INTCON.INTF) {
        smenu++;
        z1=smenu;
       TMR0 = 0;
       INTCON.INTF=0;
    }
    
    if(smenu&gt;2) {smenu=0;}
    
     switch (smenu)  {
     case 0 : LCD_Custom_Chr(2,9,32);
              break;
     case 1 : j=temp;
              Pwm1_Change_Duty(j); //   установить новую скважность,
              LCD_Custom_Chr(2,9,'u');
              break;
     case 2 : i1=temp1;
    
              LCD_Custom_Chr(2,9,'i');
              break;
     }
    
    
    
    
    
    }
    }//~!

    Ответить

  2. Брр, тяжело разбираться во всем коде, я бы порекомендовал сделать следующее:
    1. Создать проект, в котором будет только энкодер (чтобы я мог помочь желательно на hi-tech, я на микрос пару простых программ только делал) и количество вращений выводилось бы по уарту в терминал, так проще будет и отлаживать проект и мне не надо будет весь код понимать.
    2. Очень желательно найти осциллок или логический анализатор и посмотреть если резко крутануть энкодер, сколько импульсов вылетит, на основании этого прикидывать время опроса энкодера.

    Ответить

  3. Это вольтметр и амперметр. Для измерения тока исполбзовал два канала АЦП один на мА второй на А с автоматическим переключением каналов. Также выводятся мощность и сопротивление нагрузки. Энкодер управляет ШИМ и еще сделал просто чтобы на экране видеть как цифры меняются. переключение по нажатию кнопки. Переписывать все HI-TECH не сильно хорошоя идея для меня. Часть кода уже сделана да и в програмировании я новичок. Объясните пожалуста как получается 1мс по моим расчетам 1мкс OPTION = 0b11010001; //настраиваем таймер 0, на 1 мс.
    Частота кварца 4МГц делитель 001 1:4 получается 1МГц Т=1/100000=0,000001с или 1мкс.
    Для кварца 8МГц я по аналогии выбрал делитель 1:8. Даже в протеусе если поставить мотор-энкодер процесор почти не реагирует на вращения энкодера. Иногда выскочит 1 или 2 а потом начинает уменьшаться хотя направление вращения не меняю. Похоже получается и в железе.

    Ответить

  4. По таймеру: вы не совсем правильно считаете:
    1. Частота кварца 4 МГц, после предделителя 1/4 = 1 МГц ~ 1мкс
    2. Дальше настраиваемый предделитель, который у меня делит на 4 ~ 4 мкс
    3. Прерывание по таймеру TMR0 происходит в тот момент, когда он натикает 256 раз, то есть на момент срабатывания таймера временной промежуток будет равен 256 * 4мкс = 1024 мкс ~ 1 мс

    Подробнее про настройку таймера можно почитать вот тут http://diymicro.ru/pic-mk-eksperiment-3-ispolzovanie-tajmera-tmr0.html

    По коду, повторюсь: сделайте аналог моего проекта, отладите сначала энкодер, а потом внедрите в существующий проект, так отлаживать гораздо проще и мне не надо будет разбираться полностью во всем (если конечно не заработает, но мне кажется что будет работать :))

    PS. Энкодер лучше сразу в железе отлаживать.

    Ответить

  5. Попробовал только оставить процедуру энкодера в MICROC вроде работает но если укрутить быстрее одни пропуски. Попробовал кварц на 16МГц стало почти нормально, но если бывтро крутить энкодер немного пропускает. Решил все в хайтеке попробовать не могу подключить lcd.h. Т.е. пока в программе нет lcd_init(); все компилируется стоит добавить этот файл ступор “HI-TECH C Compiler for PIC10/12/16 MCUs (PRO Mode) V9.71a
    Copyright (C) 2010 Microchip Technology Inc.
    Serial number: HCPICP-654321 (PRO)
    Error [499] ; 0. undefined symbol:
    _init_lcd(bp2.obj) ”
    Хайтек только осваиваю почему не работает хотя init_comms(); проходит. В шапке добавил #include “lcd.h” файлы lcd.h и lcd.c бросил в папку с проектом. В lcd.h только пины на свои поменял.

    Ответить

  6. Если менять кварц и становится нормально – значит у этого энкодера другие временные интервалы => подлкючать к осцилку/логическому анализатору и определять нужный интервал.

    Откуда брался файл lcd.h? Случайно не из сайта у меня содержимое копировалось? Там какой то косяк с этим, надо почему то руками именно этот файл перенабрать, я тоже с таким сталкивался.
    Может какие скрытые символы вставляются или еще что – фиг его знает.

    Ответить

  7. Пробовал с сайта копировать и с проекта кажется тоже брал уже запутался. А с энкодером фиг его знает на другом проекте работал нормально не моем правда. Осцилограф С1-68 есть но однолучевой если он не отсырел в подвале попробую посмотреть какая длительность сигнала получается. Уже давно не включал его.

    Ответить

  8. Так епрст, вы естественно будет ошибка, вы вызываете функцию init_lcd(), которой в природе нет :). В файлах определена функция lcd_init().

    Все нормально компилируется.

    Ответить

  9. Да похоже ступил!?. Дописал вывод на дисплей переменной из функции энкодера и проверил на вшивость. Работает отлично и на 4МГц. Мне от думается, что в MICROC что то с прерываниями пошло не так. Я использовал для ШИМ и АЦП готовые функции, а они все с прерываниями работают вот там наверное что то и не срослось. Ну буду пробовать дальше делать в хайтеке правда это отбросит меня назад пока врублюсь как АЦП, ШИМ замоторить фунции вывода на дисплей переделать ну и много всего тому подобного.

    Ответить

  10. Похоже рано обрадовался как только добавил три канала АЦП и два ШИМа. Вся красота работы энкодера сразу улетучилась. Если быстро крутить не реагирует если медленно с пропусками.

    Ответить

  11. Попробуйте выключить все прерывания во время обработки энкодера, то есть примерно такой алгоритм:

    Прерывание по таймеру TMR0;
    Выключаем все прерывания (GIE = 0);
    Возимся с чемто нужным для нас;
    Обнуляем TMRIF, Включаем прерывания (GIE = 1).

    Ну это первое что на ум приходит. Не получится, давайте опять архив с проектом, но если честно без железа не представляю как смогу помочь.

    PS. У меня в будущих мозгах для усилка, вместе с энкодером заюзаны все таймеры + прерывания по измению состояния пинов и все вроде как работает.

    Ответить

  12. Охохо, что ж вы не хотите поблочно все отлаживать, а нахрапом все…

    Первое, что нужно сделать – избавиться от всего этого шлака в обработчике прерываний, в нем не рекомендуется вызывать другие функции.
    Не выйдет – тогда оставляем один канал АЦП или же один шим, а не все сразу.

    Ответить

  13. Блин, нельзя в протеусе отлаживать такие проекты, это потеря времени, причем уже не только вашего.
    Без обвеса да, в протеусе все вроде как работает, но как только вы нагородили огород, время выполнения как можно заметить увеличилось (строка внизу).
    В любом случае, на данный момент проект имеет смысл отлаживать только в железе!
    Да и еще, зачем постоянно в цикле обновлять какие-то значения на экране и при этом ловить энкодер, какой в этом смысл? Изменять ШИМ? Может лучше сделать это отдельной опцией меню?
    Зашли в нужный пункт, изменили скважность, вернулись в функцию отображения.
    А то ресурсы непонятно куда расходуются. Ну и так далее, много нюансов. Такие проекты надо писать под знакомый, иначе получается такая каша.

    Ответить

  14. а как вам такой код, у меня работает без глюков

    enum { ES0, ES1, ES3, ES2 }; //encoder sequential states
    void pollEncoder(void);
    void Encoder_Init(void);
    static char EncPre; //предыдущее состояние энкодера
    static char EncPpe; //пред-предыдущее состояние энкодера
    char Enc_Scan(void);

    void main()
    {
    Encoder_Init();

    while(1)
    {
    pollEncoder();
    }
    }

    void pollEncoder(void)
    {
    char EncTmp = Enc_Scan(); //сканируем энкодер и запоминаем результат

    if (EncTmp == EncPre) return; //если нет изменений, выход
    __delay_us(255); //антидребезговая задержка для энкодера
    char EncNew = Enc_Scan(); //сканируем энкодер еще раз
    if (EncNew != EncTmp) return; //дребезг не закончился, выход
    EncTmp = EncPpe; //пред-предыдущее состояние
    EncPpe = EncPre; //предыдущее состояние
    EncPre = EncNew; //новое состояние

    if (EncNew == ES2 && EncPpe == ES1 && EncTmp == ES0)
    { volume_down = 1; count=0; return; } //вращение против часовой стрелки
    if (EncNew == ES2 && EncPpe == ES3 && EncTmp == ES0)
    { volume_up = 1; count=0; return; } //вращение по часовой стрелке
    }

    void Encoder_Init(void)
    {
    EncPre = Enc_Scan(); //сканируем энкодер и запоминаем состояние
    EncPpe = EncPre; //запоминаем пред-предыдущее состояние
    }

    char Enc_Scan(void)
    {
    char n = 0;
    if(ENC_A) n |= 0x01; //проверка линии F1
    if(ENC_B) n |= 0x02; //проверка линии F2
    return(n);
    }

    p.s. не много ли в нем воды….. может как почистить

    Ответить

  15. Ну я тоже не большой эксперт, но пару моментов могу подсказать:

    1. Функция Encoder_init на мой взгляд лишняя, т.к. в ней фактически просто присваивание одной переменной.
    2. Ээ, а чего вы добиваетесь выражением EncPre = EncPre? (это тоже самое, что написать 10 = 10).
    3. Функция poll_encoder, вот тут я вообще не понимаю как оно может работать, в ней вы делаете так:

    EncPre = EncNew;

    а потом:

    if (EncNew == ES2 && EncPpe == ES1 && EncTmp == ES0)
    { volume_down = 1; count=0; return; } //вращение против часовой стрелки
    if (EncNew == ES2 && EncPpe == ES3 && EncTmp == ES0)
    { volume_up = 1; count=0; return; } //вращение по часовой стрелке

    То есть фактически EncPre всегда равно EncNew! По идее в эти циклы вообще входа нет, как у вас переменные volume обновляются я не понимаю.

    4. Ну и я бы это делал на прерывании по таймеру, а не макросом __delay_us, т.к. когда код разрастется код может работать не так как вам хочется.

    Ответить

  16. код не мой… там идея такая….. что типа выполняется контроль скорости вращения… вы попробуйте у себя в железе воплотить, у меня срабатывает идеально…

    без Encoder_init я тоже пробовал… работает

    Ответить

  17. на “count=0″ не смотрите… это фича) от маленького счетчика загорания и погасания подстветки экранчика при активности на портах

    Ответить

  18. Как вот это может работать?

    EncPre = EncNew; //новое состояние

    if (EncNew == ES2 && EncPpe == ES1 && EncTmp == ES0)
    { volume_down = 1; count=0; return; } //вращение против часовой стрелки
    if (EncNew == ES2 && EncPpe == ES3 && EncTmp == ES0)
    { volume_up = 1; count=0; return; } //вращение по часовой стрелке

    Сами посудите, EncPre=EncNew, а переменные обновляются только если их значения различны.

    Как вы определяете, что у вас “все работает”? Можно полный текст проекта? И ссылку на первоисточник еще если можно.

    PS. Я не утверждаю, что весь код нерабочий, вращение обрабатывается, но вот переменная таким образом ну никак не может обновляться.

    Ответить

  19. Ептыть только после этого форума понял что здесь есть две переменных EncPpe и EncPre

    Ну нафиг так переменные обзывать :) тогда все логично. Но на самом деле код практически такой же как и мой, за исключением юзания таймера, а так суть одна и та же, реализации разные.

    Ответить

  20. яж говорил 3 разных состояния и переменных)
    форум мутный…. тяжело понять где там файнал версия)

    Ответить

  21. Ну это совсем не прозрачно, я ведь и спросил какой смысл в выражении
    EncPre = EncPre

    а там оказывается EncPpe – жесть в общем, и ведь несколько раз внимательно код перечитывал…

    Ответить

  22. Автору ещё раз РЕСПЕКТ!! Работает даже на TMR2!! Работает ОТЛИЧНО!!!

    Ответить

  23. Попробовал код в железе, все работает отлично. Но не могу понять почему идет отсчет увеличеия или уменьшения переменной только через два щелчка энкодера?!

    Ответить

    sarge Ответил:

    Видимо у вас более быстрый энокдер, уменьшите переменный upcount и downcount до 2, по идее все должно заработать.

    Ответить

    Alexey Ответил:

    Вот код слегка переделанный под себя, для макетки. Энкодер тоже как и у вас с кнопкой кажется на 30 позиций. В четных позициях 11, в нечетных 00. А остальное генерится пока идет переход с позиции на позицию.
    void main()
    {
    while(TRUE)
    {
    pollEncoder();
    }
    }

    char Enc_Scan(void)
    {
    char n = (input_a() & 48);
    n = (n>>4);
    return(n);
    }

    void pollEncoder(void)
    {
    char EncTmp = Enc_Scan(); //сканируем энкодер и запоминаем результат

    if (EncTmp == EncPre) return; //если нет изменений, выход
    delay_us(255); //антидребезговая задержка для энкодера
    char EncNew = Enc_Scan(); //сканируем энкодер еще раз

    if (EncNew != EncTmp) return; //дребезг не закончился, выход
    EncTmp = EncPpe; //пред-предыдущее состояние
    EncPpe = EncPre; //предыдущее состояние
    EncPre = EncNew; //новое состояние

    if (EncNew == 3 && EncPpe == 1 && EncTmp == 0)
    {upcount = 1; } //вращение против часовой стрелки
    if (EncNew == 2 && EncPpe == 0 && EncTmp == 1)
    { downcount = 1; } //вращение по часовой стрелке

    if (upcount) { //
    h++;
    if (h>=63){h=63;} //заплняем 6бит порта с 16F630 и остаемся в максимуме
    output_C(h);
    upcount = 0;
    }
    if (downcount ) {
    h–;
    if (h > 63){h=0;} //Без этой строчки при первом включении крутим в минус а на
    //порту C все вот так 0b00111111
    if (h == 255){h=0;}
    output_C(h);
    downcount = 0;
    }
    }

    Ответить

    sarge Ответил:

    будет нагляднее если код в теги

     

    поместите

    Ответить

  24. Dobroe vremya sutok . hotel razobratsya encoderom . poproboval vashu programmu peredelat na 12f675,
    encoder podkluchil na vhodi GP3 i GP5. perepisal v prerivanie ,eto pravilno ? pochemuto ne hochet rabotat
    interrupt isr() {

    if (T0IF) {
    TMR0 = 0;
    EncData = GPIO & 0b00101000;
    EncData >>= 1;
    T0IF = 0;
    }

    Ответить

    sarge Ответил:

    Я бы сначала проверил отрабатывает ли обычная кнопка на ваших этих пинах.

    Ответить

  25. proboval ; pri vrashenie encodera svelodiodi migayut . v vashem kode neispolsovani left i right ,mojet bit eto prichinoy ne raboti?

    if(!left)GP1=1;
    else GP1=0;
    if(!right)GP0=1;
    else GP0=0;

    Ответить

    sarge Ответил:

    блин только сейчас заметил:

    if (T0IF) {
    TMR0 = 0;
    EncData = GPIO &amp; 0b00101000;
    EncData &gt;&gt;= 1;
    T0IF = 0;
    }
    

    У вас так не прокатит, надо менять эту часть (если принципиально использовать данные пины):

    if (T0IF) {
    TMR0 = 0;
    EncData_temp0 =  (GPIO &amp; 0b00101000)&gt;&gt; 3;
    EncData_temp1 =  (GPIO &amp; 0b00101000)&gt;&gt; 4;
    EncData = EncData_temp0 | EncData_temp1;
    T0IF = 0;
    }
    

    Ответить

  26. dobriy den . mojete obyasnit eti stroki &amp i gt novie peremennie?

    EncData_temp0 = (GPIO & 0b00101000)>> 3;
    EncData_temp1 = (GPIO & 0b00101000)>> 4;

    Ответить

    sarge Ответил:

    Посмотрите сейчас внимательно на свое сообщение и поймете что это за переменные :)

    Ответить

  27. sorry .kak to stranno do otpravleniya tam bili nadpisi ;

    EncData_temp0 = (GPIO& 0b00101000)>> 3;

    OGROMNOE VAM SPASIBO ZARABOTALO , YA NA VIHODI POSTAVIL DVA SVETODIODA , pri vrashenii encodera v odnu storonu zagoryetsya odin svetodiod . a pri vrashenii v drugom napravlenii zagoritsya drugoy. i ya zametil tolko posle 4 shelchka encodera zagoraetsa svetodiodi , rabotaet programma tolko pri sdvige na 4 shelchka .

    Ответить

    sarge Ответил:

    Да у меня тоже было 4 щелчка, там есть специальные переменные для этого в моем коде.

    Ответить

  28. perepisal peremennuyu 4 na 1 , teper srabativaet s odnogo shelchka bez oshibok , blogodaren vam za horoshuyu statyu i za horoshiy sayt dlya novichkov , i za podderjku.

    Ответить

  29. Пытаюсь приладить Ваш код с таймером на pic12f629, пока не получилось, поэтому попробовал тот, что в комментарии без таймера. Работает на половину, т.е. при вращении вправо один светодиод горит, влево – нет. Если менять контакты А и В местами, то работает влево, вправо – нет, т.е. ситуация меняется на обратную. Еще момент: как подключать ножки энкодера? Одну из ног притянул к минусу резистором 1 кОм. На “общий” энкодера подаю плюс, кнопку пока не использую.

    Код ниже:

    #include 
    #pragma config BOREN = OFF, MCLRE = OFF, PWRTE = ON, WDTE = OFF, FOSC = INTRCIO 
    
    #define _XTAL_FREQ 4000000
    #define LEDG GPIO1 
    #define LEDR  GPIO2 
    #define knopka GPIO3  
    #define left GPIO4                                       // пин энкодера А
    #define right GPIO5                                      // пин энкодера B
     
    enum { ES0, ES1, ES3, ES2 }; //encoder sequential states
    void pollEncoder(void);
    void Encoder_Init(void);
    static char EncPre; //предыдущее состояние энкодера
    static char EncPpe; //пред-предыдущее состояние энкодера
    char Enc_Scan(void);
    
    void main()
    {
    Encoder_Init();
    
    TRISIO = 0b111000;
    GPIO = 0;
    CMCON = 0x07;
    LEDR=1; LEDG=0;
    
    while (1) {
     
        pollEncoder();
    }
     
    }
    
    void pollEncoder(void)
    {
    char EncTmp = Enc_Scan(); //сканируем энкодер и запоминаем результат
    
    if (EncTmp == EncPre) 
        return; //если нет изменений, выход
    __delay_us(255); //антидребезговая задержка для энкодера
    char EncNew = Enc_Scan(); //сканируем энкодер еще раз
    if (EncNew != EncTmp) 
        return; //дребезг не закончился, выход
    EncTmp = EncPpe; //пред-предыдущее состояние
    EncPpe = EncPre; //предыдущее состояние
    EncPre = EncNew; //новое состояние
    
    if (EncNew == ES2 &amp;&amp; EncPpe == ES1 &amp;&amp; EncTmp == ES0)
    {
        LEDR=1;LEDG=0;
        return; 
    } //вращение против часовой стрелки
    if (EncNew == ES2 &amp;&amp; EncPpe == ES3 &amp;&amp; EncTmp == ES0)
    { 
        LEDR=0;LEDG=1;
        return; 
    } //вращение по часовой стрелке
    } // end pollEncoder
    
    void Encoder_Init(void)
    {
        EncPre = Enc_Scan(); //сканируем энкодер и запоминаем состояние
        EncPpe = EncPre; //запоминаем пред-предыдущее состояние
    }
    
    char Enc_Scan(void)
    {
        char n = 0;
        if(left) n |= 0x01; //проверка линии F1
        if(right) n |= 0x02; //проверка линии F2
        return(n);
    }
    

    Ответить

    sarge Ответил:

    Для начала не углубляясь в код (тем более что это не мой код), судя по вашему сообщению у вас подключена только одна ножка энкодера, это так?

    Мое подключение описано в прошлой статье – вот картинка http://diymicro.ru/wp-content/uploads/2012/01/wiring.jpg.pagespeed.ce.iwpSdAA2Bk.jpg.

    Ну и с одной ножкой подключенной вряд ли будет правильно работать.

    Ответить

    yeye77 Ответил:

    Да, Вы были правы. Подключил резистор и заработало. Теперь попробую Ваш код, ножки будут подключены на GP4 и GP5.

    Вот этот код мне не совсем понятен, можете объяснить?

    EncData_temp0 = (GPIO & 0b00101000)>> 3;
    EncData_temp1 = (GPIO & 0b00101000)>> 4;

    В моем случае (GP4 и GP5) будет 0b00110000?

    Ответить

    sarge Ответил:

    Да, и сдвиг надо делать >>4 и >>5, в прошлом коде видимо ошибка закралась в комментариях.

    Ответить

    yeye77 Ответил:

    Все работает!

    Спасибо Вам за оперативный отзыв и за замечательное содержание сайта!

    Еще вопрос: стоит ли на кнопку вещать двойной клик? Сами не пробовали, например, один клик вкл/выкл, двойной – меню или что-то другое? И как лучше это реализовать?

    Ответить

    sarge Ответил:

    Не пробовал вроде, но не вижу особых проблем, нужно просто с таймингами хорошенько поиграться, чтобы ложных срабатываний не было.

    Я бы делал наверное как то так:
    Мониторим прерывание – ждем антидребезг + задержка на первое отпускание – проверяем кнопка отпущена? – если да, новый таймер для прерывания второго нажатия, если по истечении времени не была нажата второй раз значит было одинарное нажатие, была нажата, значит двойной клик.

    Ответить

  30. Уточнение. При вращении, допустим, вправо, горит зеленый светодиод. При вращении влево зеленый гаснет, но красный не загорается. Для проверки в начале программы до вращения энкодера красный включил, т.е. ножка работает.

    Ответить

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">