2 кнопки - Нужно выжать 5 режимов обработки.

Обсуждаем контроллеры компании Atmel.
Ответить
Опытный кот
Аватара пользователя
Сообщения: 760
Зарегистрирован: Сб окт 22, 2016 17:33:32
Откуда: кг

Сообщение Карбофос »

main.c
Спойлер

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

#include <avr/io.h>
#include <avr/interrupt.h>
#include "Button.h"

typButton aButtons[] = {
  {.ddr=&DDRB, .port=&PORTB, .pin=&PINB, .bitmask=_BV(PB0)},
  {.ddr=&DDRC, .port=&PORTC, .pin=&PINC, .bitmask=_BV(PC1)},
  {.ddr=&DDRD, .port=&PORTD, .pin=&PIND, .bitmask=_BV(PD2)}
};

// every 1 ms
volatile uint8_t Strobe1ms;

ISR(TIMER2_COMPA_vect)
{
  Strobe1ms = 1;
}

void Setup(void)
{
  btnSetup(sizeof(aButtons) / sizeof(aButtons[0]));
  // Timer 2
  Strobe1ms = 0;
  // Timer2, Mode 2 Clear Timer on Compare Match (CTC) mode, OC2A and OC2B disconnected
  TCCR2A = _BV(WGM21);
  // T = clkIO/64 = 4 us
  TCCR2B = _BV(CS22);
  // (249 + 1) * 4 us = 1 ms
  OCR2A  = 249;
  // Output Compare A Match Interrupt Enable
  TIMSK2 = _BV(OCIE2A);
  // interrupts enable
  sei();
}

int main(void)
{
  Setup();
  while(1) {
    if(Strobe1ms) {
      Strobe1ms = 0;
      btnCheckOnTimer(sizeof(aButtons) / sizeof(aButtons[0]));
      uint8_t AllButtonsLongPressed = 1;
      for(uint8_t i = 0; i < sizeof(aButtons) / sizeof(aButtons[0]); ++i) {
        AllButtonsLongPressed &= (btnRepeat == aButtons[i].state) || (btnAmbRepeat == aButtons[i].state);
        switch(aButtons[i].state) {
        case btnDbl:    // двойной клик
          break;
        case btnOn:     // нажали
          break;
        case btnRepeat: // автоповтор
          break;
        default:
          break;
        }
      }
      if(AllButtonsLongPressed) {
        ; // все кнопки нажаты продолжительное время
      }
    }
  }
}
Button.h
Спойлер

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

#ifndef BUTTON_H
#define BUTTON_H

typedef enum {btnOff = 0, btnAmbOn, btnAmbDbl_1, btnAmbDbl_2, btnDbl, btnOn, btnAmbRepeat, btnRepeat} typBtnState;

// in makefile
#ifndef BTN_BEFORE_ON
#define BTN_BEFORE_ON       200   // 200ms
#endif

#ifndef BTN_BEFORE_DBL
#define BTN_BEFORE_DBL       50   // 50ms
#endif

#ifndef BTN_BEFORE_REPEAT
#define BTN_BEFORE_REPEAT   200   // 200ms
#endif

#ifndef BTN_REPEAT_DELAY
#define BTN_REPEAT_DELAY    150   // 150ms
#endif

typedef struct {
  volatile uint8_t * ddr;
  volatile uint8_t * port;
  volatile uint8_t * pin;
  const uint8_t bitmask;
  typBtnState state;
  uint16_t tick;
} typButton;

extern typButton aButtons[];

void btnSetup(uint8_t qtty);
uint8_t btnCheckOnTimer(uint8_t qtty);

#endif
Button.c
Спойлер

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

#include <avr/io.h>
#include "Button.h"

void btnSetup(uint8_t qtty)
{
  typButton* pButton = aButtons;
  while(qtty--) {
    // input
    *pButton->ddr  &= ~pButton->bitmask;
    // pull-up
    *pButton->port |=  pButton->bitmask;
    pButton->state  = btnOff;
    pButton->tick   = 0;
    ++pButton;
  }
}

uint8_t btnCheckOnTimer(uint8_t qtty)
{
  uint8_t bNeedCheck = 0;
  typButton* pButton = aButtons;
  while(qtty--) {
    ++pButton->tick;
    if(*pButton->pin & pButton->bitmask) {  // нет нажатия
      switch(pButton->state) {
      case btnAmbOn:  // было нажатие, возможно двойной клик, отпустили раньше
        if(BTN_BEFORE_DBL <= pButton->tick) {
          pButton->state = btnAmbDbl_1;
          pButton->tick  = 0;
        }
        break;
      case btnAmbDbl_1: // не нажали повторно для двойного клика
        if(BTN_BEFORE_ON <= pButton->tick) {
          pButton->state = btnOff;
        }
        break;
      case btnAmbDbl_2: // двойное нажатие
        if(BTN_BEFORE_ON <= pButton->tick) { // слишком долгое повторное нажатие, сброс
          pButton->state = btnOff;
        } else if(BTN_BEFORE_DBL <= pButton->tick) {
          pButton->state = btnDbl;
          bNeedCheck = 1;
        }
        break;
      default:
        pButton->state = btnOff;
        break;
      }
    } else {            // зафиксировали нажатие
      switch(pButton->state) {
      case btnAmbDbl_1: // двойное нажатие
        if(BTN_BEFORE_DBL <= pButton->tick) {
          pButton->state = btnAmbDbl_2;
          pButton->tick  = 0;
        }
        break;
      case btnAmbDbl_2: // двойное нажатие, ждем отпускание кнопки
        break;
      case btnOff:      // ничего не было зафиксированно до сих-пор
        pButton->state = btnAmbOn;
        pButton->tick  = 0;
        break;
      case btnAmbOn:    // кнопка возможно нажата
        if(BTN_BEFORE_ON <= pButton->tick) {
          pButton->state = btnOn;       // точно нажата - обработать
          pButton->tick  = 0;
          bNeedCheck = 1;
        }
        break;
      case btnOn:
      case btnRepeat:
        pButton->state = btnAmbRepeat;  // будем ждать автоповтор
        // плавно попадаем в btnAmbRepeat
      case btnAmbRepeat:
        if(BTN_BEFORE_REPEAT <= pButton->tick) {
          pButton->state = btnRepeat;   // автоповтор
          pButton->tick  = 0;
          bNeedCheck = 1;
        }
        break;
      default:
        pButton->state = btnOff;        // ерунда какая-то
        break;
      }
    }
    ++pButton;
  }
  return bNeedCheck;
}
Вложения
Buttons.rar
Проект
(6.78 КБ) 128 скачиваний
Реклама
Встал на лапы
Сообщения: 95
Зарегистрирован: Чт дек 16, 2021 18:20:02

Сообщение Vovik-78 »

[uquote="Карбофос",url="/forum/viewtopic.php?p=4180362#p4180362"]main.c[/uquote]

Спасибо, попробую.

Добавлено after 5 minutes 17 seconds:
Единственное, как я понял, тут реализован дабл клик?
Реклама
Опытный кот
Аватара пользователя
Сообщения: 760
Зарегистрирован: Сб окт 22, 2016 17:33:32
Откуда: кг

Сообщение Карбофос »

[uquote="Vovik-78",url="/forum/viewtopic.php?p=4180390#p4180390"]дабл клик?[/uquote]
Да.

Если на множественное нажатие надо реагировать 1 раз, то какой-то триггер надо городить.
Настройки таймера 2 под ATmega328 16 МГц, в makefile. Под другую конфигурацию как-то по другому надо.
Это не хвост, это антенна
Сообщения: 1492
Зарегистрирован: Ср июн 25, 2008 15:19:44

Сообщение Demiurg »

Насчёт двойного клика. На одном форуме как-то было обсуждение обработки кнопок. Навешивать некоторые функции на модуль обработки клавиш, думаю неоптимально, с точки зрения универсальности. Модуль кнопок должен опрашивать кнопки, определять обычные нажатия, удержание, автоповтор, отпускание кнопок. Подсчет количества нажатий должно вестись в соответствующем программном модуле, который принимает события от модуля кнопок и коды кнопок. В этом случае соблюдается универсальность применения модуля обработки кнопок. Да и как бы это и логично. Двойное и так далее нажатия кнопок требование конкретного проекта, а не модуля обработки кнопок. Мало того. В отдельных случаях, удержание и автоповтор тоже можно разнести. На модуль обработки кнопок оставить некий общий принцип. Скажем, минималка удержания (особые требования реализовать уже в каждом конкретном проекте). То же касается и автоповтора. Пояснение, минимальное время удержания, скажем, 500 мс, время автоповтора, 25 мс. Удержание, автоповтор могут не потребоваться. В разных проектах разные требования ко времени удержания, автоповтора.
Конечно же, окончательное решение на своих проектах каждый принимает сам. Что оптимальнее и целесообразно

