Search code examples
carduinoembeddedavr

Why do I need to put the millis() function inside the main loop to trigger the ISR?


I'm trying to replicate that earbuds functionality that consists in a multifunctional button. If you press this button x amount of times, in a period of time, the earbud will do something depending on how many times you press the button.The problem is that I need to put millis() inside the main loop to trigger the ISR that increments the millis variable: ISR (TIMER0_COMPA_vect) and I don't understand why

These are my main code and libraries.

#include <mcu_init.h>
#include <millis.h>
#include <button.h>

int main() {

    GPIO_init();

    cli();

    INT0_init();
    TMR0_init();

    sei();

    while (1) {

        // If I put millis() here, ISR (TIMER0_COMPA_vect) starts triggering
        handleButton();
        
    }

    return 0;

}
#include <millis.h>

static volatile uint32_t miliseconds = 0;

ISR (TIMER0_COMPA_vect) {
    miliseconds++;
    PORTA |= (1 << 1);

}

uint32_t millis() {

    uint32_t time;
    uint8_t oldSREG = SREG;

    cli();

    time = miliseconds;
    SREG = oldSREG;

    return time;

}

This is the logic of the button, just in case.

#include <button.h>

volatile uint8_t count = 0;
volatile uint32_t lastPressTime = 0;

ISR (INT0_vect) {

    if (millis() - lastPressTime >= 200) {
        count++;
      lastPressTime = millis();

    }

}

void handleButton() {
  
  if (millis() - lastPressTime >= 1500) {
    lastPressTime = millis();

    switch (count) {

      case 2:
        PORTA ^= (1 << 1);
        count = 0;
        break;

      case 3:
        PORTA ^= (1 << 3);
        count = 0;
        break;

      default:
        count = 0;

    }

    }

    return;

}

These are the header files

#ifndef MILLIS_H_
#define MILLIS_H_

#include <avr/interrupt.h>

uint32_t millis();

#endif
#ifndef BUTTON_H_
#define BUTTON_H_

#include <millis.h>

void handleButton();

#endif

Solution

  • In your handleButton function, you reset the count variable every time you enter the if() block.

    If you do not use the millis() function, i.e. you execute the switch() at every main loop, the count variable will probably be reset to zero again by the main as soon as your interrupt routine has incremented it from zero, has returned, and the main loop is executed again.

    If you want to reset the variable count after a timeout occurs and no further press apart the first happens, you need to handle some states, like in the following code:

    // as in your code, the minimum acceptable time between press events
    #define def_click_debounce_ms 200
    // after a press event occurs, if no more press events occur after this time, we assume that the user press sequence has completed
    #define def_pause_validate_click_ms (def_click_debounce_ms + 1000)
    
    // your ISR for the press event, just enriched with a signal on too short press
    ISR (INT0_vect) {
    
        if (millis() - lastPressTime >= def_click_debounce_ms) {
            // increment the number of press detected
            count++;
            lastPressTime = millis();
        }
        else
        {
            // signal to main that an invalid press happened
            reset_wait_release_request++;
        }
    }
    
    // the status of handleButton check 
    typedef enum _enum_check_button_press_status
    {
        enum_check_button_press_status_init = 0,
        enum_check_button_press_status_check_press_sequence,
        enum_check_button_press_status_numof
    }enum_check_button_press_status;
    
    // our check button press status variable
    enum_check_button_press_status e_check_button_press_status = enum_check_button_press_status_init;
    // we use this variable to keep the number of press events handleButton detects
    uint8_t num_consecutive_press_detected = 0;
    
    void handleButton() {
        // store the current time
        const uint32_t now_ms = millis();
        switch(e_check_button_press_status)
        {
        case enum_check_button_press_status_init:
        default:
        {
            // no press detected at this moment
            num_consecutive_press_detected = 0;
            // check for a press sequence
            e_check_button_press_status = enum_check_button_press_status_check_press_sequence;
            break;
        }
        case enum_check_button_press_status_check_press_sequence:
        {
            // has the interrupt routine detected a new press?
            if (count != main_count)
            {
                // align our own press counter
                main_count = count;
                // increment the number of press detected by the main
                num_consecutive_press_detected++;
                // mark the time of the last press detected by the main
                start_wait_release_ms = now_ms;
            }
            // if an invalid press happened, let's restart our release timer
            else if (reset_wait_release_request != reset_wait_release_ack)
            {
                reset_wait_release_ack = reset_wait_release_request;
                // we restart the time of the last press detected by the main
                start_wait_release_ms = now_ms;
            }
            // if a timeout happens waiting for next press, let's check if it was a valid press sequence
            else if ((num_consecutive_press_detected > 0) && (now_ms - start_wait_release_ms >= def_pause_validate_click_ms))
            {
                // do your stuff
                switch(num_consecutive_press_detected)
                {
                case 0:
                {
                    // should never happen...
                    break;
                }
                case 2:
                {
                    // do the 2-clicks stuff
                    PORTA ^= (1 << 1);
                    break;
                }
                case 3:
                {
                    // do the 3-clicks stuff
                    PORTA ^= (1 << 3);
                    break;
                }
                
                case 1:
                default:
                {
                    printf("Invalid number of press detected: %u\n", (unsigned int)num_consecutive_press_detected);
                    break;
                }
                }
                
                // go back to the init state
                e_check_button_press_status = enum_check_button_press_status_init;
            }
            break;
        }
        }   
    
    }