Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Таймер на Atmega8
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > MCS51, AVR, PIC, STM8, 8bit
shatov
Таймер на Atmega8.

Таймер должен считать дни, питание от 3В. При нажатии на кнопку INT0 высвечивается счет секунд (нужно для проверки работоспособности таймера), при повторном нажатии высвечиваются отсчитанные дни. В спящий режим таймер переходит при нажатии на кнопку в третий раз, либо по истечении 5 сек с момента нажатия на кнопку в первый раз:

if (secL==5) {flagbutton=0;}
if (secL>9) {secH++; secL=0; flagbutton=0;}


Также необходим reset, но не замыканием соответствующего вывода на землю, а так, чтобы человек перепутав кнопки не сбросил свое насчитанное. Поэтому Reset осуществляется при последовательном нажатии int0 и зажимании PB5, т.е. нажав только PB5 таймер не сбросить.

Два индикатора: на одном отображаются единицы (PORTC), на другом - десятки (PORTD).
Т.к. питание от двух пальчиковых батареек, то необходим power save mode, поэтому ставлю внешний часовой кварц и по нему считает timer2.
Из спящего режима МК выходит по низкому уровню на INT0.

При симуляции всё работает, но при реализации на макетной плате ничего не выходит. Не пойму ошибку в коде. На этой же плате проверялась более простая версия прошивки (файл: as_work.c). Reset подтянул через 10k к питанию, кварц выдает что то похожее на нужную частоту с размахом около 0,5В. МК никак не реагирует на нажатие кнопки (INT0). Даже если бы совпадали моменты обнуления flagbutton и нажатия, то при повторном нажатии индикатор должен был бы показать число. МК обрабатывает нажатие INT0, т.к. ток потребления увеличивается (подключал к лабораторному БП), также варьировал с питанием (от 3 до 5В). Если кнопку не трогать, БП показывает ноль по потреблению тока, значит МК находится в режиме Powre Save и при нажатие обрабатывается прерывание... но почему ничего не выводится на сегменты, не могу понять.


#include <mega8.h>
#include <delay.h>

char sec=0;
int min=0;
int hour=0;
char secH=0, secL=0;
int flagbutton=0;
char dayH=0, dayL=0;


interrupt [EXT_INT0] void ext_int0_isr(void)
{
flagbutton++;
}

interrupt [TIM2_OVF] void timer2_ovf_isr(void)
{
secL++;
if (secL==5) {flagbutton=0;}
if (secL>9) {secH++; secL=0; flagbutton=0;}
if (secH>5) {secH=0;}
if (sec>59) {min++; sec=0;}
if (min>59) {hour++; min=0;}
if (hour>23) {dayL++; hour=0;}
if (dayL>9) {dayH++; dayL=0;}
if (dayH>9) {dayH=0;}
}

void main(void)
{
PORTB=0x20;
DDRB=0x1F;
PORTD=0x04;
DDRD=0x3B;
PORTC=0x00;
DDRC=0xFF;

ASSR=0x08;
TCCR2=0x05;
TCNT2=0x00;
OCR2=0x00;

GICR|=0x40;
GIFR=0x40;

MCUCR=0xB0;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x40;

#asm("sei");

while (1)
{while (flagbutton==1)
{
switch(secL)
{
case 0:{PORTC=~0xC0; PORTB.0=0; break;}
case 1:{PORTC=~0xF9; break;}
case 2:{PORTC=~0xA4; PORTB.0=1; break;}
case 3:{PORTC=~0xB0; break;}
case 4:{PORTC=~0x99; break;}
case 5:{PORTC=~0x92; PORTB.0=1; break;}
case 6:{PORTC=~0x82; break;}
case 7:{PORTC=~0xF8; PORTB.0=0; break;}
case 8:{PORTC=~0x80; PORTB.0=1; break;}
case 9:{PORTC=~0x90; break;}
};

switch(secH)
{
case 0:{PORTD=~0x40; break;}
case 1:{PORTD=~0x79; break;}
case 2:{PORTD=~0xA0; break;}
case 3:{PORTD=~0x30; break;}
case 4:{PORTD=~0x19; break;}
case 5:{PORTD=~0x12; break;}
case 6:{PORTD=~0x02; break;}
case 7:{PORTD=~0x78; break;}
case 8:{PORTD=~0x00; break;}
case 9:{PORTD=~0x10; break;}
};
}
while (flagbutton==2)
{
switch(dayL)
{
case 0:{PORTC=~0xC0; PORTB.0=0; break;}
case 1:{PORTC=~0xF9; break;}
case 2:{PORTC=~0xA4; PORTB.0=1; break;}
case 3:{PORTC=~0xB0; break;}
case 4:{PORTC=~0x99; break;}
case 5:{PORTC=~0x92; PORTB.0=1; break;}
case 6:{PORTC=~0x82; break;}
case 7:{PORTC=~0xF8; PORTB.0=0; break;}
case 8:{PORTC=~0x80; PORTB.0=1; break;}
case 9:{PORTC=~0x90; break;}
};

switch(dayH)
{
case 0:{PORTD=~0x40; break;}
case 1:{PORTD=~0x79; break;}
case 2:{PORTD=~0xA0; break;}
case 3:{PORTD=~0x30; break;}
case 4:{PORTD=~0x19; break;}
case 5:{PORTD=~0x12; break;}
case 6:{PORTD=~0x02; break;}
case 7:{PORTD=~0x78; break;}
case 8:{PORTD=~0x00; break;}
case 9:{PORTD=~0x10; break;}
};
};

PORTD=0x04; PORTC=0x00; PORTB.0=0;
#asm("sleep");
}

}
Палыч
Цитата(shatov @ Oct 16 2013, 07:19) *
... но почему ничего не выводится на сегменты, не могу понять.
Вероятно, из-за дребезга контактов по однократному нажатию кнопки переменная flagbutton принимает значение гораздо больше единицы.
Abell
Цитата(shatov @ Oct 16 2013, 07:19) *
... питание от 3В.
... Два индикатора: на одном отображаются единицы (PORTC), на другом - десятки (PORTD).
Т.к. питание от двух пальчиковых батареек, то необходим power save mode, поэтому ставлю внешний часовой кварц и по нему считает timer2.
Из спящего режима МК выходит по низкому уровню на INT0.

