/** * Lab 3 * * This is my solution to the lab 3 assignment. * There are multiple structs and enums defined in this file. * * time_struct is used to store the start time and current time for * time calculations. * * btn_status struct is used to keep track of the number of times the * button is pressed and released. This is used to implement a debounce * algorithm. * * btn_state enum is used to return the states of the button, used to check * for errors and to return the state of the button. * * This firmware will use GPIO 15 as a pseudo-button (pull-up) and * will start a timer on button press (falling edge) and stop the timer * on button release (rising edge). * * The Timer will print the elapsed time every second as required by the * lab specification. * * The DEBOUNCE_US macro is used to set the debounce delay to 20ms, the value * can be refined to get more accurate results. * * This is made more complex with the use of interrupts, timers and a debounce * algorithm. * */ #include #include "pico/stdlib.h" #include "hardware/gpio.h" #define PSEUDO_BTN 15 // GPIO 15 is the pseudo-button #define TIMER_US 1000000 // 1-second delay for repeating timer #define DEBOUNCE_US 20000 // 20ms debounce delay struct repeating_timer timer; // Repeating timer struct time_struct { uint64_t start_time; uint64_t current_time; } time_struct = {0, 0}; struct btn_status { // Button status (Used to implement debounce) uint btn_pressed; uint btn_released; } btn_status = {0, 0}; typedef enum btn_state { // Button state (Used as return value) PRESSED = 0, RELEASED = 1, ERROR = -1 } btn_state_t; /** * Callback function for repeating timer * This function will print the elapsed time every second as required by the * lab specification. * @param t The timer that triggered the callback * @return True to keep the timer running, false to stop it */ bool elapsed_time_callback (struct repeating_timer *t) { time_struct.start_time = (time_struct.start_time == 0) ? time_us_64() : time_struct.start_time; time_struct.current_time = time_us_64(); printf("%llus\n", (time_struct.current_time - time_struct.start_time) / 1000000); return true; } /** * Get the state of the pseudo-button (pull-up) * This function will implement a debounce algorithm to * get the state of the pseudo-button. (With added complexity) * @return The state of the pseudo-button */ btn_state_t get_btn_state () { if (btn_status.btn_pressed != 0 || btn_status.btn_released != 0) { return ERROR; } u_int64_t start_time = time_us_64(); while (time_us_64() - start_time < DEBOUNCE_US) { if (gpio_get(PSEUDO_BTN)) { btn_status.btn_released += 1; } else { btn_status.btn_pressed += 1; } } btn_state_t state = (btn_status.btn_pressed > btn_status.btn_released) ? PRESSED : RELEASED; // Reset btn_status btn_status = (struct btn_status) {0, 0}; return state; } /** * Interrupt callback function for pseudo-button (pull-up) * This function will start the timer on button press (falling edge) * and stop the timer on button release. (rising edge) * @param gpio The GPIO pin that triggered the interrupt * @param events The type of interrupt that triggered the callback */ void gpio_callback (uint gpio, uint32_t events) { btn_state_t btn_state = get_btn_state(); if (btn_state == ERROR) { printf("Error: Button is bouncing\n"); return; } if (events == GPIO_IRQ_EDGE_FALL && btn_state == PRESSED) { printf("Timer Started\n"); add_repeating_timer_us( TIMER_US, elapsed_time_callback, NULL, &timer ); } if (events == GPIO_IRQ_EDGE_RISE && btn_state == RELEASED) { if (cancel_repeating_timer(&timer)) { printf("Timer Stopped\n"); printf("Final Elapsed Time: %llus\n", (time_struct.current_time - time_struct.start_time) / 1000000); } time_struct = (struct time_struct) {0, 0}; } } /** * Initialize the pseudo-button (pull-up) * The pseudo-button is connected to GPIO 15 and * is configured as an input with a pull-up resistor. * * The interrupt is configured to trigger on both * rising and falling edges. */ static void btn_init () { gpio_init(PSEUDO_BTN); gpio_set_dir(PSEUDO_BTN, GPIO_IN); gpio_set_pulls(PSEUDO_BTN, true, false); gpio_set_irq_enabled_with_callback( PSEUDO_BTN, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, &gpio_callback ); } int main () { stdio_init_all(); btn_init(); while (true) { tight_loop_contents(); } }