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