INF2004_Labs/lab_3/lab_3.c

185 lines
4.9 KiB
C

/**
* 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 <stdio.h>
#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 { // Time struct (Used for time calculations)
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)
ERROR = -1,
PRESSED = 0,
RELEASED = 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();
}
}