Vovik-78 писал(а):Один вот, под кнопки
под кнопки отдельного таймера не надо, достаточно в любом уже задействованном добавить один счетчик. этот счетчик должен считать примерно каждые 10 мс, можно от 1 до 20, больше не желательно, чтобы не терять события.
код в целом такой:
Код: Выделить всё
// эти константы измеряются в тиках счетчика, т.е. если счетчик
// тикает каждые 10 мс, то время долгого удержания будет 3 сек, а врем автоповтора при удержании 300 мс
#define LONG_PRESS_TIME (300)
#define REPEAT_TIME (30)
// если ни одна кнопка не нажималась это время, будет послано сообщение EVENT_DEFAULT_MODE
#define DEFAULT_MODE_TIME (3000)
static event_t prev = EVENT_EMPTY;
static uint16_t cnt;
void reset_event(void){
cnt = get_ticks();
}
/**
* функция возвращает код события в момент нажатия кнопки.
* если кнопка удерживается нажатой долго, то через LONG_PRESS_TIME
* код события возвращается повторно с флагом EV_LONG,
* а затем повторяется каждые REPEAT_TIME.
* при отпускании кнопки код событи не возвращается
* каждые DEFAULT_MODE_TIME с момента выдачи последнего кода события
* при отсутствии нажатий кнопок автоматически формируется событие
* EVENT_DEFAULT_TIME
* @return код события
*/
event_t get_event(void){
uint8_t btn = get_key(); // эта функция должна вернуть битовое состояние кнопок
uint16_t now = get_ticks(); // вот эта функция должна вернуть значение упомянутого счетчика int16_t
event_t tmp;
if(btn == 0){
// ничего не нажато
if(!(prev & EV_RELEASED) && (prev != EVENT_EMPTY)){
// фиксируем момент отпускания
cnt = now;
prev |= EV_RELEASED;
// уточняем, сколько времени прошло с предыдущего нажатия
} else {
prev = EVENT_EMPTY;
}
if((now - cnt) > DEFAULT_MODE_TIME){
// если больше заданного - возвращаем событие сброса режима
cnt = now;
return EVENT_DEFAULT_MODE;
}
return prev;
} else {
// что-то нажато, разбираем кнопки по событиям
// KEY_XXX - это БИТОВЫЕ МАСКИ кнопок
switch(btn){
case KEY_UP: tmp = EVENT_UP; break;
case KEY_DN: tmp = EVENT_DN; break;
case KEY_ENT: tmp = EVENT_ENTER; break;
case KEY_ESC: tmp = EVENT_ESC; break;
// а вот и комбинация одновременно нажатых кнопок
case KEY_UP | KEY_DN: tmp = EVENT_UPDN; break;
default:
// всё, что выше не описано, будет игнорироваться
tmp = EVENT_EMPTY;
}
}
// проверка долгое/короткое нажатие
if(tmp == (prev & EVENT_IGNORE_LONG)){
// нажато то же самое, что и в прошлый раз
if((now - cnt) > LONG_PRESS_TIME){
// если время удержания больше заданного, ставим флаг долгого события
prev = tmp;
tmp |= EV_LONG;
cnt += REPEAT_TIME;
return tmp;
}
} else {
// нажато что-то другое, чем в прошлый раз
cnt = now; // фиксируем момент нажатия
prev = tmp; // запоминаем, что нажали
return tmp;
}
return EVENT_EMPTY;
}
/// любое первое событие, кроме пустого и "по умолчанию", сопровождает звуковым сигналом
event_t get_key_event(void){
event_t ev = get_event();
//if((ev != EVENT_EMPTY) && (ev != EVENT_DEFAULT_MODE) && !(ev & (EV_RELEASED | EV_LONG)))
// play_song(beep);
return ev;
}
в начале кода есть две функции, которые вы должны реализовать. первая опрашивает порт кнопок и возвращает ЕДИНИЦЫ в битах, соответствующих нажатм кнопкам.
вторая просто должна вернуть значение счетчика тиков.
в дополнение к этому коду вы делаете такое описание комбинаций ваших кнопок
Код: Выделить всё
/// флаг повтора события (при долгом удержании)
#define EV_LONG 0x80
/// флаг отпускания кнопки
#define EV_RELEASED 0x40
/// варианты генерируемых событий
typedef enum{
EVENT_EMPTY, //!< события отсутствуют
EVENT_UP, //!< кнопка ВВЕРХ
EVENT_DN, //!< кнопка ВНИЗ
EVENT_ENTER, //!< кнопка ЭНТЕР
EVENT_UPDN, //!< обе кнопки ВВЕРХ и ВНИЗ
EVENT_ESC,
EVENT_DEFAULT_MODE, //!< автособытие - перейти в режим времени
EVENT_UP_LONG = EVENT_UP | EV_LONG, //!< ВВЕРХ удерживается долго
EVENT_DN_LONG = EVENT_DN | EV_LONG, //!< ВНИЗ удерживается долго
EVENT_ENTER_LONG = EVENT_ENTER | EV_LONG,//!< ЭНТЕР удерживается долго
EVENT_UPDN_LONG = EVENT_UPDN | EV_LONG,//!< ВВЕРХ и ВНИЗ удерживаются долго
EVENT_ENTER_RELEASED = EVENT_ENTER | EV_RELEASED,//!< кнопка ЭНТЕР отпущена
EVENT_ESC_LONG = EVENT_ESC | EV_LONG,
EVENT_ESC_RELEASED = EVENT_ESC | EV_RELEASED,
// не добавлять ниже!
EVENT_COUNT //!< общее количество событий (не используется)
} event_t;
в главном коде вы получаете событие и выполняете то, что вам надо сделать
Код: Выделить всё
event_t ev = get_event();
switch(ev){
case EVENT_NONE: break; // ничего не нажималось и не отпускалось
сase EVENT_UP | EV_RELEASED : break; // если отпущена кнопка UP
case EVENT_UP_LONG: break; // если нажата и долго удерживается кнопка UP
// и так далее
}
единственное, что вы должны учитывать, что если вам надо реагировать раздельно на короткое и долгое нажатие комбинации кнопок, вы должны обрабатывать долгое, как выше, а короткое - НА ОТПУСКАНИЕ, то есть с
EV_RELEASED, если сделать без
EV_RELEASED просто
EVENT_UP, то сначала сработает обработка КОРОТКОГО нажатия, а потом уже ДОЛГОГО.