Как обработать удержание кнопки в МК AVR?

Всем привет ! Набросал небольшой код, который обрабатывает нажатие и удержание кнопки по таймеру. Кнопка подтянута резистором к +5В и к PORTA4 микроконтроллера.
При появлении 0 на PA4 запускает таймер(START_HOLD_DOWN_TIMER), ~каждые 10мс в прерывании таймер проверяется состояние пина. Если попали в условии 300 раз, что ~равно 3 сек. Выводится сообщении в UART btn holded down. Если же кнопку отпустили раньше, считаем что это было просто однократное нажатие. Также после того, как отловили удержание(а за тем пользователь отпустил кнопку), может возникнуть дребезг контактов, соответсвенно после отпускание МК зафиксирует еще одно нажатие. Для этого после удержания, запускается еще один таймер, который активирует обработку(btn_enabled) через ~ 500мс. Просьба дать отзывы по алгоритму и самому коду)
Сам код:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdio.h>



static int uart_putchar(char c, FILE *stream);
static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);
#define loop_until_bit_is_set(sfr,bit) \
do { } while (bit_is_clear(sfr, bit))
void debug_usart_init(){
	UBRR0H = 0;
	UBRR0L = 103;//103 for 16 mhz
	
	UCSR0C |= (1<<UCSZ00) | (1<<UCSZ01);
	UCSR0B |= (1<<RXCIE0) | (1<<TXEN0) | (1<<RXEN0);
}
static int uart_putchar(char c, FILE *stream)
{
	if (c == '\n')
	uart_putchar('\r', stream);
	UDR0 = c;
	loop_until_bit_is_set(UCSR0A, UDRE0);
	return 0;
}


#define START_HOLD_DOWN_TIMER TCCR1B |= (1<<CS10) | (1<<CS12);
#define STOP_HOLD_DOWN_TIMER TCCR1B &= ~(1<<CS12) & ~(1<<CS11) & ~(1<<CS10);

#define START_BTN_TIMER TCCR3B |= (1<<CS30) | (1<<CS32);
#define STOP_BTN_TIMER TCCR3B &= ~(1<<CS32) & ~(1<<CS31) & ~(1<<CS30);

void init_hold_down_timer();
void init_btn_timer();

volatile unsigned int btn_pressed_cnt = 0;
volatile uint8_t btn_enabled = 0;
volatile uint8_t btn_holded_down = 0;
volatile uint8_t btn_pressed = 0;
volatile uint8_t btn_counter = 0;

int main(void)
{
    /* Replace with your application code */
	init_btn_timer();
	init_hold_down_timer();
	debug_usart_init();
	stdout = &mystdout;
	btn_enabled = 1;
    DDRA &= ~(1<<PA4);
	sei();
	printf("STARTED!\n");
	while (1)
	{
		if( !(PINA & (1<<PA4)) && btn_enabled ){
			//asm("nop");
			btn_enabled = 0;
			START_HOLD_DOWN_TIMER;					
		}
		if(btn_holded_down){
			printf("btn holded down\n");			
			btn_holded_down = 0;
			START_BTN_TIMER;
		}
		if(btn_pressed){
			printf("btn pressed %d\n", btn_counter);
			btn_pressed = 0;
		}

    }
}

void init_hold_down_timer(){
	OCR1A = 156;
	TIMSK1 |= (1<<OCIE1A);
}
ISR(TIMER1_COMPA_vect){
	TCNT1 = 0;
	if( !(PINA & (1<<PA4)) ){
		btn_pressed_cnt ++;
		if(btn_pressed_cnt == 300){
			btn_holded_down = 1;			
			btn_pressed_cnt = 0;			
			TCNT1 = 0;
			STOP_HOLD_DOWN_TIMER;
		}
	}else{
		if(btn_pressed_cnt > 10){
			btn_counter++;
			btn_pressed = 1;
			btn_pressed_cnt = 0;
			btn_enabled = 1;
			TCNT1 = 0;
			STOP_HOLD_DOWN_TIMER;
		}
		
	}
	
}

void init_btn_timer(){
	OCR3A = 7812;
	TIMSK3 |= (1<<OCIE3A);
}
ISR(TIMER3_COMPA_vect){
	btn_enabled = 1;
	TCNT3 = 0;
	STOP_BTN_TIMER;
}
  • Вопрос задан
  • 3483 просмотра
Пригласить эксперта
Ответы на вопрос 2
@Vasilii_B2
Разрабатываю ПО и электронику
Э... круто накручено аж 2 таймера, кнопка должна обзавидоваться что столько кода для нее одной сделано))), респект за мега кодовый замес!!!
Я бы предложил использовать 1 переменную счетчик: при нажатой кнопке счетчик увеличивается с шагом 1, при отпущенной уменьшается (здесь могут быть большие шаги), если счетчик превысил какой-то порог - объявляю что кнопка нажата, далее пока переменная счетчик не докатится до нуля- не разрешаю объявлять что кнопка нажата. Опрос кнопки по таймеру либо делей если не существенно.
Ответ написан
@OSBoy
Я, например, вот так сделал (для обработки коротких и длинных нажатий трёх кнопок):
uint8_t buttonFlags = 0; // буфер флагов зарегистрированных нажатий

// ОПРОС КНОПОК
void buttons_check(void)
{
	uint8_t buttonState = 0; // буфер для текущего состояния кнопок
	static uint8_t buttonPressCounter[] = { 0, 0, 0 }; // счётчики продолжительности нажатия кнопок
	buttonState = ( BUTTON0_STATE | BUTTON1_STATE << 1 | BUTTON2_STATE << 2 ) ^ 0x07;
	for ( uint8_t i = 0; i <= 2; i++ ) // для каждой из 3 кнопок проверяем:
	{
		if ( ((buttonState >> i) & 1) ) // если кнопка нажата
		{
			buttonPressCounter[i]++; // увеличиваем значение счётчика
			if ( buttonPressCounter[i] == 3 ) // если кнопка нажата около 50мс
			{
				buttonFlags |= ( 1 << i ); // фиксируем короткое нажатие
			}
			else if ( buttonPressCounter[i] == 95 ) // если кнопка нажата около 1.5 секунд
			{
				buttonFlags |= ( 1 << i ) | ( 1 << (i+3) ); // фиксируем длинное нажатие
				buttonPressCounter[i] = 80; // повторяем действие длинного нажатия при удерживании кнопки (примерно 4 раза в секунду)
			}
			else
			{
			buttonFlags &= ~( 1 << i ); // сбрасываем флаг короткого нажатия кнопки
			}
		}
		else // если кнопка отпущена
		{
			buttonPressCounter[i] = 0; // сбрасываем счётчик
			buttonFlags &= ~( ( 1 << i ) | ( 1 << (i+3)) ); // сбрасываем флаги нажатия кнопки
		}
	}
}

Функция вызывается в теле обработчика прерывания по таймеру, перед функцией - обработчиком нажатий, в зависимости от текущего состояния переменной buttonFlags, и там же, делаем другие нужные нам вещи, типа обновления информации на дисплее, и т.д.) Таким образом, всего одним таймером убиваем сразу всех зайцев :)
P.S. Значения счётчиков - 3, 80 и 95 - с расчётом, что прерывание по таймеру срабатывает каждые 16мс.
P.P.S. Если чё, сильно не пинять - я ни разу не программист, а так, балуюсь немножко ))
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы