Страница 1 из 4

Обработка кнопок, EXTI[0]

Добавлено: Пн ноя 11, 2024 17:16:30
aleksey chilov
Добрый вечер кому как.
Подскажите пожалуйста варианты обработки кнопочек, для меню.
Спасибо.

Re: Обработка кнопок, EXTI[0]

Добавлено: Пн ноя 11, 2024 18:51:07
Аlex
Покажите свои варианты, а мы оценим :)

Re: Обработка кнопок, EXTI[0]

Добавлено: Пн ноя 11, 2024 19:36:46
BOB51
Вариантов огромное множество.
Зависит от схемы, компилятора и идеи самого устройства.
8)

Re: Обработка кнопок, EXTI[0]

Добавлено: Вт ноя 12, 2024 07:34:19
aleksey chilov
Ну, тут просто флаги и переменные...

Изображение

Добавлено after 4 minutes 5 seconds:
Потом просто хендлер с прерываниями по спаду и по фронту поднимает и опускает флажки при нажатии кнопок...
Нажата кнопка флаг true отжата false... Булий...

Изображение

Добавлено after 5 minutes 38 seconds:
Дальше настроил таймер 3 на 100Гц и в прерывании с частотой 100 ГЦ идёт опрос кнопок и обработка ADC и если флаг
кнопки true тогда накапливаем переменную при каждом входе в прерывание если флаг позволяет. Это такакя у меня попытка была создать короткие и долгие нажатия кнопок. Ну и всё... Дальше я завис...



Изображение

Добавлено after 5 minutes 6 seconds:
Хочу посмотреть другие решения, сколько людей столько и идей... Не хотелось бы прибегать к каким то библиотекам тем более всего 3 кнопки да и библиотеки все на HAL а я использую CMSIS, она меньше памяти хавает правда проблем в 1000.000 раз больше чем в HAL ..

Re: Обработка кнопок, EXTI[0]

Добавлено: Вт ноя 12, 2024 10:39:49
Martian
А что, в АРМах теперь для флагов один бит нельзя? И проверки битов больше нет?

Re: Обработка кнопок, EXTI[0]

Добавлено: Вт ноя 12, 2024 11:26:19
AlanDrakes
Кто как, а я опрашиваю кнопки в основном потоке, когда код ничего не делает (внутри while (1) { .... __WFI(); } ) обычно после выхода из сна, а флаг "пора проверить кнопки" выставляется таймером.

Но суть это не меняет. Логика примерно та же:
- Читаем состояние.
- Увеличиваем/уменьшаем переменную (софт-фильтр), если она в нормальных пределах. Если она в ОСОБЫХ значениях - то это нажатие или длительное нажатие (или даже повторы)

Псевдокод:

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

if (ButtonFilter[N] == BTN_PRESS_DURATION) {
  DoPressCallback(N);
} else if (ButtonFilter[N] == BTN_LONG_PRESS_DURATION) {
  DoLongPressCallback(N);
} else if (ButtonFilter[N] == BTN_LONG_PRESS_REPEAT_DURATION) {
  DoPressRepeatCallback(N);
  ButtonFilter[N] -= BTN_REPEAT_DURATION;
}
И условия:
0 < BTN_PRESS_DURATION < BTN_LONG_PRESS_DURATION < BTN_LONG_PRESS_REPEAT_DURATION
BTN_REPEAT_DURATION > 0
BTN_REPEAT_DURATION > (BTN_LONG_PRESS_REPEAT_DURATION - BTN_LONG_PRESS_DURATION)

Например, BTN_PRESS_DURATION = 50мс
BTN_LONG_PRESS_DURATION = 500мс
BTN_LONG_PRESS_REPEAT_DURATION = 800мс
BTN_REPEAT_DURATION = 200мс

Таким образом счётчик фильтра кнопки будет считать с нуля (у меня каждую милисекунду просыпается контроллер, ибо SysTickRate = 1kHz), на отметке 50мс после нажатия кнопки (считается, что антидребезг работает и устранил момент искрения контактов) сработает "Нажатие", затем через 450мс отработает "Длительное нажатие" (можно поднять до 1с), ещё через 300 запустятся повторы и счётчик будет сброшен на значение 600мс и продолжёт тикать вверх до 800, генерируя новый "повтор".
И так циклически, пока кнопку не отпустят.

Естественно, длительности нужно подбирать "под свои потребности"

Спойлер

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

#define BTN_FILTER			50
uint8_t Buttons_Tmr[3];

void ButtonsRd(void) {
	// Power
	if (GPIOA->IDR & GPIO_IDR_5) {
		if (Buttons_Tmr[0] < BTN_FILTER * 5) {
			Buttons_Tmr[0]++;
			if (Buttons_Tmr[0] == BTN_FILTER * 5) {
				Buttons_Tmr[0]++;
				OnBtnPress(0);
			}
		}
	} else {
		if (Buttons_Tmr[0]) {
			Buttons_Tmr[0]--;
		}
	}
	// Next
	if (GPIOA->IDR & GPIO_IDR_9) {
		if (Buttons_Tmr[1] < BTN_FILTER) {
			Buttons_Tmr[1]++;
			if (Buttons_Tmr[1] == BTN_FILTER) {
				Buttons_Tmr[1]++;
				OnBtnPress(1);
			}
		}
	} else {
		if (Buttons_Tmr[1]) {
			Buttons_Tmr[1]--;
		}
	}
	// Mode
	if (GPIOA->IDR & GPIO_IDR_10) {
		if (Buttons_Tmr[2] < BTN_FILTER) {
			Buttons_Tmr[2]++;
			if (Buttons_Tmr[2] == BTN_FILTER) {
				Buttons_Tmr[2]++;
				timer = 0;
				OnBtnPress(2);
			}
		}
	} else {
		if (Buttons_Tmr[2]) {
			Buttons_Tmr[2]--;
		}
	}
}

int main(void) {
// Инициализация пропущена, оставлен только основной цикл
	while(1) {
		__WFI();
		ButtonsRd();
		Job();
	}
}
Тут я НЕ использую длительные нажатия и прочие фокусы, но их можно добавить по желанию.

А тут - использую, но без повторов:

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

void ButtonFilfer(uint8_t ButtonNumber, uint8_t Direction) {
	if (Direction) {
		BtnFilter[ButtonNumber]++;
		if (BtnFilter[ButtonNumber] == BUTTON_PRESS_DURATION) {
			// OnPress()
			OnButtonPress(ButtonNumber);
		} else if (BtnFilter[ButtonNumber] == BUTTON_LONG_PRESS_DUR) {
			// On LongPress()
			OnButtonLongPress(ButtonNumber);
		} else if (BtnFilter[ButtonNumber] > BUTTON_LONG_PRESS_DUR) {
			BtnFilter[ButtonNumber] = BUTTON_LONG_PRESS_DUR;
		}
	} else {
		if (BtnFilter[ButtonNumber]) {
			BtnFilter[ButtonNumber] = 0;
			OnButtonRelease(ButtonNumber);
		} else {
			BtnFilter[ButtonNumber] = 0;
			OnButtonRelease(ButtonNumber);
		}
	}
}

Re: Обработка кнопок, EXTI[0]

Добавлено: Вт ноя 12, 2024 11:39:43
jcxz
[uquote="aleksey chilov",url="/forum/viewtopic.php?p=4648108#p4648108"]Потом просто хендлер с прерываниями по спаду и по фронту поднимает и опускает флажки при нажатии кнопок...[/uquote]1. Зачем сброс флагов - тремя операциями вместо одной?
2. Чтение GPIOA->IDR то же самое - зачем 6(!) чтений вместо одного??? :shock:
3. Вместо прерывания EXTI лучше использовать периодическое прерывание.

При таком кривом обработчике прерываний иногда будут создаваться ситуации, когда кнопка оказывается залипшей в нажатом состоянии (или будут пропуски нажатий). Из-за того, что читаете GPIOA->IDR много раз вместо одного.
Подумайте - что будет, если кнопка изменит состояние между вашими чтениями?