При симуляции всё работает, но при реализации на макетной плате ничего не выходит. Не пойму ошибку в коде.... МК находится в режиме Powre Save и при нажатие обрабатывается прерывание... но почему ничего не выводится на сегменты, не могу понять.

Если при симуляции все работает, зачем искать ошибку в коде? Попробуйте поискать ошибку в монтаже, о грамотной разводке земли недавно тема была. Опять же, два индикатора - это порядочный ток, как питание себя ведет? Ну и наконец - у меги 8-8 нижний предел питания 2.7в, у Вас какой?
В крайнем случае, код можно проверить по кускам, отключите прерывание и проверьте циклический вывод индикации, а потом уже двигайтесь дальше. Кстати, fuse как определяете sm.gif
shatov
Цитата(Палыч @ Oct 15 2013, 18:30) *
Вероятно, из-за дребезга контактов по однократному нажатию кнопки переменная flagbutton принимает значение гораздо больше единицы.


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

Цитата(Abell @ Oct 15 2013, 18:41) *
Если при симуляции все работает, зачем искать ошибку в коде? Попробуйте поискать ошибку в монтаже, о грамотной разводке земли недавно тема была. Опять же, два индикатора - это порядочный ток, как питание себя ведет? Ну и наконец - у меги 8-8 нижний предел питания 2.7в, у Вас какой?
В крайнем случае, код можно проверить по кускам, отключите прерывание и проверьте циклический вывод индикации, а потом уже двигайтесь дальше. Кстати, fuse как определяете sm.gif


На одной и той же отладочной плате проверяю две прошивки. Ту, что привел, и ту, что попроще:

#include <mega8.h>
#include <delay.h>

char sec=0;
int min=0;
int hour=0;
int day=0;
char secH=0, secL=0;
int flagbutton=0;

interrupt [EXT_INT0] void ext_int0_isr(void)
{
flagbutton=1;
}

interrupt [TIM2_OVF] void timer2_ovf_isr(void)
{
TCNT2=0x00;
secL++;
if (secL>9) {secH++; secL=0;}
if (secH>5) {secH=0;}
if (sec>59) {min++; sec=0;}
if (min>59) {hour++; min=0;}
if (hour>23) {day++; hour=0;}
if (day>99) {day=0;}

}


void main(void)
{

PORTB=0x00;
DDRB=0x3F;
PORTD=0x04;
DDRD=0xFB;
PORTC=0x00;
DDRC=0x01;

ASSR=0x08;
TCCR2=0x05;
TCNT2=0x00;
OCR2=0x00;

GICR|=0x40;
GIFR=0x40;

MCUCR=0xB0;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x40;

#asm("sei")

while (1)
{
if (flagbutton==1)
{switch(secL)
{
case 0:{PORTB=~0xC0; break;}
case 1:{PORTB=~0xF9; break;}
case 2:{PORTB=~0xE4; PORTC.0=1; break;}
case 3:{PORTB=~0xF0; PORTC.0=1; break;}
case 4:{PORTB=~0xD9; PORTC.0=1; break;}
case 5:{PORTB=~0xD2; PORTC.0=1; break;}
case 6:{PORTB=~0xC2; PORTC.0=1; break;}
case 7:{PORTB=~0xF8; PORTC=0x00; break;}
case 8:{PORTB=~0xC0; PORTC.0=1; break;}
case 9:{PORTB=~0xD0; PORTC.0=1; break;}
};

switch(secH)
{
case 0:{PORTD=~0x40; break;}
case 1:{PORTD=~0x79; break;}
case 2:{PORTD=~0xA0; break;}
case 3:{PORTD=~0x30; break;}
case 4:{PORTD=~0x19; break;}
case 5:{PORTD=~0x12; break;}
case 6:{PORTD=~0x02; break;}
case 7:{PORTD=~0x78; break;}
case 8:{PORTD=~0x00; break;}
case 9:{PORTD=~0x10; break;}
};
}

flagbutton=0;
delay_ms(10);
if (flagbutton==0)
{PORTB=0x00; PORTD=0x04; PORTC.0=0x00;}

#asm("sleep");
}}

