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

Подключение Кнопок К Микроконтроллерам


Рекомендуемые сообщения

В этой теме обсуждаем подключение кнопок к МК и алгоритмы их обработки.

---------------------------------------------------------------------------------------------------------------

Итак. Первый пример обработки кнопок от 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.

Изменено пользователем Alex
Ссылка на комментарий
Поделиться на другие сайты

  • 6 лет спустя...

Всем доброго дня !

В посте я боролся с помехами , помехи вроде победил , теперь решил немного модернизировать программу , и сделать опрос кнопки управления воротами не в основном цикле , а повесить кнопку на 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 работают вполне корректно ?

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

Это какой то жуткий бред с прерыванием в обработчике которого стоят задержки по 50 мс.

Причем тут вообще прерывания, если кнопка нажимается РУКОЙ, то есть задержка в обработке этого события никаким боком к реальному времени не привязана.

Правда если у Вас алгоритм строится на таких же задержках, то тогда вообще нет вменяемого решения...

戦う前に相手のベルトの色に注目

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

Сравнительное тестирование аккумуляторов EVE Energy и Samsung типоразмера 18650

Инженеры КОМПЭЛ провели сравнительное тестирование аккумуляторов EVE и Samsung популярного для бытовых и индустриальных применений типоразмера 18650. 

Для теста были выбраны аккумуляторы литий-никельмарганцевой системы: по два образца одного наименования каждого производителя – и протестированы на двух значениях тока разряда: 0,5 А и 2,5 А. Испытания проводились в нормальных условиях на электронной нагрузке EBD-USB от ZKEtech, а зарядка осуществлялась от лабораторного источника питания в режиме CC+CV в соответствии с рекомендациями в даташите на определенную модель. Подробнее>>

Реклама: АО КОМПЭЛ, ИНН: 7713005406, ОГРН: 1027700032161

Кнопки это вообще больная тема для новичков.

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

Но вообще вешать кнопку на прерывание по входу не лучшая идея. Кнопка это всегда медленное событие и мгновенная его обработка лишена здравого смысла. Кнопки обычно обрабатываются в основном цикле процедурой опроса. Быстрее им просто не требуется

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

Изменено пользователем mail_robot

Нужно делать то, что нужно. А то, что не нужно, делать не нужно. (С) Винни Пух

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

Новый аккумулятор EVE серии PLM для GSM-трекеров, работающих в жёстких условиях (до -40°С)

Компания EVE выпустила новый аккумулятор серии PLM, сочетающий в себе высокую безопасность, длительный срок службы, широкий температурный диапазон и высокую токоотдачу даже при отрицательной температуре. 

Эти аккумуляторы поддерживают заряд при температуре от -40/-20°С (сниженным значением тока), безопасны (не воспламеняются и не взрываются) при механическом повреждении (протыкание и сдавливание), устойчивы к вибрации. Они могут применяться как для автотранспорта (трекеры, маячки, сигнализация), так и для промышленных устройств мониторинга, IoT-устройств. Подробнее параметры и результаты тестов новой серии PLM по ссылке.

Реклама: АО КОМПЭЛ, ИНН: 7713005406, ОГРН: 1027700032161

настроите таймер на сброс при совпадении, отмеряйте через сколько тактов при ваше частоте пройдет 20 мс, и в этом обработчике опрашивайте кнопку. заведите 2 флага обработки этой кнопки, и антидребезг вам обеспечен и основной цикл всякой фигней не занимается, и делеи проч

Не знаеш как? Спроси у Google'а !!!

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

Литиевые батарейки и аккумуляторы от мирового лидера  EVE в Компэл

Компания Компэл, официальный дистрибьютор EVE Energy, бренда №1 по производству химических источников тока (ХИТ) в мире, предлагает продукцию EVE как со склада, так и под заказ. Компания EVE широко известна в странах Европы, Америки и Юго-Восточной Азии уже более 20 лет. Недавно EVE была объявлена поставщиком новых аккумуляторных элементов круглого формата для электрических моделей «нового класса» компании BMW.

Продукция EVE предназначена для самого широкого спектра применений – от бытового до промышленного. Подробнее>>

Реклама: АО КОМПЭЛ, ИНН: 7713005406, ОГРН: 1027700032161

Плюс если линия от кнопки до контроллера достаточно длинная, надо озаботиться подавлением помех