Для ТС. Двойное и более нажатия. Реализация вне модуля опроса кнопок. В модуле приема событий кнопок создаётся переменная счётчик. Алгоритм. В случае события нажатия кнопок разбираем, какая кнопка нажата. Если на эту кнопку ведётся подсчет, инкремент переменной счётчика. Далее сравнение и соответствующее действие.
Контактная информация:
Реклама
Эиком - электронные компоненты и радиодетали
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Сообщение Reflector »

[uquote="Demiurg",url="/forum/viewtopic.php?p=4180596#p4180596"]Подсчет количества нажатий должно вестись в соответствующем программном модуле, который принимает события от модуля кнопок и коды кнопок. В этом случае соблюдается универсальность применения модуля обработки кнопок. Да и как бы это и логично. Двойное и так далее нажатия кнопок требование конкретного проекта, а не модуля обработки кнопок. Мало того. В отдельных случаях, удержание и автоповтор тоже можно разнести. На модуль обработки кнопок оставить некий общий принцип. Скажем, минималка удержания (особые требования реализовать уже в каждом конкретном проекте).[/uquote]
Не удобно, я обычно стараюсь запихнуть всего побольше, но с возможностью выбора, правда на С такое красиво сделать вряд ли получится:

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

using pins = PinList<PC13, PA1, PB0, PB10>;
Buttons<pins> buttons;
Это простейший случай, ловим только обычные нажатия для всех кнопок.

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

constexpr ButtonConfig conf =
{
    .queueSize   = 4,
    .shortMask   = 0x0E,
    .longMask    = 0x0D,
    .releaseMask = 0x01,
    .repeatMask  = 0x0C,
    .longThreshold = 500,
    .....
};

using pins = PinList<PC13, PA1, PB0, PB10>;
Buttons<pins, conf> buttons;
А тут я включил очередь, что помимо прочего позволяет вызывать update() из прерывания, далее для трех кнопок ловим обычные нажатия, для двух из них включен автоповтор, а четвертая реагирует только на отпускания и длинные нажатия. Все эти параметры передаются в класс как константы, они даже во флеше не хранятся и если, допустим, сделать longMask нулевой, то весь код за обработки длинных нажатий выпилится потому что в классе десяток if constexpr:

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

if constexpr (longMask)
{
    auto diff = (prevLongPressed ^ current);
    state.longPressed = diff & current & longMask;
    prevLongPressed = current;
}
И так со всем, если очередь не используется, то вместо класса подставится bool и нигде не используемая булевая переменная тоже выпилится.
Реклама
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18785
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

Очереди, структуры в очередях... Как обычно, забыли о разделе, где идет обсуждение: в большинстве популярных AVR на очередь кнопок половина ОЗУ уйдет
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Реклама
Это не хвост, это антенна
Сообщения: 1492
Зарегистрирован: Ср июн 25, 2008 15:19:44

Сообщение Demiurg »

To Reflector. Задача модуля кнопок выдать события нажатия, отжатия, удержания и повтора. Коды кнопок. В большинстве случаев в девайсах работает принцип обработка всех кнопок, как одну. Сочетания или одновременное нажатие кнопок используется не всегда.
Подсчет количества нажатий это уже задача конкретного проекта. Именно по этому я говорил про универсальность. Берешь свои готовые либы и чуток подпиливаешь под нужный проект. В большинстве случаев просто дефайнами переназначаешь пины мк. В вашем же случае это всегда глубокая переработка.

To ARV. Поддерживаю. Очередь кнопок не всегда нужна. Далеко не всегда. Это уже попахивает более серьезными проектами и микроконтроллерами. Скорее, линуксом. :)
Контактная информация:
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Сообщение Reflector »