Эта работает как надо. Фьюзы в обоих случаях ставлю одинаково:

Abell
Цитата(shatov @ Oct 16 2013, 09:17) *
На одной и той же отладочной плате проверяю две прошивки. Ту, что привел, и ту, что попроще:

while (1)
{
if (flagbutton==1)
{switch(secL)
{
};

switch(secH)
{
};
}

flagbutton=0;
delay_ms(10);
if (flagbutton==0)
{PORTB=0x00; PORTD=0x04; PORTC.0=0x00;}

#asm("sleep");
}}

Извиняюсь спросить, а если в первом варианте, который не работает, после свичей тоже обнулять flagbutton, и тоже выдержку поставить, изменения будут? Или я просто не заметил, где обнуление впендюрено и сколько времени индикатор отрабатывает, прежде чем контроллер заснет - тогда извиняюсь laughing.gif ...
shatov
Цитата(Abell @ Oct 16 2013, 01:27) *
Извиняюсь спросить, а если в первом варианте, который не работает, после свичей тоже обнулять flagbutton, и тоже выдержку поставить, изменения будут? Или я просто не заметил, где обнуление впендюрено и сколько времени индикатор отрабатывает, прежде чем контроллер заснет - тогда извиняюсь laughing.gif ...


обнуление каждые пять секунд

Проблема в строке: flagbutton++;
Но т.к. дребезг был исключен замыканием INT0 на землю, то совсем не ясно, почему нет увеличения переменной по нажатию...
Abell
Цитата(shatov @ Oct 16 2013, 15:18) *
обнуление каждые пять секунд

Проблема в строке: flagbutton++;
Но т.к. дребезг был исключен замыканием INT0 на землю, то совсем не ясно, почему нет увеличения переменной по нажатию...

Да, уже разглядел. Ну как-то надо исключать дребезг, аппаратно или программно laughing.gif А пока, если вообще отключить прерывание и присвоить flagbutton значение 1, то контроллер запускается и на индикаторе идет отсчет 5 секунд, правильно понял?
Палыч
Цитата(shatov @ Oct 16 2013, 15:18) *
Проблема в строке: flagbutton++;

Не в самой строке, но - близко. Вы настроили прерывание по int0 на низкий уровень, и этот уровень в настоящее время постоянно присутствует - прерывания генерятся, переменная наращивается... Нажатие кнопки, как правило, фиксируют по перепаду уровня.
shatov
Цитата(Abell @ Oct 16 2013, 02:23) *
Да, уже разглядел. Ну как-то надо исключать дребезг, аппаратно или программно laughing.gif А пока, если вообще отключить прерывание и присвоить flagbutton значение 1, то контроллер запускается и на индикаторе идет отсчет 5 секунд, правильно понял?


Даже если в прерывании записать flagbutton=1, то при нажатии кнопки будут отображаться 5 сек счета. Поняли правильно.

Цитата(Палыч @ Oct 16 2013, 02:31) *
Не в самой строке, но - близко. Вы настроили прерывание по int0 на низкий уровень, и этот уровень в настоящее время постоянно присутствует - прерывания генерятся, переменная наращивается... Нажатие кнопки, как правило, фиксируют по перепаду уровня.


Сделал MCUCR=0xB1, т.е. настроил на перепад, перемычку заменил кнопкой, но ничего не изменилось, индикаторы молчат.

Цитата(Палыч @ Oct 16 2013, 02:31) *
Не в самой строке, но - близко. Вы настроили прерывание по int0 на низкий уровень, и этот уровень в настоящее время постоянно присутствует - прерывания генерятся, переменная наращивается... Нажатие кнопки, как правило, фиксируют по перепаду уровня.


Но я понял, что исключив дребезг - зациклил программу. Тогда нужно как то избавляться от дребезга. Пробовал сделать задержку в прерывании (10ms), но не помогло. Что можно еще сделать? Поставить параллельно 10нФ и последовательно резистор около 300Ом?
Abell
Цитата(shatov @ Oct 16 2013, 15:58) *
Тогда нужно как то избавляться от дребезга. Пробовал сделать задержку в прерывании (10ms), но не помогло. Что можно еще сделать? Поставить параллельно 10нФ и последовательно резистор около 300Ом?