Ругался на отсутствие форматирования исходного кода (включая отсутствие осмысленных комментариев и наличие неубранного после конфигуратора мусора) не менее 15 раз.

Часть моих наработок.

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

Я почему решил попробовать повесить кнопку на прерывание 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;
}

}
}

Плюс если линия от кнопки до контроллера достаточно длинная, надо озаботиться подавлением помех

Немного по подробней можно , примерно как должна выглядеть эта защита ? Изменено пользователем DmitryS
Ссылка на комментарий
Поделиться на другие сайты

У Вас все задержки дальнейшей программы из-за delay-ев. Не используйте их без особой надобности.

Для чего втыкать задержку, скажем на 100 миллисекунд, если за это время контроллер может делать что-то полезное ? А ведь delay_ms - это просто, извиняюсь за выражение, тупое зацикливание программы. Пустой цикл...

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

Уважаемый Alex , я знаю , что делей это просто задержка и что в это время МК мог бы заняться еще чем то , но я не могу придумать контроллеру задачу на это время , а ту задачу которую я ему задал , он поганец отказывается выполнять . Шутка .А по поаду 100мс , так это я пробовал бороться с помехами , потому такие большие и поставил , изначально да и сейчас можно уменьшить до 20-50мс , это антидребезг . А вот другие задержки нужны , потому как два реле которые меняют полярность на двигателе , могут в силу своих конструктивных особеностей страбатывать не одновременно , по этому я переключаю сначала эти реле , жду когда они сработают , и лишь потом другим реле пускаю на них ток . На всякий случай так сказать .

Изменено пользователем DmitryS
Ссылка на комментарий
Поделиться на другие сайты

Чтобы зафиксировать отжатие/нажатие кнопки, не обязательно его ждать. Достаточно просто поймать момент изменения уровня на ноге МК. И ловить его можно необязательно внешним прерыванием, а периодичным опросом состояния входа, к которому подключена кнопка. Даже, я бы сказал не можно, а нужно - во избежании поимки дребезга.

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

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

И я опрашиваю состояние входа . Может это надо делать как то по другому ?

Изменено пользователем DmitryS
Ссылка на комментарий
Поделиться на другие сайты

_delay_ms(1000);
while (PINB &(1<<PINB2)){}

У Вас задержка на 1 секунду (зачем, совершенно не понятно) и ожидание уровня на входе ! Это не фиксирование изменения, а просто зацикливание программы, с ожиданием уровня.
Ссылка на комментарий
Поделиться на другие сайты

У Вас задержка на 1 секунду (зачем, совершенно не понятно)
Если убрать эту задержку , то не возможно предсказать что выберется в свитче Изменено пользователем Alex
Цитата
Ссылка на комментарий
Поделиться на другие сайты

не возможно предсказать что выберется в свитче
Это как ?

В свиче выберется всегда тот пункт, значение которого имеет переменная в скобках.

Видимо, я то-то недопонимаю...

Что у Вас на PINB2 ?

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

не возможно предсказать что выберется в свитче
Это как ?

В свиче выберется всегда тот пункт, значение которого имеет переменная в скобках.

Видимо, я то-то недопонимаю...

Этот свитч мк за несколько мс прочешет несколько десятков раз , эта конструкция стоит в основном цике , т.е. она постоянно опрашивается с высокой скоростью , за время нажатия кнопки , даже кратковременное , контролер успевает несколько раз прокрутить весь цикл , т.е пока кнопка нажата даже кратковременно МК заходит в перый case выходит из него , заходит во второй case и выходит из него , итак туеву хучу раз , приходится его остановить принудительно , на какое то время .

На PINB2 кнопка , этот пин и опрашивается

Изменено пользователем DmitryS
Ссылка на комментарий
Поделиться на другие сайты

Всё понятно.

Сделайте тогда, хотя бы, так :

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;

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

Этот код нужно вынести в обработчик по таймеру и выставлять флаг нажатой кнопки. А в основном цикле уже этот флаг опрашивать, не забыв его сбросить после опроса. Тогда Вы в основном цикле, в любой момент времени, сможете узнать была ли нажата кнопка, задерживая цикл абсолютно на любое время.

Вот и вся магия...

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

Спасибо 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;
}

Изменено пользователем DmitryS
Ссылка на комментарий
Поделиться на другие сайты

