M
M
MaxEpt2018-11-09 16:20:16
Arduino
MaxEpt, 2018-11-09 16:20:16

How to handle button hold in MK AVR?

Hello ! I sketched a small code that handles pressing and holding a button on a timer. The button is pulled up by a resistor to + 5V and to PORTA4 of the microcontroller.
When 0 appears on PA4, it starts the timer (START_HOLD_DOWN_TIMER), ~ every 10ms in the interrupt, the timer checks the state of the pin. If you hit the condition 300 times, which is ~3 sec. A message is output to the UART btn helded down. If the button was released earlier, we consider that it was just a single press. Also, after the hold is caught (and then the user releases the button), a bounce of contacts may occur, respectively, after releasing the MK, it will record another press. To do this, after holding, another timer is started, which activates the processing (btn_enabled) after ~ 500ms. Please give feedback on the algorithm and the code itself)
The code itself:

#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;
}

Answer the question

In order to leave comments, you need to log in

2 answer(s)
V
Vasily F, 2018-11-15
@Vasilii_B2

Uh ... already 2 timers are coolly wound, the button should be envious that so much code has been done for it alone))), respect for the mega code batch !!!
I would suggest using 1 counter variable: when the button is pressed, the counter increases in increments of 1, when the button is released, it decreases (there may be large steps here), if the counter has exceeded some threshold, I declare that the button is pressed, then until the counter variable reaches zero- I do not allow to declare that the button is pressed. Polling a button on a timer or a delay if not essential.

O
OSBoy, 2019-01-09
@OSBoy

For example, I did this (to handle short and long presses of three buttons):

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)) ); // сбрасываем флаги нажатия кнопки
    }
  }
}

The function is called in the body of the timer interrupt handler, before the click handler function, depending on the current state of the buttonFlags variable , and in the same place, we do other things we need, such as updating information on the display, etc.) Thus, only one timer kill all birds with one stone :)
PS The values ​​of the counters - 3, 80 and 95 - with the expectation that the timer interrupt is triggered every 16ms.
PPS If Che, don't kick too much - I've never been a programmer, and so, I indulge a little))

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question