Alex Опубликовано 31 января, 2009 Поделиться Опубликовано 31 января, 2009 (изменено) В этой теме обсуждаем подключение кнопок к МК и алгоритмы их обработки. --------------------------------------------------------------------------------------------------------------- Итак. Первый пример обработки кнопок от mail_robot для PIC'ов. http://forum.cxem.ne...20#comment-2213207 Ещё один пример от mail_robot под АРМы. http://forum.cxem.ne...20#comment-2213705 Еще один вариант описания алгоритма обслуживания клавиатуры от ST_A - http://forum.cxem.ne...20#comment-2214985 Вариант алгоритма от Illusi0ns - http://forum.cxem.ne...20#comment-2279559 . Код несколькими постами дальше - http://forum.cxem.ne...40#comment-2279693 Вариант от меня - http://forum.cxem.ne...40#comment-2293341 компилятор - CVAVR Ещё один вариант от POlSS0N - http://forum.cxem.net/index.php?showtopic=157804&st=60#comment-2380479 Пример под PIC16f877a. Изменено 4 апреля, 2016 пользователем Alex 0 Ссылка на комментарий Поделиться на другие сайты Поделиться
DmitryS Опубликовано 25 августа, 2015 Поделиться Опубликовано 25 августа, 2015 Всем доброго дня ! В посте я боролся с помехами , помехи вроде победил , теперь решил немного модернизировать программу , и сделать опрос кнопки управления воротами не в основном цикле , а повесить кнопку на INT0 , и осуществлять управление в обработчике прерываний . /* Garazh_vorota3.c * Created: 20.08.2015 23:50:50 * Author: Dmitry S */ //ATtiny13A Управление секционными воротами #define F_CPU 1200000 #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> #define DIRECT_OPEN PORTB |= (1<<0)//&= ~(1<<0) #define DIRECT_CLOSE PORTB &= ~(1<<0)//|= (1<<0) #define MOTOR_STOP PORTB |= (1<<2)//&= ~(1<<1) #define MOTOR_LAUNCH PORTB &= ~(1<<2)//|= (1<<1) unsigned char a=1; void Stop() // Остановка двигателя { _delay_ms(20); MOTOR_STOP; _delay_ms(50); DIRECT_OPEN; }; ISR(INT0_vect) { GIMSK &= ~(1<<INT0); switch(a) { case 1: _delay_ms(50); DIRECT_OPEN; // Вращение на открытие _delay_ms(100); MOTOR_LAUNCH; // Пуск двигателя a=2; break; case 2: // Stop Stop(); a=3; break; case 3: _delay_ms(50); DIRECT_CLOSE; // Вращение на закрытие _delay_ms(150); MOTOR_LAUNCH; // Пуск двигателя a=4; break; case 4: //Stop Stop(); a=1; break; } } ISR(PCINT0_vect) // обработка прерывания { if (!(PINB &(1<<PINB3))) // Только если концевик замкнут { MOTOR_STOP; // остановить двигатель _delay_ms(50); DIRECT_OPEN; a++; // определить направление вращения if(a==5) a=1; } if (!(PINB &(1<<PINB4))) // Защита заклинивания ворот по току двигателя { _delay_ms(300); // отфильтровываем пусковой ток if (!(PINB &(1<<PINB4))) { MOTOR_STOP; // остановить двигатель _delay_ms(50); DIRECT_OPEN; a++; // определить направление вращения if(a==5) a=1; } } } int main(void) { // Настрока порта B DDRB = 0b00000101; PORTB = 0b00011111; PCMSK |= (1<<PCINT4) | (1<<PCINT3);// Настройка прерывания PCINT3 PCINT4 MCUCR |= (ISC01<<1) | (ISC00<<0);//INT0 по спадающему фронту GIMSK |= (1<<PCIE) | (1<<INT0);//Разрешение прерываний GIFR |= (1<<PCIF) | (1<<INTF0);//Сброс флагов sei(); while(1) { GIMSK |= (1<<INT0); } } И нихрена у меня не получилось , INT0 ловит дребезг кнопки просто жутко , хоть и прописано в коде уходить в прерывание по спадающему фронту , преывание ловит его и при размыкании кнопки . В Протеусе все работает отлично , в железе просто жуть . Я пробовал даже запрещать прерывание INT0 в обработчике , ни чего не помогает . Пришлось все возвращать в зад . Причину такого поведения INT0 я что то не догоняю , ведь прерывания по PCINT работают вполне корректно ? 0 Ссылка на комментарий Поделиться на другие сайты Поделиться
20% скидка на весь каталог электронных компонентов в ТМ Электроникс!Акция "Лето ближе - цены ниже", успей сделать выгодные покупки!Плюс весь апрель действует скидка 10% по промокоду APREL24 + 15% кэшбэк и бесплатная доставка!Перейти на страницу акции Реклама: ООО ТМ ЭЛЕКТРОНИКС, ИНН: 7806548420, info@tmelectronics.ru, +7(812)4094849
my504 Опубликовано 25 августа, 2015 Поделиться Опубликовано 25 августа, 2015 Это какой то жуткий бред с прерыванием в обработчике которого стоят задержки по 50 мс. Причем тут вообще прерывания, если кнопка нажимается РУКОЙ, то есть задержка в обработке этого события никаким боком к реальному времени не привязана. Правда если у Вас алгоритм строится на таких же задержках, то тогда вообще нет вменяемого решения... 0 戦う前に相手のベルトの色に注目 Ссылка на комментарий Поделиться на другие сайты Поделиться
Выбираем схему BMS для корректной работы литий-железофосфатных (LiFePO4) аккумуляторов Обязательным условием долгой и стабильной работы Li-FePO4-аккумуляторов, в том числе и производства EVE Energy, является применение специализированных BMS-микросхем. Литий-железофосфатные АКБ отличаются такими характеристиками, как высокая многократность циклов заряда-разряда, безопасность, возможность быстрой зарядки, устойчивость к буферному режиму работы и приемлемая стоимость. Но для этих АКБ, также как и для других, очень важен контроль процесса заряда и разряда, а специализированных микросхем для этого вида аккумуляторов не так много. Инженеры КОМПЭЛ подготовили список имеющихся микросхем и возможных решений от разных производителей. Подробнее>> Реклама: АО КОМПЭЛ, ИНН: 7713005406, ОГРН: 1027700032161
mail_robot Опубликовано 25 августа, 2015 Поделиться Опубликовано 25 августа, 2015 (изменено) Кнопки это вообще больная тема для новичков. При размыкании кнопки происходит тот же самый дребезг, что и при замыкании. Поэтому вылавливание фронтов пустая затея. Необходимо по входу в прерывание его запретить (и только его), поставить на эту тему глобальный флаг. По выходу из прерывания отработать антидребезг и сбросить флаг и только после этого опять разрешить прерывание по входу. Но вообще вешать кнопку на прерывание по входу не лучшая идея. Кнопка это всегда медленное событие и мгновенная его обработка лишена здравого смысла. Кнопки обычно обрабатываются в основном цикле процедурой опроса. Быстрее им просто не требуется INT0 предназначен в основном для мгновенной обработки несистемных событий, требующих именно безотлагательных действий контроллера. К примеру прием ИК посылки. Хотя и это не та задача, потому как она тоже достаточно неторопливая. Но просто как пример Изменено 25 августа, 2015 пользователем mail_robot 1 Нужно делать то, что нужно. А то, что не нужно, делать не нужно. (С) Винни Пух Ссылка на комментарий Поделиться на другие сайты Поделиться
Viktor26 Опубликовано 25 августа, 2015 Поделиться Опубликовано 25 августа, 2015 настроите таймер на сброс при совпадении, отмеряйте через сколько тактов при ваше частоте пройдет 20 мс, и в этом обработчике опрашивайте кнопку. заведите 2 флага обработки этой кнопки, и антидребезг вам обеспечен и основной цикл всякой фигней не занимается, и делеи проч 0 Не знаеш как? Спроси у Google'а !!! Ссылка на комментарий Поделиться на другие сайты Поделиться
COKPOWEHEU Опубликовано 25 августа, 2015 Поделиться Опубликовано 25 августа, 2015 Плюс если линия от кнопки до контроллера достаточно длинная, надо озаботиться подавлением помех 0 Ругался на отсутствие форматирования исходного кода (включая отсутствие осмысленных комментариев и наличие неубранного после конфигуратора мусора) не менее 15 раз. Часть моих наработок. Ссылка на комментарий Поделиться на другие сайты Поделиться
DmitryS Опубликовано 25 августа, 2015 Поделиться Опубликовано 25 августа, 2015 (изменено) Я почему решил попробовать повесить кнопку на прерывание INT0, потому что в моем в первоначальном исполнении хоть все и работает , но есть небольшой недостаток , когда кнопка опрашивается в основном цикле , если кнопку долго держать нажатой , больше 1сек , выполнится следующее действие , т.е выбор в операторе switch будет происходить по череди с интервалом чуть больше секунды , вот я и хотел избавится от этого недостатка , повесив кнопку на INT0 , что бы сколько кнопку не держи нажатой , пока её не отпустишь и не нажмешь по новой , ни чего не происходило . /* * GarVorota_2.c * * Created: 13.04.2015 21:57:50 * Author: Dmitry */ #define F_CPU 1200000 #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> #define NAPR_OPEN PORTB |= (1<<0)//&= ~(1<<0) #define NAPR_CLOSE PORTB &= ~(1<<0)//|= (1<<0) #define DVIG_STOP PORTB |= (1<<1)//&= ~(1<<1) #define DVIG_PUSK PORTB &= ~(1<<1)//|= (1<<1) unsigned char a=1; //unsigned char b; ISR(PCINT0_vect) // обработка прерывания { if (!(PINB &(1<<PINB3))) // Только если концевик замкнут { DVIG_STOP; // остановить двигатель _delay_ms(50); NAPR_OPEN; a++; // определить направление вращения if(a==5) a=1; } if (!(PINB &(1<<PINB4))) // Защита заклинивания ворот по току двигателя { _delay_ms(300); // отфильтровываем пусковой ток if (!(PINB &(1<<PINB4))) { DVIG_STOP; // остановить двигатель _delay_ms(50); NAPR_OPEN; a++; // определить направление вращения if(a==5) a=1; } } } void Stop() // Остановка двигателя { _delay_ms(20); DVIG_STOP; _delay_ms(50); NAPR_OPEN; }; int main(void) { // Настрока порта DDRB = 0b00000011; PORTB = 0b00011111; // Настройка и разрешение прерывания PCINT3 PCINT4 PCMSK = (0<<PCINT5) | (1<<PCINT4) | (1<<PCINT3) | (0<<PCINT2) | (0<<PCINT1) | (0<<PCINT0); GIMSK |= (1<<PCIE); GIFR |= (1<<PCIF); sei(); while(1) { _delay_ms(1000); while (PINB &(1<<PINB2)){} switch(a) { case 1: _delay_ms(100); NAPR_OPEN; // Вращение на открытие _delay_ms(100); DVIG_PUSK; // Пуск двигателя a=2; break; case 2: // Stop Stop(); a=3; break; case 3: _delay_ms(100); NAPR_CLOSE; // Вращение на закрытие _delay_ms(100); DVIG_PUSK; // Пуск двигателя a=4; break; case 4: //Stop Stop(); a=1; break; } } } Плюс если линия от кнопки до контроллера достаточно длинная, надо озаботиться подавлением помех Немного по подробней можно , примерно как должна выглядеть эта защита ? Изменено 25 августа, 2015 пользователем DmitryS 0 Ссылка на комментарий Поделиться на другие сайты Поделиться
Alex Опубликовано 25 августа, 2015 Автор Поделиться Опубликовано 25 августа, 2015 У Вас все задержки дальнейшей программы из-за delay-ев. Не используйте их без особой надобности. Для чего втыкать задержку, скажем на 100 миллисекунд, если за это время контроллер может делать что-то полезное ? А ведь delay_ms - это просто, извиняюсь за выражение, тупое зацикливание программы. Пустой цикл... 0 Ссылка на комментарий Поделиться на другие сайты Поделиться
DmitryS Опубликовано 25 августа, 2015 Поделиться Опубликовано 25 августа, 2015 (изменено) Уважаемый Alex , я знаю , что делей это просто задержка и что в это время МК мог бы заняться еще чем то , но я не могу придумать контроллеру задачу на это время , а ту задачу которую я ему задал , он поганец отказывается выполнять . Шутка .А по поаду 100мс , так это я пробовал бороться с помехами , потому такие большие и поставил , изначально да и сейчас можно уменьшить до 20-50мс , это антидребезг . А вот другие задержки нужны , потому как два реле которые меняют полярность на двигателе , могут в силу своих конструктивных особеностей страбатывать не одновременно , по этому я переключаю сначала эти реле , жду когда они сработают , и лишь потом другим реле пускаю на них ток . На всякий случай так сказать . Изменено 25 августа, 2015 пользователем DmitryS 0 Ссылка на комментарий Поделиться на другие сайты Поделиться
Alex Опубликовано 25 августа, 2015 Автор Поделиться Опубликовано 25 августа, 2015 Чтобы зафиксировать отжатие/нажатие кнопки, не обязательно его ждать. Достаточно просто поймать момент изменения уровня на ноге МК. И ловить его можно необязательно внешним прерыванием, а периодичным опросом состояния входа, к которому подключена кнопка. Даже, я бы сказал не можно, а нужно - во избежании поимки дребезга. 0 Ссылка на комментарий Поделиться на другие сайты Поделиться
DmitryS Опубликовано 25 августа, 2015 Поделиться Опубликовано 25 августа, 2015 (изменено) Аlex , а я что делаю ? _delay_ms(1000); while (PINB &(1<<PINB2)){} switch(a) { case 1: _delay_ms(100); NAPR_OPEN; // Вращение на открытие _delay_ms(100); DVIG_PUSK; // Пуск двигателя a=2; break; case 2: // Stop Stop(); a=3; break; case 3: _delay_ms(100); NAPR_CLOSE; // Вращение на закрытие _delay_ms(100); DVIG_PUSK; // Пуск двигателя a=4; break; case 4: //Stop Stop(); a=1; break; } И я опрашиваю состояние входа . Может это надо делать как то по другому ? Изменено 25 августа, 2015 пользователем DmitryS 0 Ссылка на комментарий Поделиться на другие сайты Поделиться
Alex Опубликовано 25 августа, 2015 Автор Поделиться Опубликовано 25 августа, 2015 _delay_ms(1000); while (PINB &(1<<PINB2)){} У Вас задержка на 1 секунду (зачем, совершенно не понятно) и ожидание уровня на входе ! Это не фиксирование изменения, а просто зацикливание программы, с ожиданием уровня. 0 Ссылка на комментарий Поделиться на другие сайты Поделиться
DmitryS Опубликовано 25 августа, 2015 Поделиться Опубликовано 25 августа, 2015 (изменено) У Вас задержка на 1 секунду (зачем, совершенно не понятно)Если убрать эту задержку , то не возможно предсказать что выберется в свитче Изменено 25 августа, 2015 пользователем Alex Цитата 0 Ссылка на комментарий Поделиться на другие сайты Поделиться
Alex Опубликовано 25 августа, 2015 Автор Поделиться Опубликовано 25 августа, 2015 не возможно предсказать что выберется в свитчеЭто как ?В свиче выберется всегда тот пункт, значение которого имеет переменная в скобках. Видимо, я то-то недопонимаю... Что у Вас на PINB2 ? 0 Ссылка на комментарий Поделиться на другие сайты Поделиться
DmitryS Опубликовано 25 августа, 2015 Поделиться Опубликовано 25 августа, 2015 (изменено) не возможно предсказать что выберется в свитчеЭто как ?В свиче выберется всегда тот пункт, значение которого имеет переменная в скобках. Видимо, я то-то недопонимаю... Этот свитч мк за несколько мс прочешет несколько десятков раз , эта конструкция стоит в основном цике , т.е. она постоянно опрашивается с высокой скоростью , за время нажатия кнопки , даже кратковременное , контролер успевает несколько раз прокрутить весь цикл , т.е пока кнопка нажата даже кратковременно МК заходит в перый case выходит из него , заходит во второй case и выходит из него , итак туеву хучу раз , приходится его остановить принудительно , на какое то время .На PINB2 кнопка , этот пин и опрашивается Изменено 25 августа, 2015 пользователем DmitryS 0 Ссылка на комментарий Поделиться на другие сайты Поделиться
Alex Опубликовано 25 августа, 2015 Автор Поделиться Опубликовано 25 августа, 2015 Всё понятно. Сделайте тогда, хотя бы, так : while(1){ static char butt_cur=0, butt_prev=0; // Не знаю, есть ли булевые переменные в вашем компиляторе. По этому - char //..................... //..................... _delay_ms(10); butt_cur=((PINB &(1<<PINB2))==0); // 0- активный уровень кнопки if(butt_cur && !butt_prev){ // Если нажали кнопку //..................... // Тут код, который нужно выполнить после нажатия //..................... // Т.б. , как я понял, switch. } butt_prev=butt_cur; //..................... // Тут выполняем что-то полезное. Но не задерживаем надолго программу. //..................... } максимум, на сколько будет задерживаться цикл - 10 миллисекунд. Но по уму, опрос ПИНа и сравнение его с предыдущим значением нужно вынести в обработчик прерывания по таймеру, настроенный на 10 мс. Тогда основной цикл полностью освободиться от этой рутины и можно будет его удерживать любое время. Кстати, вот это : butt_cur=((PINB &(1<<PINB2))==0); if(butt_cur && !butt_prev){ .......... } butt_prev=butt_cur; и есть детектирование изменения уровня. Т.е. опрашиваем вход, сравниваем с предыдущим на изменение, сохраняем текущее значение для следующего опроса (оно потом будет предыдущим). Этот код нужно вынести в обработчик по таймеру и выставлять флаг нажатой кнопки. А в основном цикле уже этот флаг опрашивать, не забыв его сбросить после опроса. Тогда Вы в основном цикле, в любой момент времени, сможете узнать была ли нажата кнопка, задерживая цикл абсолютно на любое время. Вот и вся магия... 0 Ссылка на комментарий Поделиться на другие сайты Поделиться
DmitryS Опубликовано 25 августа, 2015 Поделиться Опубликовано 25 августа, 2015 (изменено) Спасибо Alex за пример , но честно , я мало понял как это работает , особливо вот эта строчка butt_cur=((PINB &(1<<PINB2))==0); // 0- активный уровень кнопки Но вы меня наталкнули на мысль , что надо внутри каждого case , сделать проверку , на нажата ли еще кнопка Выглядеть будет примерно так while (PINB &(1<<PINB2)){} switch(a) { case 1: _delay_ms(20); NAPR_OPEN; // Вращение на открытие _delay_ms(100); DVIG_PUSK; // Пуск двигателя if (~PINB &(1<<PINB2)) { a=1; } else a=2; break; case 2: // Stop Stop(); if (~PINB &(1<<PINB2)) { a=2; } else a=3; break; case 3: _delay_ms(20); NAPR_CLOSE; // Вращение на закрытие _delay_ms(100); DVIG_PUSK; // Пуск двигателя if (~PINB &(1<<PINB2)) { a=3; } else a=4; break; case 4: //Stop Stop(); if (~PINB &(1<<PINB2)) { a=4; } else a=1; break; } Изменено 25 августа, 2015 пользователем DmitryS 0 Ссылка на комментарий Поделиться на другие сайты Поделиться
Alex Опубликовано 25 августа, 2015 Автор Поделиться Опубликовано 25 августа, 2015 особливо вот эта строчкаВ переменную butt_cur заносится инвертированное значение ПИНа. Т.б. состояние кнопки (=1 - нажата, =0 - отжата).А дальше это значение сравнивается с предыдущим. Этим самым сравнением мы и ловим изменение состояния кнопки. надо внутри каждого case , сделать проверку , на нажата ли еще кнопкаЗачем ? Я же Вам написал готовый пример. Разберитесь с ним, он на самом деле прост до безобразия 0 Ссылка на комментарий Поделиться на другие сайты Поделиться
Alexeyslav Опубликовано 25 августа, 2015 Поделиться Опубликовано 25 августа, 2015 если кнопку долго держать нажатой , больше 1сек , выполнится следующее действие Как уже говорили, это из-за того что вы просто проверяете уровень состояния кнопки. Для вашей задачи, его надо не просто проверять а еще и сравнивать с предыдущим. А это предыдущее значение надо где-то хранить, в переменной. Тогда, вместо того чтобы просто сравнить значение вы смотрите состояние кнопки - ага, нажата! А потом смотрите предыдущее состояние кнопки - если она нажата была до этого, то ничего не делать а если отпущена - вот только тогда что-то делать с ней. Аналогично с отпусканием кнопки - если текущее состояние отпущено, смотрим предыдущее - если оно тоже равно отпущено - ничего не делаем. А если же равно нажатое - значит произошло отпускание кнопки, делаем необходимое действие. И после всего этого сравнения не забыть скопировать текущее состояние кнопки в ту самую переменную. Теперь этот алгоритм можно выполнять хоть 10 раз в секунду - соответствующее действие произойдет лишь однократно для нажатия и если это необходимо - для отпускания. 0 Учение - изучение правил. Опыт - изучение исключений. Ссылка на комментарий Поделиться на другие сайты Поделиться
mail_robot Опубликовано 26 августа, 2015 Поделиться Опубликовано 26 августа, 2015 (изменено) а у меня есть неплохие читабельные примеры кода под арм и пик. Один без таймеров, второй с ними, но оба без задержек. Работают безотказно ну мало ли... Изменено 26 августа, 2015 пользователем mail_robot 0 Нужно делать то, что нужно. А то, что не нужно, делать не нужно. (С) Винни Пух Ссылка на комментарий Поделиться на другие сайты Поделиться
my504 Опубликовано 26 августа, 2015 Поделиться Опубликовано 26 августа, 2015 Я не претендую на истину в последней инстанции. Просто почему то все хотят некоего кода, который можно скопипастить в свой проект. Причем совершенно не понимая как оно работает и с чем конфликтует. Очень ценными являются такие идеи, которые адаптируются в проекты как ФОНОВЫЙ ПРОЦЕСС с минимальным занятием ресурсов. Особенно вычислительных. Обработка кнопок должна напоминать работу официанта в ресторане. То есть основной процесс (клиент) не должен ощущать его присутствия иначе как по результату действия. 0 戦う前に相手のベルトの色に注目 Ссылка на комментарий Поделиться на другие сайты Поделиться
Viktor26 Опубликовано 27 августа, 2015 Поделиться Опубликовано 27 августа, 2015 Пример с использованием таймера Т0 в Atmega8 Особых настроек этот таймер не требует, так-как он умеет только генерировать прерывание по переполнению и ничего больше. код приводить не буду, пишите его сами. все на словах, постараюсь очень, приочень доступно объяснить. условно тактируем МК от внутреннего RC 1 МГц и так, подключаем все необходимые файлы к проекту, не забываем про interrupt.h настраиваем порты как Вам нужно, записываем единичку в нужный бит регистра TIMSK (какой бит отвечает за разрешения прерывания по событию "переполнение Т0" смотрите в даташите) теперь запускаем таймер с делителем \256. Почему \256? потому что с таким коэффициентом деления он будет переполнятся раз в ~65мс. теперь в обработчике прерывания от Т0 опрашиваем кнопку, один раз в ~65мс, вот и антидребезг. если кнопка нажата выставляем флаг в единицу(флаг-любая переменная с удобным для вас именем) в главном цикле программы проверяем флаг, если он равен единице выполняем нужный код, ВНИМАНИЕ, в конце которого НЕ забываем сбросить флаг. На вот такой метод меня подтолкнул Геннадий(спасибо ему огромное, многому научил меня), но он делал это немножечко мудрее, как-то с двумя флагами, так и не въехал как. Хотя и описанная мною конструкция тоже очень хорошо и без сбоев работает. 0 Не знаеш как? Спроси у Google'а !!! Ссылка на комментарий Поделиться на другие сайты Поделиться
солар Опубликовано 27 августа, 2015 Поделиться Опубликовано 27 августа, 2015 (изменено) Первый флаг flag1 - предыдущее состояние кнопки. Второй флаг flag2 - текущее состояние кнопки. flag1= flag2; flag2= читаем пин кнопки. if(flag1 && !flag2) //- момент нажатия кнопки if(!flag1 && !flag2) //- кнопка удерживается нажатой if(!flag1 && flag2) //- момент отпускания кнопки if(flag1 && flag2) //- кнопка не нажата. Тут вам и антидребезг, и состояния кнопки. Надеюсь, теперь въезжабельно. Изменено 27 августа, 2015 пользователем солар 1 Я не раздаю удочки. Я продаю рыбу. Ссылка на комментарий Поделиться на другие сайты Поделиться
Viktor26 Опубликовано 27 августа, 2015 Поделиться Опубликовано 27 августа, 2015 оченамана 0 Не знаеш как? Спроси у Google'а !!! Ссылка на комментарий Поделиться на другие сайты Поделиться
mail_robot Опубликовано 27 августа, 2015 Поделиться Опубликовано 27 августа, 2015 (изменено) Ну начнем пожалуй с простого варианта, но в общем то годного для многих задач, не требующих постоянного внимания процессора. Это задачи типа - нажал на кнопку, сработало реле. Потом отпустили кнопку, чего то ждем. В принципе это аналогично использованию функции delay, только чуть элегантнее. Написано для PIC16F886 Суть в том, чтобы заставить работать программу в ритме с системными тиками, которые формируются в прерывании одного из таймеров и позволяют в любом месте программы получать задержки нужной продолжительности. Я использую обычно TMR0 для этой цели. Перво наперво надо сконфигурировать таймер. Делается это в регистре OPTION_REG void InitApp(void) { ... OPTION_REG = 0b01010110; // O_R<7>=0 init individual latch for port B (включить индивидуальную подтяжку порта В) // |___________ O_R<5>=0 timer 0 internal CLK source Fosc/4 (тактирование таймера 0 от внутреннего генератора) // |_________ O_R<3>=0 prescaller assigned to Timer 0 (говорим что таймер будет работать с предделителем) // |||______ O_R<2:0>=110 prescaller rate 1:128 (устанавливаем предделитель в значение 128) // Timer 0 period = 1/120 sec (теперь период переполнений таймера будет 1/120 секунды или 8,3 мс) T0IE = 1; // Timer 0 interrupt ON (обязательно разрешаем прерывания от таймера 0) // не забываем о том что чуть позже будет неплохо разрешить еще и прерыывания в принципе GIE = 1 как только нам понадобится начать считать тики; . . . после такой инициализации таймер TMR0 дает нам 120 прерываний в секунду. Достаточно мелко для относительно быстрых процессов и достаточно крупно для реализации долгих задержек если требуется. Код прерывания таймера void interrupt isr(void) { if (T0IF) // если произошло прерывание таймера (переполнился) { TMR0 = 126; // перезагружаем таймер заново некоторым значением, которое дает нам более менее близкий к милисекунде интервал. Можно за точностью не гнаться, если она не нужна особо. Но точных часов тут сделать не получится T0IF = 0; // сбрасываем флаг прерывания (сам он не сбросится) if (TICK == 255) TICK = 0; // на всякий случай проверяем переполнение системных тиков если где-то прозевали TICK++; // если с тиками все в порядке, фиксируем тик } } тут надо пояснить, что в основной программе обьявлена глобальная переменная char TICK; , которая глобально хранит значение текущего количества системных тиков. Для чего они нужны увидим чуть ниже. Эта переменная обнуляется в основной программе всякий раз перед и после окончания использования. Тем самым мы добиваемся ее постоянной готовности к делу и следим, чтобы она никогда не переполнилась. А если переполнилась, то это можно использовать как индикатор некой ошибки и включать процедуру обработки исключения, вплоть до перезапуска программы. Можно считать это софтовым WDT таймером, но с более интеллектуальной логикой работы. Итак, как же пользоваться тиком Вот простой пример полусекундной задержки TICK = 0; while (TICK!=60); вот код обработки нажатия на кнопку if (RB4==0) // нажали на кнопку, активный уровень 0 { SendMedic(); // делаем чтото полезное пока не отвлекаясь на кнопку (я посылаю ИК команду) TICK = 0; // сбрасываем счетчк тиков while (TICK<5) // отсчитываем 5 мс на защиту от дребезга при замыкании (на всякий пожарный) // если процедура SendMedic() долгая, то можно и не делать защиту while (!RB4); //Ждем когда юзер отпустит кнопку TICK = 0; // опять сбрасываем системный тик while (TICK<5); // отсчитываем еще 5 мс для подавления дребезга } данная процедура писалась под тактовые кнопки еще в пору когда я только начинал шкодить, поэтому делайте скидку на относительную примитивность. Однако код может быть познавательным. В принципе можно это все дело оформить и на delay, тем самым немного упростив. Главное понять общи принцип проект целиком Аптечка умная.zip Изменено 28 августа, 2015 пользователем mail_robot 2 Нужно делать то, что нужно. А то, что не нужно, делать не нужно. (С) Винни Пух Ссылка на комментарий Поделиться на другие сайты Поделиться
Рекомендуемые сообщения
Присоединяйтесь к обсуждению
Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.
Примечание: Ваш пост будет проверен модератором, прежде чем станет видимым.