Конфликт u8g.nextPage() и прерывания по таймеру

Тема в разделе "Arduino & Shields", создана пользователем Inversus, 26 апр 2016.

  1. Inversus

    Inversus Нуб

    Всем привет !

    Использую для вывода на дисплей библиотеку u8g. Понадобилось добавить в программу обработку прерывания по таймеру. Обнаружился конфликт с методом u8g.nextPage() - при её вызове прерывания перестают обрабатываться. Отдельно вывод на дисплей и обработка прерываний работают. Пробовал использовать разные таймеры - результат один. Кто знает почему так происходит и как их можно подружить ? Исходник прилагается.

    Код (C++):
    #include "U8glib.h"
    #include "stdlib.h"

    U8GLIB_ST7920_128X64_1X u8g(12, 11, 10);  // SPI Com: SCK = en = 18, MOSI = rw = 16, CS = di = 17

    const int enc_A = 4;  // pin 12
    const int enc_B = 5;  // pin 11

    int i=0;

    void draw(int p) {
      u8g.setColorIndex(1);
      u8g.setFont(u8g_font_courB18);
      static char *s = new char[4];
      s = itoa(p,s,10);

      u8g.firstPage();
      do {
        u8g.drawStr(39, 40, s);
      } while( u8g.nextPage() );
    }

    void Ctrl() {
      bool Af;
      bool Bf;
      static bool pf = 0;

        Af = digitalRead(enc_A);    // Read encoder pins
        Bf = digitalRead(enc_B);
        if((!Af) && (pf)) {
          // A has gone from high to low
          if(Bf) {
            i++;
          }
          else {
            i--;
          }
        }
        pf = Af;     // Store value of A for next time  
    }

    SIGNAL(TIMER0_COMPA_vect) {
      Ctrl();
      draw(i);
    }

    void setup(void) {
      OCR0A = 0xAF;
      TIMSK0 |= _BV(OCIE0A);

      if ( u8g.getMode() == U8G_MODE_R3G3B2 ) {
        u8g.setColorIndex(255);     // white
      }
      else if ( u8g.getMode() == U8G_MODE_GRAY2BIT ) {
        u8g.setColorIndex(3);         // max intensity
      }
      else if ( u8g.getMode() == U8G_MODE_BW ) {
        u8g.setColorIndex(1);         // pixel on
      }

      pinMode(enc_A, INPUT);  //энкодер к1
      pinMode(enc_B, INPUT);  //энкодер к2

      attachInterrupt(1, Ctrl, CHANGE);
    }

    void loop() {
    }
     
  2. AlexU

    AlexU Гуру

    В обработчике прерывания 'TIMER0_COMPA_vect' вызывается функция 'draw()', которая занимается выводом на дисплей, -- это не очень хорошо. Я бы даже сказал, что очень плохо. Может стоит функцию 'draw()' перенести внутрь функции 'loop()'?
    Второе, функция 'ctrl()', которая "читает" енкодер, является обработчиком прерывания 'INT1' (изменения уровня на пине 3) и одновременно вызывается из обработчика прерывания 'TIMER0_COMPA_vect' -- здесь явно, что-то перемудрили...
    И переменную 'i' объявите как:
    Код (C++):
    volatile int i=0;
    , а то компилятор может немного "начудить".

    PS: информации мало предоставили -- вероятность получить реальную помощь стремиться к нулю.
     
    Inversus нравится это.
  3. Inversus

    Inversus Нуб

    Да, действительно перемудрил. Полагаю Draw в обработчике плохая идея из за большого времени выполнения ?
    Это не исходный пример, просто хотел продемонстрировать задуманное, неудачно. Идея изначально состоит в написании меню с управлением по валкодеру. В меню будут числовые значения. Приращение этих значений прогрессивное - тем больше, чем быстрее крутиться валкодер. Отсюда необходимость обработчика прерываний по таймеру для подсчета временных интервалов между импульсами валкодера. Вот код на данный момент (без самого меню), он выводит постоянно нарастающую до пятиста переменную на дисплей, которая сбрасывается в ноль импульсами с валкодера, на одном выходе которого висит обработчик.

    Код (C++):
    #include "U8glib.h"
    #include "stdlib.h"

    U8GLIB_ST7920_128X64_1X u8g(12, 11, 10);  // SPI Com: SCK = en = 18, MOSI = rw = 16, CS = di = 17

    const int enc_A = 4;
    const int enc_B = 3;

    volatile int i=0, pause=0;

    void draw(int p) {
      u8g.setColorIndex(1);
      u8g.setFont(u8g_font_courB18);
      static char *s = new char[4];
      s = itoa(p,s,10);

      u8g.firstPage();
      do {
        u8g.drawStr(39, 40, s);
      } while( u8g.nextPage() );
    }

    void Ctrl() {
      bool Af;
      bool Bf;
      static bool pf = 0;

        Af = digitalRead(enc_A);    // Read encoder pins
        Bf = digitalRead(enc_B);
        if((!Af) && (pf)) {
          // A has gone from high to low
          if(Bf) {
            i++;
          }
          else {
            i--;
          }
        }
        pf = Af;     // Store value of A for next time  

        pause = 0;
    }

    SIGNAL(TIMER0_COMPA_vect) {
      if (pause < 500) pause++;
    }

    void setup(void) {
      OCR0A = 0xAF;
      TIMSK0 |= _BV(OCIE0A);

      if ( u8g.getMode() == U8G_MODE_R3G3B2 ) {
        u8g.setColorIndex(255);     // white
      }
      else if ( u8g.getMode() == U8G_MODE_GRAY2BIT ) {
        u8g.setColorIndex(3);         // max intensity
      }
      else if ( u8g.getMode() == U8G_MODE_BW ) {
        u8g.setColorIndex(1);         // pixel on
      }

      pinMode(enc_A, INPUT);  //энкодер к1
      pinMode(enc_B, INPUT);  //энкодер к2

      attachInterrupt(1, Ctrl, CHANGE);
    }

    void loop() {
      draw(pause);
    }
    Пытался обрабатывать валкодер по таймеру - снова выходит ерунда, импульсы проходят редко и хаотично, видимо из за задержек на выполнение остального кода. Я полагаю в крупной программе обработка кнопок по таймеру плохое решение ? Была идея обрабатывать валкодер и все кнопки через один сигнал внешнего исключения - схемно подвести их все например на D3(nano) через диоды, и по общему исключению уже считывать "родные" пины кнопок, но из за дребезга, который не удалось до конца устранить кондёрами, такая схема работает очень глючно. Может есть готовое приемлемое решение в таком духе ?
     
  4. AlexU

    AlexU Гуру

    Для этого не обязательно мудрить с таймером. Можно воспользоваться функциями 'millis()' или 'micros()'.

    Обработку кнопок лучше делать с помощью внешних прерываний (внешние прерывания поддерживают все сигнальные пины).

    Аппаратно -> низкочастотные RC-фильтры + триггер Шмитта.
    Программно -> использование функции 'millis()'.

    И повторюсь -- слишком мало информации, например, как устроен валкодер и как он подключен?
     
  5. Inversus

    Inversus Нуб

    Валкодер механический маде ин чина (EC11)
    [​IMG]
    топорный но зато дешёвый :cool:
    Подключение стандартное:
    [​IMG]
    А вот про прерывания не понял, цитирую "Большинство контроллеров Arduino умеют обрабатывать до двух внешних прерываний". На одном у меня висит ответственный датчик, и на все остальное остаётся только одно, вроде так ?
     
    Последнее редактирование: 26 апр 2016
  6. AlexU

    AlexU Гуру

    Откуда цитата?
    Повторяю, в AVR, которые используются в Arduino, все сигнальные пины поддерживают внешние прерывания. Те два, о которых Вы говорите, более функциональные -- могут срабатывать по фронту, изменению и т.п. -- остальные только по изменению состояния. Ищите информацию про прерывания PCINTx.

    PS: Arduino это семейство плат, в некоторых из которых используются микроконтроллеры AVR.
     
  7. Inversus

    Inversus Нуб

    Цитата отсюда: http://arduino.ru/Reference/AttachInterrupt
    Да и тут то же самое написали http://arduino.ua/ru/prog/AttachInterrupt
    Странно почему в ардуино не реализовали штатным способом остальные внешние прерывания.

    Попробовал через PCINT - реакция контроллера на вращение валкодера - перезагрузка :confused:
    Код (C++):
    #include "U8glib.h"
    #include "stdlib.h"

    U8GLIB_ST7920_128X64_1X u8g(12, 11, 10);  // SPI Com: SCK = en = 18, MOSI = rw = 16, CS = di = 17

    const int enc_A = 4;
    const int enc_B = 3;

    volatile int i=0, pause=0;

    void draw(int p) {
      u8g.setColorIndex(1);
      u8g.setFont(u8g_font_courB18);
      static char *s = new char[4];
      s = itoa(p,s,10);

      u8g.firstPage();
      do {
        u8g.drawStr(39, 40, s);
      } while( u8g.nextPage() );
    }

    void Ctrl() {
      bool Af;
      bool Bf;
      static bool pf = 0;

        Af = digitalRead(enc_A);    // Read encoder pins
        Bf = digitalRead(enc_B);
        if((!Af) && (pf)) {
          // A has gone from high to low
          if(Bf) {
            i++;
          }
          else {
            i--;
          }
        }
        pf = Af;     // Store value of A for next time

        pause = 0;
    }

    SIGNAL(TIMER0_COMPA_vect) {
      if (pause < 500) pause++;
    }

    ISR(PCINT20_vect) {
      Ctrl();
    }

    void setup(void) {
      OCR0A = 0xAF;
      TIMSK0 |= _BV(OCIE0A);

      PCMSK2 |= _BV(PCINT20);
      PCICR  |= _BV(PCIE2);

      if ( u8g.getMode() == U8G_MODE_R3G3B2 ) {
        u8g.setColorIndex(255);     // white
      }
      else if ( u8g.getMode() == U8G_MODE_GRAY2BIT ) {
        u8g.setColorIndex(3);         // max intensity
      }
      else if ( u8g.getMode() == U8G_MODE_BW ) {
        u8g.setColorIndex(1);         // pixel on
      }

      pinMode(enc_A, INPUT);  //энкодер к1
      pinMode(enc_B, INPUT);  //энкодер к2
    }

    void loop() {
      draw(pause);
    }
     
    Последнее редактирование: 26 апр 2016
  8. AlexU

    AlexU Гуру

    Здесь речь идёт о функции 'attachInterrupt()', а не о внешних прерываниях в целом. Функция 'attachInterrupt()' действительно работает только с прерываниями INTx, которых в Atmega328P всего два -- INT0, INT1.

    И честно говоря немного подустал -- телепатия отнимает много сил...
    До сих пор Вы не сообщили что за плата (есть догадки, что Arduino Nano), к каким пинам подключен валкодер (опять догадки, что к пинам 3 и 4), а ко второму пину что-нибудь подключено? И т.д. и т.п.
    Если мои догадки верны, то что это такое:
    Код (C++):
    ISR(PCINT20_vect)
    При сборке ошибок не было?
     
    Inversus нравится это.
  9. Inversus

    Inversus Нуб

    Верно, Arduino Nano, Я упоминал
    Валкодер подключен к пинам 3 и 4. Ошибок не было, и даже благополучно вгрузил в контроллер. Делал по примеру отсюда http://forum.amperka.ru/threads/rc-передатчик-и-arduino-uno.1244/#post-11005
     
  10. AlexU

    AlexU Гуру

    В сообщении #7 в приведённом коде ошибка, которая не позволит скомпилировать код. Мало того, что из Вас клещами приходится вытаскивать необходимую для помощи информацию, так Вы ещё предоставляете недостоверные данные, которые не соответствуют действительности, поэтому я вряд ли смогу чем-нибудь Вам помочь.
     
  11. Inversus

    Inversus Нуб

    Я ценю Вашу помощь. Так как у меня опыт с контроллерами небольшой, видимо, Я не понимаю какая необходима информация, если Вы разъясните по пунктам, все с радостью предоставлю.

    Я скопировал свой код отсюда в среду ардуино - компилируется и грузиться зараза ! Поверьте, у меня нет намерения тратить Ваше и своё время впустую. Но, повторюсь, железо при этом работает не корректно - при вращении валкодера происходит перезагрузка. Если Вы знаете как исправить код - буду признателен.
     
    Последнее редактирование: 27 апр 2016
  12. Inversus

    Inversus Нуб

    Все поправил работает, сказывается малый опыт. Спасибо за содействие. Про прерывания на остальные пины не знал, Вы мне действительно помогли. И всё-же тот код компилировался без ошибок, и Ваш камень - разработчикам среды. Более того: ISR(mogno_napisat_chto_ugodno) - будет компилироваться ! Проверьте сами - Arduino v1.6.5.

    Как здесь плюс к стате поставить ?
     
    Последнее редактирование: 27 апр 2016
  13. AlexU

    AlexU Гуру

    Честно говоря ошибок в декларации обработчиков прерываний ни разу не допускал и был уверен, что компилятор будет на ошибку очень сильно ругаться, а он только предупреждение выдает:
    С недостоверной информацией погорячился -- прошу прощения. Но на предупреждения всё-таки следует обращать очень пристальное внимание.

    В данном случае Вы активировали обработку прерывания PCINT2, но обработчик не указали (нет такого прерывания как 'PCINT20'). Поэтому компилятор подставил "обработчик по-умолчанию", который производит перезагрузку контроллера.
    Правильно будет:
    Код (C++):
    ISR(PCINT2_vect)
    Так же поменяйте
    Код (C++):
    SIGNAL(TIMER0_COMPA_vect)
    на
    Код (C++):
    ISR(TIMER0_COMPA_vect)
    'SIGNAL' -- это устаревший макрос, сейчас он заменён на 'ISR'.
     
  14. Inversus

    Inversus Нуб

    Спасибо, поменял.

    Либо в Линуксе другой компилятор, либо поправили в 1.6.8. Но на винде версия 1.6.5 со строкой ISR(mogno_napisat_chto_ugodno) выдает только это:
    Поэтому сразу и не понял в чем проблема. Думаю что разрабам есть куда двигаться, особенно в направлении отладки. Я например алгоритмы напрямую не связанные с железом отлаживаю в QT, потом переношу в ардуино.
     
    Последнее редактирование: 27 апр 2016