Обработка нажатия кнопок на Atmega8

Здесь принимаются все самые невообразимые вопросы... Главное - не стесняйтесь. Поверьте, у нас поначалу вопросы были еще глупее :)
Ответить
Родился
Сообщения: 13
Зарегистрирован: Ср апр 15, 2020 12:28:32

Сообщение andreybolo »

Всем привет!
Борюсь с дребезжанием кнопок, мне нужно увеличивать уставку температуры нажатием кнопки. Кучу времени потратил, чтобы разобраться, но пока до конца не уверен в решении.
Как я понял часто применяют задержку __delay_ в несколько миллисекунд. Но мне такая практика не нравится, так как если на каждую кнопку ставить задержку то контроллер только и будет что ждать и ничего не делать.
Хочу аппаратно сделать обработку нажатия кнопки, простых решений не нашел, придумал свой велосипед) Посмотрите пожалуйста, насколько такая обработка нажатия (посредство использования таймера) вообще логична и правильна? В целом сейчас все работает как и хотел, но не очень стабильно, поэтому хочется услышать ваше мнение.

unsigned char timer_00(void)
{
TCCR0 &= ~(1<<CS01);
TCCR0 |=(1<<CS02)|(1<<CS00); // делим на 1024
if (TCNT0 == 100) // считает до 100 и обнуляется
{
b1++;
TCNT0 = 0;
}
if (b1 == 30) // переменная для подсчета количества переполнений, обнуляем после 30
{

b1=0;
}
return b1; // возвращаем количество переполнений счетчика (информационно)
}

while(1)
{
unsigned int zx=timer_00();
setpos(5,1);
vivod_chisla(zx);

PORTC|=(1<<5); //ПОДТЯГИВАЮЩИЙ РЕЗИСТОР НА КНОПКУ

if(~PINC & (1<<5))// проверяем нажатие кнопки
{
if(TCNT0==50) // если кнопка нажата и значение в таймере 50, увеличить уставку на 1
{
T_ustavka++;
}

}
setpos(10,1);
vivod_chisla(T_ustavka);

}
Реклама
Собутыльник Кота
Аватара пользователя
Сообщения: 2723
Зарегистрирован: Пт сен 07, 2018 20:20:02
Откуда: деревня в Тульской губернии

Сообщение ПростоНуб »

Если МК нужно над чем-то трудится параллельно опросу кнопок, то все делается на прерываниях и кольцевом буфере сообщений.
Идея следующая.
В прерывании по таймеру каждые N миллисекунд инкрементируем некоторую беззнаковую глобальную переменную t.
В прерывании по фронту с порта с кнопкой сверяем значение t со значением статической переменной ts. Если разница между ними, с учетом переполнения переменной t, оказывается больше M, то помещаем в кольцевой буфер сообщение о нажатии кнопки. После чего сохраняем в переменной ts новое значение t.

N подбирается таким, чтобы его можно было использвоать не только для опроса кнопки, но и в иных целях в программе. Если N = 10мс, а максимальная частота нажатия клавиш 100мс, то M должно быть равно 10.

Основная программа МК в цикле проверяет наличие сообщений в кольцевом буфере и их последовательно в фоне обрабатывает. На практике, кольцевого буфера в 16 байт мне почти всегда хватает.
Реклама
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18786
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

andreybolo писал(а):если на каждую кнопку ставить задержку то контроллер только и будет что ждать и ничего не делать.
вообще-то, при работе с кнопками он и так ничего не делает другого, кроме как кнопки обрабатывает :)

я вот недавно накорябал вот такую пару функций С ЗАДЕРЖКАМИ - и они совершенно никому и ничему не мешают:

Код: Выделить всё

/**
 * опрос пинов порта, к которому подключены кнопки
 * @return битовое состояние кнопок
 */
static uint8_t get_pins(void){
	uint8_t pin = ~PINC & 0x7F;
	_delay_ms(10);
	if (pin != (~PINC & 0x7F))
		return 0;
	else
		return pin;
}

#define DELAY	30

/**
 * генерация событий меню для навигации
 * @return событие меню
 */