[uquote="ARV",url="/forum/viewtopic.php?p=4180712#p4180712"]Очереди, структуры в очередях... Как обычно, забыли о разделе, где идет обсуждение: в большинстве популярных AVR на очередь кнопок половина ОЗУ уйдет[/uquote]
Не очередь, а опциональная и выключенная по умолчанию очередь. А структура у меня на STM32, там 32 бита в регистре вернуть можно, на AVR никто так делать не заставляет.

Добавлено after 2 minutes 24 seconds:
[uquote="Demiurg",url="/forum/viewtopic.php?p=4180715#p4180715"]To Reflector. Задача модуля кнопок выдать события нажатия, отжатия, удержания и повтора. Коды кнопок. В большинстве случаев в девайсах работает принцип обработка всех кнопок, как одну. Сочетания или одновременное нажатие кнопок используется не всегда.
Подсчет количества нажатий это уже задача конкретного проекта. Именно по этому я говорил про универсальность. Берешь свои готовые либы и чуток подпиливаешь под нужный проект. В большинстве случаев просто дефайнами переназначаешь пины мк. В вашем же случае это всегда глубокая переработка.[/uquote]
Ничего не понял, я же показал код в котором все конфигурируется в одном месте, где там глубокая переработка? То весь код, минимум две строки, дальше нужно вызвать init(), update и получить состояние кнопок, т.е. еще 3 строки. Сам класс живет в хедере и никогда не меняется, никаких дефайнов и т.д. больше нигде нет.
Это не хвост, это антенна
Сообщения: 1492
Зарегистрирован: Ср июн 25, 2008 15:19:44

Сообщение Demiurg »

Ты же сам написал. На си красиво не получится. Я про си и говорю
Контактная информация:
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18785
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

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

Мой уютный бложик... заходите!
Контактная информация:
Это не хвост, это антенна
Сообщения: 1492
Зарегистрирован: Ср июн 25, 2008 15:19:44

Сообщение Demiurg »

Как бы об этом я и писал и примерно так у меня и реализовано.
Контактная информация:
Опытный кот
Аватара пользователя
Сообщения: 760
Зарегистрирован: Сб окт 22, 2016 17:33:32
Откуда: кг

Сообщение Карбофос »

А чего стоит оставить своё мнение при себе?
Или, требуется _всех_ убедить в правильности своего мнения?
Я прекрасно понимаю, о чем вы пишите.
Только смысла в ваших конструкциях не вижу.
Переиначивая, весь ваш бред - бессмысленная хня.
Не умеешь писать - не пиши.

Добавлено after 3 hours 28 minutes 25 seconds:
[uquote="Demiurg",url="/forum/viewtopic.php?p=4180812#p4180812"]примерно так у меня и реализовано.[/uquote]
Не стесняйся, выкладывай.
Счас все поржём.
Это не хвост, это антенна
Сообщения: 1492
Зарегистрирован: Ср июн 25, 2008 15:19:44

Сообщение Demiurg »

Вроде весна ещё не наступила... Выпил?
Контактная информация:
Встал на лапы
Сообщения: 95
Зарегистрирован: Чт дек 16, 2021 18:20:02

Сообщение Vovik-78 »

Всем привет. продолжаю эксперименты с кнопками.
Набросал вот такой код:

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

#include <avr/io.h>
#include <avr/interrupt.h>
#include "main.h"

volatile uint8_t button1_count;// счетчик кнопки 1
volatile uint8_t button2_count;// счетчик кнопки 2
volatile uint8_t button3_count;// счетчик кнопки 2
volatile uint8_t button_click;//