Как вариант. Или в обработчике прерывания сразу же запрещать INT0, генерить задержку по свободному таймеру и разрешать. Прерывание, естественно, по фронту.
Палыч
Цитата(shatov @ Oct 16 2013, 15:58) *
Сделал MCUCR=0xB1, т.е. настроил на перепад

Логичнее было бы применить "The falling edge of INT0 generates an interrupt request", т.е. прерывание по спаду.

PS. Чтобы не мучать ни себя ни других людей, которые вызовутся Вам помочь, возмите себе за правило записывать значения в регистры таким образом:
Код
MCUCR= (1<<SE) | (1<<SM1) | (1<<SM0) | (1<<ISC10);
Tarbal
Дребезг исключается другим образом.
Во первых не следует использовать прерывание для чтения кнопки.

Следует сделать автомат на 4 состояния и вызывать его каждые 10 миллисекунд



void state_machine()
{
static int timeout;
static int state = 0;

switch(state)
{
default:
case 0:
if(button_pressed())
{
state = 1;
timeout = 5;
}
break;


case 1:
if(--timeout == 0)
{
if(button_pressed())
{
state = 2;
/* buton was pressed call button press handler */
buton_press_action();
}
else
{
state = 0;
}
}
break;

case 2:
if(button_pressed())
{
state = 3;
timeout = 5;
}
break;

if(--timeout == 0)
{
if(button_pressed())
{
state = 2;
}
else
{
state = 0;
/* buton was released -- call button press handler */
buton_release_action();
}
}
break;
}
}
alexeyv
2 Tarbal:
Где case 3: ??
Abell
Цитата(shatov @ Oct 16 2013, 15:58) *
Даже если в прерывании записать flagbutton=1, то при нажатии кнопки будут отображаться 5 сек счета. Поняли правильно.

Сделал MCUCR=0xB1, т.е. настроил на перепад, перемычку заменил кнопкой, но ничего не изменилось, индикаторы молчат.

Но я понял, что исключив дребезг - зациклил программу. Тогда нужно как то избавляться от дребезга. Пробовал сделать задержку в прерывании (10ms), но не помогло. Что можно еще сделать? Поставить параллельно 10нФ и последовательно резистор около 300Ом?

От дребезга избавитесь потом, когда станет понятно, что только он - источник всех бед. Хорошо, если при этом станет понятно, что алгоритм работы устройства следует радикально пересмотреть. В частности, совершенно нет обработки критических ситуаций, ошибок.
Микроконтроллер за Вас додумывать не будет, ему надо четко расписать, как себя вести в разных ситуациях sm.gif Переменная flagbutton должна принимать только те значения, которые можно корректно обработать. Ну хотя бы в обработчик прерывания вставьте if, который при переполнении flugbutton будет сбрасывать в 0. В этом случае даже при сильном дребезге у Вас будет веселое перемигивание индикаторов, но оно - будет!
А уж как дребезг устранить - дело десятое, повторяю - хоть программно, хоть аппаратно.
Палыч
Цитата(Tarbal @ Oct 18 2013, 06:25) *
Во первых не следует использовать прерывание для чтения кнопки.

IMHO, вредный совет для разработки устройств с батарейным питанием.
Tarbal
Цитата(alexeyv @ Oct 18 2013, 07:11) *
2 Tarbal:
Где case 3: ??

Sorry

case 2:
if(button_pressed())
{
state = 3;
timeout = 5;
}
break;

case 3:
if(--timeout == 0)

Цитата(Палыч @ Oct 18 2013, 08:55) *
IMHO, вредный совет для разработки устройств с батарейным питанием.


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

Цитата(shatov @ Oct 16 2013, 07:19) *
При симуляции всё работает, но при реализации на макетной плате ничего не выходит.


При симуляции у Вас нет дребезжащей кнопки, поэтому условие сравнивания количества нажатий с единицей проходит. В реальности количество нажатий больше единицы и сравнение с единиицей не выстреливает.
Палыч
Цитата(Tarbal @ Oct 18 2013, 15:28) *
Это почему это?
От прерывания можно просыпаться, но использовать его для обработки нажатия дребезжащей кнопки очень нехорошо.

Чтобы не "создавать ненужные сущности".
Я понимаю, что кому то нравиться поллинг, кому то прерывания... Но, коль в данном случае мы вынуждены пользоваться прерыванием, то накладывать на него ещё и поллинг - это и есть "ненужные сущности". Методы борьбы с дребезгом кнопки, подключенной к линии генерации прерывания int0 - банальны: аккуратно запретить прерывание от int0 на время несколько бОльшее, чем время дребезга.