PS: Для вставки кода здесь есть соответствующий тэг "Code". (вместо картинок)

Re: Обработка кнопок, EXTI[0]

Добавлено: Вт ноя 12, 2024 11:40:23
aleksey chilov
Обработчик у вас интересный. Пожалуй я его возьму к себе в проект. Только у меня контролер не спит но я могу поместить обработчик в хендлер по таймеру там у меня он на 100Гц настроен думаю этого вполне достаточно будет.

Re: Обработка кнопок, EXTI[0]

Добавлено: Вт ноя 12, 2024 11:52:54
jcxz
[uquote="aleksey chilov",url="/forum/viewtopic.php?p=4648108#p4648108"]Дальше настроил таймер 3 на 100Гц и в прерывании с частотой 100 ГЦ идёт опрос кнопок[/uquote]Зачем тогда EXTI, если уже есть периодическое прерывание?
Какой-то бурелом..... :facepalm:

Добавлено after 1 minute 55 seconds:
[uquote="Martian",url="/forum/viewtopic.php?p=4648152#p4648152"]А что, в АРМах теперь для флагов один бит нельзя? И проверки битов больше нет?[/uquote]Автор один и тот же порт, содержащий все кнопки, читает 6 раз! А вы про битовые переменные.... 8)
Он в принципе не понимает что такое "биты". Судя по коду. :dont_know:

Re: Обработка кнопок, EXTI[0]

Добавлено: Вт ноя 12, 2024 11:59:40
aleksey chilov
Чё вы несёте??? Какие 6 раз???
Там прерывание настроено по фронту и по спаду на каждый пин порта "А" который опрашивается. Порт в нуле флаг поднялся, второе прерывание когда порт в +3,3 когда кнопку бросил флаг сбросился чё не понятно какие 6 раз читайте внимательно что написанно

Re: Обработка кнопок, EXTI[0]

Добавлено: Вт ноя 12, 2024 12:01:54
jcxz
Примерно так:

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

enum {BUT_1, BUT_2, BUT_3, ...};
uint volatile buttons = 0;

void Isr()
{
  EXTI->PR = EXTI_PR_PR5 | EXTI_PR_PR6 | EXTI_PR_PR7;
  uint i = GPIOA->IDR, c = 0;
  if (!(i & 1 << 7)) c += 1 << BUT_1;
  if (!(i & 1 << 6)) c += 1 << BUT_2;
  if (!(i & 1 << 5)) c += 1 << BUT_3;
  buttons = c;
}
Также обращаем внимание на volatile.

Re: Обработка кнопок, EXTI[0]

Добавлено: Вт ноя 12, 2024 12:02:08
aleksey chilov
EXTI настраивал дамал флагами как-то кнопки отслеживать но можно и без них я пробовал варианты какие лучше

Re: Обработка кнопок, EXTI[0]

Добавлено: Вт ноя 12, 2024 12:03:42
jcxz
[uquote="aleksey chilov",url="/forum/viewtopic.php?p=4648186#p4648186"]Чё вы несёте??? Какие 6 раз???[/uquote]"Несёте" это вы.

[uquote="aleksey chilov",url="/forum/viewtopic.php?p=4648186#p4648186"]чё не понятно какие 6 раз читайте внимательно что написанно[/uquote]Ясно. Гопник детектед.
Дальше в своём говнокоде бултыхайтесь сами.

Re: Обработка кнопок, EXTI[0]

Добавлено: Вт ноя 12, 2024 12:40:54
Martian
[uquote="aleksey chilov",url="/forum/viewtopic.php?p=4648190#p4648190"]EXTI настраивал думал флагами как-то кнопки отслеживать но можно и без них я пробовал варианты какие лучше[/uquote]
Это метод тыка. Даже если получите рабочий вариант, это не даст опыта и знаний.
Надо по науке: изучить язык, изучить контроллер, продумать алгоритм. И вот тогда идти на форум и спрашивать непонятное. Но, скорее всего, с таким вопросом уже не придёте - не будет непонятного, или же быстро разберётесь сами.
jcxz привёл код, так что, больше и говорить нечего.

