diff --git a/lab_3/lab_3.c b/lab_3/lab_3.c index 33f9f1d..48fded1 100644 --- a/lab_3/lab_3.c +++ b/lab_3/lab_3.c @@ -1,17 +1,31 @@ /** * Lab 3 - * To develop a simple stopwatch that measures time intervals between - * button presses. * - * On pressing the START pseudo-button (GP15), - * the stopwatch will begin, - * and the elapsed time will be displayed every second on - * the Serial Monitor (or equivalent). + * This is my solution to the lab 3 assignment. + * There are multiple structs and enums defined in this file. * - * Releasing the START pseudo-button will stop the timer and - * reset the elapsed time to zero. + * time_struct is used to store the start time and current time for + * time calculations. * - * The START pseudo-button must incorporate a debouncing algorithm. + * 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. * */ @@ -21,21 +35,26 @@ #define PSEUDO_BTN 15 // GPIO 15 is the pseudo-button #define TIMER_US 1000000 // 1-second delay for repeating timer -#define DEBOUNCE_US 100 // 100us debounce time +#define DEBOUNCE_US 20000 // 20ms debounce delay struct repeating_timer timer; // Repeating timer -u_int64_t time = 0; // Elapsed time -typedef enum btn_state { // Button state - PRESSED = 0, - RELEASED = 1 -} btn_state_t; +struct time_struct { + uint64_t start_time; + uint64_t current_time; +} time_struct = {0, 0}; -struct btn_status { // Button status +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 @@ -44,9 +63,14 @@ struct btn_status { // Button status * @return True to keep the timer running, false to stop it */ bool elapsed_time_callback (struct repeating_timer *t) { - time++; - // Print elapsed time every second - printf("Elapsed Time: %llus\n", time); + 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; } @@ -57,7 +81,13 @@ bool elapsed_time_callback (struct repeating_timer *t) { * @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)) @@ -71,7 +101,7 @@ btn_state_t get_btn_state () { } btn_state_t state = (btn_status.btn_pressed > btn_status.btn_released) ? - PRESSED : RELEASED; + PRESSED : RELEASED; // Reset btn_status btn_status = (struct btn_status) {0, 0}; @@ -89,11 +119,15 @@ btn_state_t get_btn_state () { 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("\n====================================\n"); printf("Timer Started\n"); - printf("====================================\n"); add_repeating_timer_us( TIMER_US, @@ -101,31 +135,29 @@ void gpio_callback (uint gpio, uint32_t events) { NULL, &timer ); - } if (events == GPIO_IRQ_EDGE_RISE && btn_state == RELEASED) { - cancel_repeating_timer(&timer); - - // Print elapsed time and reset - printf("\n====================================\n"); - printf("Timer Stopped\n"); - printf("====================================\n"); - printf("Final Elapsed Time: %llus\n", time); - printf("====================================\n"); - - time = 0; + 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. + * 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);