to TC
Прошу прощения, выше я Вам дал "вредный" совет - использовать прерывание по спаду для фиксации факта нажатия на кнопку. Дело в том, что выход из "Idle mode" возможен только по уровню, но никак не по перепаду...
Tarbal
Цитата(Палыч @ Oct 18 2013, 16:45) *
Чтобы не "создавать ненужные сущности".
Я понимаю, что кому то нравиться поллинг, кому то прерывания... Но, коль в данном случае мы вынуждены пользоваться прерыванием, то накладывать на него ещё и поллинг - это и есть "ненужные сущности". Методы борьбы с дребезгом кнопки, подключенной к линии генерации прерывания int0 - банальны: аккуратно запретить прерывание от int0 на время несколько бОльшее, чем время дребезга.

to TC
Прошу прощения, выше я Вам дал "вредный" совет - использовать прерывание по спаду для фиксации факта нажатия на кнопку. Дело в том, что выход из "Idle mode" возможен только по уровню, но никак не по перепаду...


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


Палыч, а хотите я расскажу вам про приоритеты прерываний то, что я знаю давно, а вы не знаете.
Палыч
Цитата(Tarbal @ Oct 18 2013, 18:03) *
Я применяю то, что больше подходит.
И, это - правильно. Полностью с Вами согласен.
Цитата(Tarbal @ Oct 18 2013, 18:03) *
Манипуляция с запретом прерываний на время это беда.
Возможно, вы недопоняли... Речь шла не о полном (глобальном) запрете прерываний, а о врЕменном запрете только прерывания INT0,
Цитата(Tarbal @ Oct 18 2013, 18:03) *
Палыч, а хотите я расскажу вам про приоритеты прерываний то, что я знаю давно, а вы не знаете.
Если Вы знаете что-то такое о прерываниях в AVR, что не отражено в документации производителя, то - расскажите. Эта информация, вероятно, будет полезна многим. Наверное, уместнее будет это сделать в специально созданной для этого теме.
Tarbal
Цитата(Палыч @ Oct 18 2013, 20:45) *
И, это - правильно. Полностью с Вами согласен.
Возможно, вы недопоняли... Речь шла не о полном (глобальном) запрете прерываний, а о врЕменном запрете только прерывания INT0,

Проходили это. Не нашли хорошего решения.


Цитата(Палыч @ Oct 18 2013, 20:45) *
Если Вы знаете что-то такое о прерываниях в AVR, что не отражено в документации производителя, то - расскажите. Эта информация, вероятно, будет полезна многим. Наверное, уместнее будет это сделать в специально созданной для этого теме.

Да я просто выпендриться захотел сгоряча. Однако есть один тонкий момент с пониманием приоритета прерывания, о котором мало кто думает. Знать может и знают, но не осознают.
Дело в том, что в мелких микропроцессорах если обрабатывается прерывание, другое прерывание вызвать обработчик не сможет. Однако есть такое понятие как приоритет прерываний. Что оно означает? Оно означает в каком порядке будут вызываться прерывания, которые ждали обработки, пока либо обрабатывалось прерывание, либо прерывания были запрещены.
Однако в более мощных процессорах есть приоритет прерывания, означающий, что прерывание может прервать обработчик низкоприоритетного прерывания. Таким образом есть два различных понятия, которые называются одинаково.
shatov
Решил переделать программно, поставив вторую кнопку на INT1. Всё заработало.

#include <mega8.h>
#include <delay.h>

char sec=0;
int min=0;
int hour=0;
char secH=0, secL=0;
int flagbutton0=0, flagbutton1=1;
char dayH=0, dayL=0;

void(* resetFunc) (void) = 0; // Reset MC function

interrupt [EXT_INT0] void ext_int0_isr(void)
{
flagbutton0=1;
flagbutton1=0;
if (PIND.2==0&&PIND.3==0) {resetFunc();}
}

interrupt [EXT_INT1] void ext_int1_isr(void)
{
flagbutton1=1;
flagbutton0=0;
if (PIND.2==0&&PIND.3==0) {resetFunc();}
}

interrupt [TIM2_OVF] void timer2_ovf_isr(void)
{
secL++;
if (secL>9) {secH++; secL=0; flagbutton0=0; flagbutton1=0;}
if (secH>5) {secH=0;}
if (sec>59) {min++; sec=0;}
if (min>59) {hour++; min=0;}
if (hour>23) {dayL++; hour=0;}
if (dayL>9) {dayH++; dayL=0;}
if (dayH>9) {dayH=0;}
}




