Всем привет ! Использую для вывода на дисплей библиотеку 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() { }
В обработчике прерывания 'TIMER0_COMPA_vect' вызывается функция 'draw()', которая занимается выводом на дисплей, -- это не очень хорошо. Я бы даже сказал, что очень плохо. Может стоит функцию 'draw()' перенести внутрь функции 'loop()'? Второе, функция 'ctrl()', которая "читает" енкодер, является обработчиком прерывания 'INT1' (изменения уровня на пине 3) и одновременно вызывается из обработчика прерывания 'TIMER0_COMPA_vect' -- здесь явно, что-то перемудрили... И переменную 'i' объявите как: Код (C++): volatile int i=0; , а то компилятор может немного "начудить". PS: информации мало предоставили -- вероятность получить реальную помощь стремиться к нулю.
Да, действительно перемудрил. Полагаю 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) через диоды, и по общему исключению уже считывать "родные" пины кнопок, но из за дребезга, который не удалось до конца устранить кондёрами, такая схема работает очень глючно. Может есть готовое приемлемое решение в таком духе ?
Для этого не обязательно мудрить с таймером. Можно воспользоваться функциями 'millis()' или 'micros()'. Обработку кнопок лучше делать с помощью внешних прерываний (внешние прерывания поддерживают все сигнальные пины). Аппаратно -> низкочастотные RC-фильтры + триггер Шмитта. Программно -> использование функции 'millis()'. И повторюсь -- слишком мало информации, например, как устроен валкодер и как он подключен?
Валкодер механический маде ин чина (EC11) топорный но зато дешёвый Подключение стандартное: А вот про прерывания не понял, цитирую "Большинство контроллеров Arduino умеют обрабатывать до двух внешних прерываний". На одном у меня висит ответственный датчик, и на все остальное остаётся только одно, вроде так ?
Откуда цитата? Повторяю, в AVR, которые используются в Arduino, все сигнальные пины поддерживают внешние прерывания. Те два, о которых Вы говорите, более функциональные -- могут срабатывать по фронту, изменению и т.п. -- остальные только по изменению состояния. Ищите информацию про прерывания PCINTx. PS: Arduino это семейство плат, в некоторых из которых используются микроконтроллеры AVR.
Цитата отсюда: http://arduino.ru/Reference/AttachInterrupt Да и тут то же самое написали http://arduino.ua/ru/prog/AttachInterrupt Странно почему в ардуино не реализовали штатным способом остальные внешние прерывания. Попробовал через PCINT - реакция контроллера на вращение валкодера - перезагрузка Код (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); }
Здесь речь идёт о функции 'attachInterrupt()', а не о внешних прерываниях в целом. Функция 'attachInterrupt()' действительно работает только с прерываниями INTx, которых в Atmega328P всего два -- INT0, INT1. И честно говоря немного подустал -- телепатия отнимает много сил... До сих пор Вы не сообщили что за плата (есть догадки, что Arduino Nano), к каким пинам подключен валкодер (опять догадки, что к пинам 3 и 4), а ко второму пину что-нибудь подключено? И т.д. и т.п. Если мои догадки верны, то что это такое: Код (C++): ISR(PCINT20_vect) При сборке ошибок не было?
Верно, Arduino Nano, Я упоминал Валкодер подключен к пинам 3 и 4. Ошибок не было, и даже благополучно вгрузил в контроллер. Делал по примеру отсюда http://forum.amperka.ru/threads/rc-передатчик-и-arduino-uno.1244/#post-11005
В сообщении #7 в приведённом коде ошибка, которая не позволит скомпилировать код. Мало того, что из Вас клещами приходится вытаскивать необходимую для помощи информацию, так Вы ещё предоставляете недостоверные данные, которые не соответствуют действительности, поэтому я вряд ли смогу чем-нибудь Вам помочь.
Я ценю Вашу помощь. Так как у меня опыт с контроллерами небольшой, видимо, Я не понимаю какая необходима информация, если Вы разъясните по пунктам, все с радостью предоставлю. Я скопировал свой код отсюда в среду ардуино - компилируется и грузиться зараза ! Поверьте, у меня нет намерения тратить Ваше и своё время впустую. Но, повторюсь, железо при этом работает не корректно - при вращении валкодера происходит перезагрузка. Если Вы знаете как исправить код - буду признателен.
Все поправил работает, сказывается малый опыт. Спасибо за содействие. Про прерывания на остальные пины не знал, Вы мне действительно помогли. И всё-же тот код компилировался без ошибок, и Ваш камень - разработчикам среды. Более того: ISR(mogno_napisat_chto_ugodno) - будет компилироваться ! Проверьте сами - Arduino v1.6.5. Как здесь плюс к стате поставить ?
Честно говоря ошибок в декларации обработчиков прерываний ни разу не допускал и был уверен, что компилятор будет на ошибку очень сильно ругаться, а он только предупреждение выдает: С недостоверной информацией погорячился -- прошу прощения. Но на предупреждения всё-таки следует обращать очень пристальное внимание. В данном случае Вы активировали обработку прерывания PCINT2, но обработчик не указали (нет такого прерывания как 'PCINT20'). Поэтому компилятор подставил "обработчик по-умолчанию", который производит перезагрузку контроллера. Правильно будет: Код (C++): ISR(PCINT2_vect) Так же поменяйте Код (C++): SIGNAL(TIMER0_COMPA_vect) на Код (C++): ISR(TIMER0_COMPA_vect) 'SIGNAL' -- это устаревший макрос, сейчас он заменён на 'ISR'.
Спасибо, поменял. Либо в Линуксе другой компилятор, либо поправили в 1.6.8. Но на винде версия 1.6.5 со строкой ISR(mogno_napisat_chto_ugodno) выдает только это: Поэтому сразу и не понял в чем проблема. Думаю что разрабам есть куда двигаться, особенно в направлении отладки. Я например алгоритмы напрямую не связанные с железом отлаживаю в QT, потом переношу в ардуино.