особливо вот эта строчка
В переменную butt_cur заносится инвертированное значение ПИНа. Т.б. состояние кнопки (=1 - нажата, =0 - отжата).

А дальше это значение сравнивается с предыдущим. Этим самым сравнением мы и ловим изменение состояния кнопки.

надо внутри каждого case , сделать проверку , на нажата ли еще кнопка
Зачем ? Я же Вам написал готовый пример. Разберитесь с ним, он на самом деле прост до безобразия :)
Ссылка на комментарий
Поделиться на другие сайты

если кнопку долго держать нажатой , больше 1сек , выполнится следующее действие

Как уже говорили, это из-за того что вы просто проверяете уровень состояния кнопки.

Для вашей задачи, его надо не просто проверять а еще и сравнивать с предыдущим. А это предыдущее значение надо где-то хранить, в переменной.

Тогда, вместо того чтобы просто сравнить значение вы смотрите состояние кнопки - ага, нажата! А потом смотрите предыдущее состояние кнопки - если она нажата была до этого, то ничего не делать а если отпущена - вот только тогда что-то делать с ней.

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

И после всего этого сравнения не забыть скопировать текущее состояние кнопки в ту самую переменную.

Теперь этот алгоритм можно выполнять хоть 10 раз в секунду - соответствующее действие произойдет лишь однократно для нажатия и если это необходимо - для отпускания.

Учение - изучение правил. Опыт - изучение исключений.

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

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

ну мало ли...

Изменено пользователем mail_robot

Нужно делать то, что нужно. А то, что не нужно, делать не нужно. (С) Винни Пух

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

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

Причем совершенно не понимая как оно работает и с чем конфликтует.

Очень ценными являются такие идеи, которые адаптируются в проекты как ФОНОВЫЙ ПРОЦЕСС с минимальным занятием ресурсов. Особенно вычислительных. Обработка кнопок должна напоминать работу официанта в ресторане. То есть основной процесс (клиент) не должен ощущать его присутствия иначе как по результату действия.

戦う前に相手のベルトの色に注目

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

Пример с использованием таймера Т0 в Atmega8

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

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

условно тактируем МК от внутреннего RC 1 МГц

и так, подключаем все необходимые файлы к проекту, не забываем про interrupt.h

настраиваем порты как Вам нужно,

записываем единичку в нужный бит регистра TIMSK (какой бит отвечает за разрешения прерывания по событию "переполнение Т0" смотрите в даташите)

теперь запускаем таймер с делителем \256. Почему \256? потому что с таким коэффициентом деления он будет переполнятся раз в ~65мс.

теперь в обработчике прерывания от Т0 опрашиваем кнопку, один раз в ~65мс, вот и антидребезг.

если кнопка нажата выставляем флаг в единицу(флаг-любая переменная с удобным для вас именем)

в главном цикле программы проверяем флаг, если он равен единице выполняем нужный код, ВНИМАНИЕ, в конце которого НЕ забываем сбросить флаг.

На вот такой метод меня подтолкнул Геннадий(спасибо ему огромное, многому научил меня), но он делал это немножечко мудрее, как-то с двумя флагами, так и не въехал как. Хотя и описанная мною конструкция тоже очень хорошо и без сбоев работает.

Не знаеш как? Спроси у Google'а !!!

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

Первый флаг flag1 - предыдущее состояние кнопки. Второй флаг flag2 - текущее состояние кнопки.

flag1= flag2;
flag2= читаем пин кнопки.
if(flag1 && !flag2) //- момент нажатия кнопки
if(!flag1 && !flag2) //- кнопка удерживается нажатой
if(!flag1 && flag2) //- момент отпускания кнопки
if(flag1 && flag2) //- кнопка не нажата.

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

Изменено пользователем солар

Я не раздаю удочки. Я продаю рыбу.

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

Ну начнем пожалуй с простого варианта, но в общем то годного для многих задач, не требующих постоянного внимания процессора. Это задачи типа - нажал на кнопку, сработало реле. Потом отпустили кнопку, чего то ждем. В принципе это аналогично использованию функции 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

Изменено пользователем mail_robot

Нужно делать то, что нужно. А то, что не нужно, делать не нужно. (С) Винни Пух

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

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

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

Гость
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Ответить в этой теме...

×   Вставлено с форматированием.   Восстановить форматирование

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

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

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

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

Загрузка...
  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу

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