void main(void)
{

PORTB=0x20;
DDRB=0x1F;
PORTD=0x0C;
DDRD=0xF3;
PORTC=0x00;
DDRC=0xFF;

ASSR=0x08;
TCCR2=0x05;
TCNT2=0x00;
OCR2=0x00;

GICR|=0xC0;
MCUCR=0xB1;
GIFR=0xC0;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x40;

#asm("sei");

while (1)
{while (flagbutton0==1)
{
switch(secL)
{
case 0:{PORTD=~0x40; PORTB.1=1; break;}
case 1:{PORTD=~0x71; PORTB.1=0; break;}
case 2:{PORTD=~0xA0; PORTB.1=1; break;}
case 3:{PORTD=~0x30; PORTB.1=1; break;}
case 4:{PORTD=~0x11; PORTB.1=0; break;}
case 5:{PORTD=~0x12; PORTB.1=1; break;}
case 6:{PORTD=~0x02; PORTB.1=1; break;}
case 7:{PORTD=~0x70; PORTB.1=0; break;}
case 8:{PORTD=~0x00; PORTB.1=1; break;}
case 9:{PORTD=~0x10; PORTB.1=1; break;}
};
}
while (flagbutton1==1)
{ switch(secH)
{
case 0:{PORTD=~0x40; PORTB.1=1; break;}
case 1:{PORTD=~0x71; PORTB.1=0; break;}
case 2:{PORTD=~0xA0; PORTB.1=1; break;}
case 3:{PORTD=~0x30; PORTB.1=1; break;}
case 4:{PORTD=~0x11; PORTB.1=0; break;}
case 5:{PORTD=~0x12; PORTB.1=1; break;}
case 6:{PORTD=~0x02; PORTB.1=1; break;}
case 7:{PORTD=~0x70; PORTB.1=0; break;}
case 8:{PORTD=~0x00; PORTB.1=1; break;}
case 9:{PORTD=~0x10; PORTB.1=1; break;}
};
}

PORTD=0x0C; PORTC=0x00; PORTB.0=0; PORTB.1=0;
#asm("sleep");
}
}

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

P.S. Не хотел бы, чтобы моё сообщение послужило прерыванием дискуссии между Палыч и Tarbal "О создании ненужных сущностей"

в конце немного укоротил программу, т.к. для проверки работоспособности проще выводить ход секунд, чтобы не ждать, и убрал один индикатор, но суть осталась та же
Палыч
Цитата(Tarbal @ Oct 18 2013, 22:04) *
...есть два различных понятия, которые называются одинаково.

Это Вы - погорячились. Приоритет - он, как говорится "и в Африке - приоритет". А, вот - реализация механизма прерываний в разных контроллерах/процессорах, действительно - разная... И хотя при описании работы этих разных механизмов используется термин "приоритет", однако его значение (а, термин "приоритет" означает: "старшинство", "важность", "первенство") не меняется.

Цитата(Tarbal @ Oct 18 2013, 22:04) *
Не нашли хорошего решения.
Это - не значит, что его не существует.
Так уж сложилось, что в проектируемых мной устройствах кнопок, обычно, - "воз и маленькая тележка". Из-за необходимости экономить дефицитные ноги МК, они (кнопки) собираются в матрицу. Наилучшее решение в этом случае - применить поллинг для определения изменения их состояния. В тех немногочисленных устройствах, где кнопок немного и есть возможность "посадить" каждую из них на "свою" ногу МК поллинг используется уже "по-привычке", как хорошо отработанный приём работы с кнопками... Однако, было и устройство, где необходимо было довольно точно определять момент нажатия на кнопку (точнее - это был концевик движения суппорта). Кнопка (концевик) была "посажена" на линию прерывания, ну, а устранение дребезга описал выше Abell:
Цитата
в обработчике прерывания сразу же запрещать INT0, генерить задержку по свободному таймеру и разрешать
Единственное, что можно было бы добавить к этому описанию антидребезга: после запрета прерывания INT0 - сбросить флаг INTF0, который мог быть взведен внешним сигналом за интервал времени, прошедший между автоматической очисткой этого флага при входе в прерывание и моментом времени запрета прерывания.
Tarbal
Цитата(shatov @ Oct 18 2013, 23:10) *
P.S. Не хотел бы, чтобы моё сообщение послужило прерыванием дискуссии между Палыч и Tarbal "О создании ненужных сущностей"


sm.gif Я дискуссию закончил.
Abell
Цитата(shatov @ Oct 18 2013, 23:10) *
Решил переделать программно, поставив вторую кнопку на INT1. Всё заработало.

interrupt [EXT_INT0] void ext_int0_isr(void)
{
flagbutton0=1;
flagbutton1=0;
if (PIND.2==0&&PIND.3==0) {resetFunc();}
}

interrupt [EXT_INT1] void ext_int1_isr(void)
{
flagbutton1=1;
flagbutton0=0;
if (PIND.2==0&&PIND.3==0) {resetFunc();}
}

interrupt [TIM2_OVF] void timer2_ovf_isr(void)
{
secL++;
if (secL>9) {secH++; secL=0; flagbutton0=0; flagbutton1=0;}
if (secH>5) {secH=0;}
if (sec>59) {min++; sec=0;}
if (min>59) {hour++; min=0;}
if (hour>23) {dayL++; hour=0;}
if (dayL>9) {dayH++; dayL=0;}
if (dayH>9) {dayH=0;}
}

Вот это правильно sm.gif только зачем две переменных завели, когда достаточно одной?

interrupt [EXT_INT0] void ext_int0_isr(void)
{
flagbutton=1;
if (PIND.2==0&&PIND.3==0) {resetFunc();}
}