Re: Обработка кнопок, EXTI[0]

Добавлено: Вт ноя 12, 2024 13:13:19
jcxz
[uquote="Martian",url="/forum/viewtopic.php?p=4648215#p4648215"]jcxz привёл код, так что, больше и говорить нечего.[/uquote]По уму в той процедуре ещё хорошо бы после:

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

EXTI->PR = EXTI_PR_PR5 | EXTI_PR_PR6 | EXTI_PR_PR7;
добавить какую-то синхронизацию перед чтением IDR. Чтобы быть уверенным, что запись в EXTI->PR реально выполнилась к моменту чтения IDR.
Например так (обратным чтением):

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

EXTI->PR = EXTI_PR_PR5 | EXTI_PR_PR6 | EXTI_PR_PR7;
uint j = EXTI->PR;
uint i = GPIOA->IDR, c = 0;
...
Потому как EXTI и GPIO - разная периферия, а значит - может находиться на разных сегментах шин МК.
Иначе - есть вероятность сбросить событие переключения состояния кнопки до его чтения.

Но это уже - не для ТС. Ему - не читать. Иначе - есть опасность поумнеть. 8)

Re: Обработка кнопок, EXTI[0]

Добавлено: Вт ноя 12, 2024 13:17:30
aleksey chilov
За код спасибо!
Меня и интересовали методы реализации опроса кнопок.
Хотел посмотреть кто как делает чтоб не придумывать то что давно придумано.
Ну ничего, разберёмся по маленьку...

Re: Обработка кнопок, EXTI[0]

Добавлено: Вт ноя 12, 2024 13:20:41
Adrift
[uquote="jcxz",url="/forum/viewtopic.php?p=4648189#p4648189"]

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

  uint i = GPIOA->IDR, c = 0;
  if (!(i & 1 << 7)) c += 1 << BUT_1;
  if (!(i & 1 << 6)) c += 1 << BUT_2;
  if (!(i & 1 << 5)) c += 1 << BUT_3;
  buttons = c;
}
[/uquote]
Тут напрашивается функция которая будет битики переносить:

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

uint32_t i = GPIOA->IDR;
buttons = moveBit(i, 7, BUT_1) | moveBit(i, 6, BUT_2) | moveBit(i, 5, BUT_3);

Re: Обработка кнопок, EXTI[0]

Добавлено: Вт ноя 12, 2024 13:22:46
jcxz
[uquote="Adrift",url="/forum/viewtopic.php?p=4648233#p4648233"]Тут напрашивается функция которая будет битики переносить:

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

uint32_t i = GPIOA->IDR;
buttons = moveBit(i, 7, BUT_1) | moveBit(i, 6, BUT_2) | moveBit(i, 5, BUT_3);
[/uquote]Конечно, так лучше. Но для начинающих может быть уже сложно. Не все они знают - что такое макросы. :wink:

Ещё лучше описать пины кнопок в хидере:

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

#define PIN_BUTTON1        A, 7
#define PIN_BUTTON2        A, 6
#define PIN_BUTTON3        A, 5
...
и ссылаться через них, а не через "магические числа".
PS: Это для тех, кто знает - что такое "макрос". :wink:

Re: Обработка кнопок, EXTI[0]

Добавлено: Вт ноя 12, 2024 13:35:34
Martian
buttons = GPIOA->IDR & mask; ;)

Re: Обработка кнопок, EXTI[0]

Добавлено: Вт ноя 12, 2024 13:43:24
Adrift
[uquote="jcxz",url="/forum/viewtopic.php?p=4648234#p4648234"]Ещё лучше описать пины кнопок в хидере:

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

#define PIN_BUTTON1        A, 7
#define PIN_BUTTON2        A, 6
#define PIN_BUTTON3        A, 5
...
[/uquote]
Пины в хедере - это уже дикий оверхед, придется читать с порта по разу на каждую кнопку )