menu_event_t get_event(void){
	static menu_event_t key = MEV_NONE;
	static uint8_t prev;
	static uint8_t delay = DELAY;
	uint8_t pin = get_pins();

	if(pin == 0){
		delay = DELAY;
		key = MEV_NONE;
	} else if(pin != prev){
		switch(pin){
		case 1: key = MEV_NEXT; break;
		case 2: key = MEV_DEC; break;
		case 4: key = MEV_ENTER; break;
		case 8: key = MEV_INC; break;
		case 16: key = MEV_ESCAPE; break;
		case 32: key = MEV_PREV; break;
		default:
			key = MEV_NONE;
			delay = DELAY;
		}
	} else {
		// реализован простейший автоповтор с ускорением: чем больше удерживается кнопка, тем чаще генерируется событие
		for(uint8_t i=delay; i && (get_pins() == pin); i--) _delay_ms(10);
		if(get_pins() == pin){
			if(delay > 10) delay -= 5;
			return key;
		}
		else key = MEV_NONE;
	}
	prev = pin;
	return key;
}
конечно, это плохой код - магические числа и т.п., но писал сгоряча, торопясь и не задумываясь... вот под такую схему:
Изображение
Вложения
snip_20200509165810.png
(22.65 КБ) 1029 скачиваний
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Родился
Сообщения: 13
Зарегистрирован: Ср апр 15, 2020 12:28:32

Сообщение andreybolo »

ПростоНуб писал(а):Если МК нужно над чем-то трудится параллельно опросу кнопок, то все делается на прерываниях и кольцевом буфере сообщений.
Спасибо, идея в принципе понятна, попробую реализовать. В отличие от Вас я правда нуб в этом деле, хотел избежать прерываний)) Получается нужно Все кнопки подключить через внешние прерывания и по фронту их обрабатывать?
ARV писал(а):вообще-то, при работе с кнопками он и так ничего не делает другого, кроме как кнопки обрабатывает

Ну вот в моей программке, контроллер не зацикливается на кнопке, а параллельно считает остальные нули и единицы в основном цикле. Мне в конечном итоге хочется ПИД регулятор сделать и если я правильно понимаю, при входе в режим задержки кнопки, он не будет опрашивать АЦП (датчик температуры) и выдавать сигнал на выход.
Реклама
Эиком - электронные компоненты и радиодетали
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18786
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

andreybolo писал(а):Ну вот в моей программке, контроллер не зацикливается на кнопке, а параллельно считает остальные нули и единицы в основном цикле. Мне в конечном итоге хочется ПИД регулятор сделать и если я правильно понимаю, при входе в режим задержки кнопки, он не будет опрашивать АЦП (датчик температуры) и выдавать сигнал на выход.
понимаете все правильно, а вот логически построили программу не правильно.

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

Мой уютный бложик... заходите!
Контактная информация:
Реклама
Собутыльник Кота
Аватара пользователя
Сообщения: 2723
Зарегистрирован: Пт сен 07, 2018 20:20:02
Откуда: деревня в Тульской губернии

Сообщение ПростоНуб »

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

Объяснять проблему длинных обработчиков прерывания подробно не буду. Ограничусь утверждением, что найти компромисс между запретом прерываний в длинном обработчике и давлением на стек, при разрешении вложенных прерываний в этом же обработчике, не всегда возможно.
Реклама
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18786
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

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

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

Мой уютный бложик... заходите!
Контактная информация:
Собутыльник Кота
Аватара пользователя
Сообщения: 2723
Зарегистрирован: Пт сен 07, 2018 20:20:02
Откуда: деревня в Тульской губернии

Сообщение ПростоНуб »

ARV, ну зачем Вы пикируетесь? Я же так и написал, что отчасти Вы правы. И случаи, когда Вы не правы детализировал. Если в рамках конкретного проекта эти случаи не встречаются - ради бога, делайте задержки программным циклом и развлекайтесь поллингом. Никто это Вам запретить не сможет )))
С точки зрения методологии, я сторонник универсальных решений, которые не заставят пересматривать парадигму программирования при возникновении новых требований к системе. Поэтому и указал ТС наиболее универсальный подход в его случае.

Кстати, задержки на поллинг при анимации на LCD очень даже заметны на глаз, а при воспроизведении звуков - на слух. Попробуйте на досуге простейшую игрушку уровня ZX Spectrum/16 написать на MK с поллингом - убедитесь.
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18786
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

Моя парадигма программирования элементарная: просто значит правильно.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Ответить

Вернуться в «Теория»