interrupt [EXT_INT1] void ext_int1_isr(void)
{
flagbutton=2;
if (PIND.2==0&&PIND.3==0) {resetFunc();}
}

а в обработчике по таймеру после вывода индикации сбрасываете в 0, и все laughing.gif
Палыч
Цитата(Abell @ Oct 19 2013, 09:37) *
Вот это правильно

Гм-м-м.... Правильно? Ужасно! Лучше бы уж тогда ТС внял советам Tarbal и использовал бы прерывание для выхода из Idle, а состояние кнопки определял поллингом... ТС не привел текст программы к удобоваримому виду, лазить по DS и переводить константы - лень, то - "наглазок":
1. Вторая кнопка введена в устройство только лишь для того, чтобы исправить пробелы в программировании ТС. Функциональной необходимости в ней нет.
2. Кнопка, подключенная на int0, не выведет МК из спячки, поскольку прерывание настроено на перепад сигнала из высокого уровня в низкий.
3. Прерывания на int1, настроены на низкий уровень сигнала, поэтому при нажатии и удержании в нажатом состоянии подключенной к int1 кнопки будут формироваться множественные прерывания по int1. Фактически при выходе из прерывания МК снова войдет в процедуру прерывания по int1.
4. Вызов resetFunc() переведёт МК в состояние близкое к состоянию "по включению питания", при этом обнулятся переменные, ради подсчета которых и затевалась разработка устройства. Это - такая задумка: сбросить показания счетчика при нажатии двух кнопок? Тогда, возможно, смысл во второй кнопке есть...
5. Перевод устройства в "исходное" состояние (когда оба флага равны нулю) производится таймером при переходе единиц секунд через ноль (т.е. когда secL>9). Поскольку нажатие на кнопку производится в случайный момент времени, то время до перехода в "исходное" будет варьироваться от 9 до 1 сек.
6. Переход в спящий режим осуществляется в конце каждого цикла main loop. Поэтому индикация будет осуществляться исключительно потому, что непрерывно происходят прерывания по int1, которые из спящего режима МК выводят (см. п.3)
7. ...пожалуй, приведённого выше - достаточно...
ae_
Цитата(shatov @ Oct 19 2013, 04:10) *
...
char sec=0;
...
char secH=0, secL=0;
...
secL++;
if (secL>9) {secH++; secL=0; flagbutton0=0; flagbutton1=0;}
if (secH>5) {secH=0;}
if (sec>59) {min++; sec=0;}
...

У Вас объявлены sec,secL,secH, но sec нигде далее в программе не изменяется, значит и проверка "if (sec>59) {min++; sec=0;}" никогда не выполнится.
secL,secH - будут крутиться, а минуты, часы и дни - не будут.
smk
Вот такую строчку допишите: #include <avr/interrupt.h>
А Вы наверно в кодевижине... тогда не надо.
Abell
Цитата(Палыч @ Oct 19 2013, 10:53) *
Гм-м-м.... Правильно? Ужасно! Лучше бы уж тогда ТС внял советам Tarbal и использовал бы прерывание для выхода из Idle, а состояние кнопки определял поллингом...
1. Вторая кнопка введена в устройство только лишь для того, чтобы исправить пробелы в программировании ТС. Функциональной необходимости в ней нет.
2. Кнопка, подключенная на int0, не выведет МК из спячки, поскольку прерывание настроено на перепад сигнала из высокого уровня в низкий.
3. Прерывания на int1, настроены на низкий уровень сигнала, поэтому при нажатии и удержании в нажатом состоянии подключенной к int1 кнопки будут формироваться множественные прерывания по int1. Фактически при выходе из прерывания МК снова войдет в процедуру прерывания по int1.
4. Вызов resetFunc() переведёт МК в состояние близкое к состоянию "по включению питания", при этом обнулятся переменные, ради подсчета которых и затевалась разработка устройства. Это - такая задумка: сбросить показания счетчика при нажатии двух кнопок? Тогда, возможно, смысл во второй кнопке есть...
5. Перевод устройства в "исходное" состояние (когда оба флага равны нулю) производится таймером при переходе единиц секунд через ноль (т.е. когда secL>9). Поскольку нажатие на кнопку производится в случайный момент времени, то время до перехода в "исходное" будет варьироваться от 9 до 1 сек.
6. Переход в спящий режим осуществляется в конце каждого цикла main loop. Поэтому индикация будет осуществляться исключительно потому, что непрерывно происходят прерывания по int1, которые из спящего режима МК выводят (см. п.3)
7. ...пожалуй, приведённого выше - достаточно...