// обработчик прерывания по переполнению таймера 0
ISR(TIMER0_OVF_vect)
{
	//=========== опрос первой кнопки =============
	if (~PINC&(1<<PC0)) // если на ПИНе 1
	{
		if (bit.flag_1==0) //если флаг =0 , то считаем до 120 иначе до 61
		{
			if (button1_count<120) button1_count++;//Счетчик будет считать до 120 (~2с)
			if (bit.flag_2==1) //Если флаг =1 включаем репит клик (быстрый инкримент)
			{
				if(button1_count==118) 
					button1_count=112;
					button_click=0;
			} 
		}	
		else
		{
		   if (button1_count<61) button1_count++;//Счетчик будет считать до 61 (~1с)	
		}    
	}
	else
	{
		if ((button1_count>5) && (button1_count<30)) bit.but_1=1;	//Короткий клик
		if ((button1_count>29) && (button1_count<=61)) bit.but_1=2;	 //Средний клик
		if ((button1_count>61) && (button1_count<=120))bit.but_1=3;  //Длинный клик
	        button1_count=0;
	}
	//===========Опрос второй кнопки ===========
	if (~PINC&(1<<PC1)) // если на ПИНе 1 	 
	{
		if (bit.flag_1==0) //если флаг =0 , то считаем до 120 иначе до 61
		{
			if (button2_count<120) button2_count++;//Счетчик будет считать до 120 (~2с)
		}
		else
		{
			if (button2_count<61) button2_count++;//Счетчик будет считать до 61 (~1с)
		}	    
	}
	else
	{
		if ((button2_count>5) && (button2_count<30)) bit.but_2=1;    //Короткий клик
		if ((button2_count>29) && (button2_count<61)) bit.but_2=2;	 //Средний клик
		if ((button2_count>61) && (button2_count<=120))	bit.but_2=3; //Длинный клик
		   button2_count=0;
	}
	////===========Опрос третьей кнопки ===========
	if (~PINC&(1<<PC2)) // если на ПИНе 1
	{
		if (bit.flag_1==0) //если флаг =0 , то считаем до 120 иначе до 61
		{
			if (button3_count<120) button3_count++;//Счетчик будет считать до 120 (~2с)
		}
		else
		{
			if (button3_count<61) button3_count++;//Счетчик будет считать до 61 (~1с)
		}	
	}
	else
	{
		if ((button3_count>5) && (button3_count<30)) bit.but_3=1;    //Короткий клик
		if ((button3_count>29) && (button3_count<=61)) bit.but_3=2;	 //Средний клик
		if ((button3_count>61) && (button3_count<=120))	bit.but_3=3; //Длинный клик
		button3_count=0;
	}
	
	button_click = (bit.but_1<<4)|(bit.but_2<<2)|(bit.but_3);	
}

int main(void)
{
	TCCR0|=(1<<CS01) | (1<<CS00);// запуск таймера 0 с делителем 64
	TIMSK|=(1<<TOIE0); // разрешить прерывание по переполнению таймера 0
	
	DDRB=0xff;// пины светодиодов на вsход
	PORTB=0;
	DDRC &= ~(1<<PC0) | (1<<PC1) |(1<<PC2)|(1<<PC5)|(1<<PC4);
	PORTC|= (1<<PC0) | (1<<PC1) |(1<<PC2)|(1<<PC5)|(1<<PC4);

	
	sei();//глобально разрешить перерывания




	


	while (1)
	{

		 if (~PINC&(1<<PC5))
		  bit.flag_1=1;
		  else
		  bit.flag_1 =0;
		  if (~PINC&(1<<PC4))
		  bit.flag_2=1;
		  else
		  bit.flag_2 =0;
		  

	
	}
}

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

#ifndef MAIN_H_
#define MAIN_H_
	struct my_button
	{
		unsigned char but_1:2;	 //Битовый код 1ой кнопки 0-3
		unsigned char but_2:2;  //Битовый код 2ой кнопки 0-3
		unsigned char but_3:2;	 //Битовый код 3ей кнопки 0-3
		unsigned char flag_1:1;  // Флаг разрешающий клик двух кнопок
		unsigned char flag_2:1;	 // Флаг разрешающий инкремент по длинному клику
	};
 	struct my_button bit;



#endif /* MAIN_H_ */
Но проблема в том, что в Протеусе, при отпускании кнопки, условия Элс выполняются через раз.
Счетчик сбрасывается в ноль стабильно, а вот присвоения значений bit.but_*** происходит как то не стабильно.
Подскажите, почему так происходит.
Думал про дребезг при отпускании, но раз счетчик обнуляется, то программа прорходит по всем сравнениям Ифф.

Тестирую пока все на кнопке РС0.
Ответить

Вернуться в «AVR»