ТС скорее всего только начинает свой трудный путь в освоении микроконтроллеров sm.gif Поэтому, как мне кажется, применить две кнопки - правильное направление, это и имел в виду. Если есть свободные порты - почему бы не использовать? Тем более, применением поллинга полностью гарантировать отсутствие дребезга не удастся, ведь в какой-то момент опроса там может быть и 0 sm.gif В конечном итоге автор сам придет к правильным выводам.
Применить два флага, когда достаточно одного - вот это неправильно, но опять-таки, это проблемы автора.
Насчет корявости кода - полностью согласен, это беда всех начинающих, постепенно с опытом проходит. Если он будет нарабатываться, этот опыт. Ну мы же не будем писать для автора темы программу, за бесплатно, верно? sm.gif
Пока же, как мне кажется, автор слабо представляет себе, как должно работать устройство и как объяснить это микроконтроллеру на понятном ему языке, ну это пройдет.
Вообще, последнее время все чаще приходится сталкиваться с самодельными разработками, клиенты воют - просят переделать, такого насмотришься, что и этот мелкий проект вполне достойно смотрится laughing.gif Еще один прикол сам не видел, но рассказывали, что кое-кто для AVR на бейсике пишет, вот это был шок, да...
Палыч
Цитата(Abell @ Oct 19 2013, 12:40) *
применением поллинга полностью гарантировать отсутствие дребезга не удастся, ведь в какой-то момент опроса там может быть и 0
При программном антидребезге что при использовании прерываний, что при использовании поллинга метод отсечения дребезга одинаков - после фиксации изменения состояния на линии ввода на некоторое время прекратить анализ состояния. При использовании прерываний этот анализ выполняет аппаратура, при поллинге - программа.


Цитата(Abell @ Oct 19 2013, 12:40) *
ТС скорее всего только начинает свой трудный путь в освоении микроконтроллеров
Ага, очень похоже на лаб работу по дисциплине "Микропроцессорная техника" biggrin.gif
Tarbal
Я многие годы делаю так:

1. Настраиваю таймер вызывать обработчик прерывания раз в 10 мС
2. при входе делаю только rt_cnt++;
3. вечный цикл:

santi_seconds = 100;
while(1)
{
while(rt_cnt==0)
{}
rt_cnt--;
//Here do each 10 ms action

if(--santi_seconds==0)
{
//Here do each second action
santi_seconds = 100;
if(++seconds==60)
{
seconds = 0;
if(++minutes==60)
{
minutes = 0;
//Here do each hour action

}
//Here do each minute action
}

}

}

Возможны опечатки. Сходу написал.
Abell
Цитата(Tarbal @ Oct 20 2013, 19:57) *
Я многие годы делаю так:

1. Настраиваю таймер вызывать обработчик прерывания раз в 10 мС
2. при входе делаю только rt_cnt++;
3. вечный цикл:

santi_seconds = 100;
while(1)
{
while(rt_cnt==0)
{}
rt_cnt--;
//Here do each 10 ms action

if(--santi_seconds==0)
{
//Here do each second action
santi_seconds = 100;
if(++seconds==60)
{
seconds = 0;
if(++minutes==60)
{
minutes = 0;
//Here do each hour action

}
//Here do each minute action
}

}

}

Возможны опечатки. Сходу написал.

Ему спать хочется, а по внешнему прерыванию просыпаться sm.gif Контроллеру, имею ввиду. А, извиняюсь спросить, английский для Вас родной?
ILYAUL
Во ,разведка вопрос задал wacko.gif Тут только носители языка
Tarbal
Цитата(Abell @ Oct 21 2013, 16:50) *
Ему спать хочется, а по внешнему прерыванию просыпаться sm.gif Контроллеру, имею ввиду. А, извиняюсь спросить, английский для Вас родной?


А как вы за временем следите?
Русский родной, но на клавиатуре нет русских букв.
Abell
Цитата(Tarbal @ Oct 21 2013, 20:21) *
А как вы за временем следите?

А я и не слежу laughing.gif Автор темы следит sm.gif
Tarbal
Цитата(Abell @ Oct 21 2013, 20:56) *
А я и не слежу laughing.gif Автор темы следит sm.gif


Очень смешно. А как одновременно спать и за временем следить?
Палыч
Цитата(Tarbal @ Oct 21 2013, 20:57) *
А как одновременно спать и за временем следить?

Замечание было о том, что "просыпаться" 100 раз в секунду энергетически более расточительно, чем 1 раз в секунду, как это сделано у ТС. Кроме того, Ваш фрагмент кода не содержит инструкций (или намёка на место, куда их вставить) перевода МК в Sleep.
Tarbal
Цитата(Палыч @ Oct 21 2013, 22:54) *
Замечание было о том, что "просыпаться" 100 раз в секунду энергетически более расточительно, чем 1 раз в секунду, как это сделано у ТС. Кроме того, Ваш фрагмент кода не содержит инструкций (или намёка на место, куда их вставить) перевода МК в Sleep.


Я пример привел, как делать, а детали каждый сам выберет. Но весельчак Abell ни о каком пробуждении ежесекундном не говорит. Tолько о кнопке.
Это концепция измерения времени. Инструкцию sleep в конце вечного цикла последней командой.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.