From 276ac618b3ded3cd7a3cfbf3d9fa2cde62e52794 Mon Sep 17 00:00:00 2001 From: Frederik Beimgraben Date: Thu, 3 Jul 2025 07:57:54 +0200 Subject: [PATCH] refactor: State Machine Refinement 3 --- doxygen.conf | 8 +- src/state_machine.c | 213 ++++++++++++++++++++++++++++++++------------ 2 files changed, 162 insertions(+), 59 deletions(-) diff --git a/doxygen.conf b/doxygen.conf index 069d4e3..d571e7d 100644 --- a/doxygen.conf +++ b/doxygen.conf @@ -42,7 +42,7 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "My Project" +PROJECT_NAME = "ESR-Gruppe 11" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version @@ -1087,7 +1087,7 @@ FILE_PATTERNS = *.c \ # be searched for input files as well. # The default value is: NO. -RECURSIVE = NO +RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a @@ -1096,7 +1096,7 @@ RECURSIVE = NO # Note that relative paths are relative to the directory from which Doxygen is # run. -EXCLUDE = +EXCLUDE = driverlib/ # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded @@ -2465,7 +2465,7 @@ SEARCH_INCLUDES = YES # RECURSIVE has no effect here. # This tag requires that the tag SEARCH_INCLUDES is set to YES. -INCLUDE_PATH = +INCLUDE_PATH = src/ # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the diff --git a/src/state_machine.c b/src/state_machine.c index a4e081c..5af30e3 100644 --- a/src/state_machine.c +++ b/src/state_machine.c @@ -4,7 +4,10 @@ * @brief State machine implementation for the beverage dispenser core program. * * Tracks per‐type stock levels, enforces stock limits on orders, - * and provides an “[A-D]/*” menu in IDLE to edit stock via the keypad. + * provides an “[A-D]/*” menu in IDLE to edit stock via the keypad, + * implements buzzer feedback, and drives onboard LEDs: + * – GREEN (P6.6) when the door may be opened (confirmed order), + * – RED (P1.0) otherwise. * * @authors * Frederik Beimgraben @@ -17,18 +20,35 @@ #include #include #include -#include "driverlib.h" +#include +#include #include "keypad.h" #include "lcd.h" #include "door_sensor.h" #include "state_machine.h" #include "timer.h" +#include "Board.h" /** Default initial stock for each beverage. */ #define INITIAL_STOCK_DEFAULT 20U /** Maximum stock representable (uint8_t limit). */ #define MAX_STOCK 255U +/** Buzzer-Settings */ +#define BUZZER_PORT_DIR P5DIR +#define BUZZER_PORT_OUT P5OUT +#define BUZZER_PIN BIT0 +#define BUZZER_PERIOD 1000U + +/** Red LED on P1.0. */ +#define LED_RED_PORT_DIR P1DIR +#define LED_RED_PORT_OUT P1OUT +#define LED_RED_PIN BIT0 +/** Green LED on P6.6. */ +#define LED_GREEN_PORT_DIR P6DIR +#define LED_GREEN_PORT_OUT P6OUT +#define LED_GREEN_PIN BIT6 + /** Last key pressed on the keypad. */ volatile char keypad_last_key = '\0'; /** Flag set when a new key is ready to handle. */ @@ -64,6 +84,12 @@ char buffer_cd[17]; /* —— Function Prototypes —— */ +/* Hardware feedback */ +static void update_leds(void); +void buzz_invalid_input(void); +void sound_alarm(void); +void reset_alarm(void); + /* Keypad input handlers */ static void keypad_handler(char key); void handle_input_idle(unsigned char key); @@ -97,9 +123,6 @@ void ui_draw_stock_select(void); void ui_draw_stock_set(void); void ui_draw_stock_confirmed(void); -/* Error/signal feedback */ -void buzz_invalid_input(void); - /* Door callbacks */ static void door_opened_handler(void); static void door_closed_handler(void); @@ -111,11 +134,34 @@ void handle_general_interrupt(void); /** * @brief Initialize peripherals and draw the initial UI. * - * Registers keypad and door callbacks, resets counts and stock, - * and displays the idle screen. + * Configures buzzer and LED GPIOs, registers callbacks, + * resets counts and stock, and displays the idle screen. */ void sm_init(void) { + /* buzzer pin out */ + BUZZER_PORT_DIR |= BUZZER_PIN; + BUZZER_PORT_OUT &= ~BUZZER_PIN; + + /* set up TB2 in up‐mode, CCR0 interrupt every BUZZER_PERIOD ticks */ + Timer_B_initUpModeParam param = { + .clockSource = TIMER_B_CLOCKSOURCE_SMCLK, + .clockSourceDivider = TIMER_B_CLOCKSOURCE_DIVIDER_1, + .timerPeriod = BUZZER_PERIOD, + //.timerInterruptEnable_TBIE = TIMER_B_TBIE_INTERRUPT_DISABLE, + //.captureCompareInterruptEnable_CCR0_CCIE = TIMER_B_CAPTURECOMPARE_INTERRUPT_ENABLE, + .timerClear = TIMER_B_DO_CLEAR, + .startTimer = true + }; + Timer_B_initUpMode(TB2_BASE, ¶m); + + /* Configure LEDs */ + LED_RED_PORT_DIR |= LED_RED_PIN; + LED_GREEN_PORT_DIR |= LED_GREEN_PIN; + /* ensure off by default */ + LED_RED_PORT_OUT |= LED_RED_PIN; // red on initially + LED_GREEN_PORT_OUT &= ~LED_GREEN_PIN; + keypad_init(keypad_handler); door_init(door_opened_handler, door_closed_handler); @@ -124,6 +170,15 @@ void sm_init(void) stock_a = stock_b = stock_c = stock_d = INITIAL_STOCK_DEFAULT; ui_draw_idle(); + update_leds(); +} + + +#pragma vector = TIMER2_B0_VECTOR +__interrupt void ISR_TB2_CCR0(void) +{ + BUZZER_PORT_OUT ^= BUZZER_PIN; // flip the buzzer output + TB2CCTL0 &= ~CCIFG; // clear the interrupt flag } @@ -139,6 +194,70 @@ void sm_loop(void) } } +bool open_door_allowed(void) { + return ( + current_state == STATE_CONFIRMED || + current_state == STATE_OPEN || + (current_state == STATE_SEL_COUNT && door_open) || + current_state == STATE_EDIT_STOCK_SELECT || + current_state == STATE_EDIT_STOCK_SET || + current_state == STATE_EDIT_STOCK_CONFIRMED + ); +} + +/** + * @brief Update the onboard LEDs. + * + * GREEN on if in STATE_CONFIRMED (door may open), else RED on. + */ +void update_leds(void) +{ + if ( + open_door_allowed() + ) { + /* allow open: green on, red off */ + LED_GREEN_PORT_OUT |= LED_GREEN_PIN; + LED_RED_PORT_OUT &= ~LED_RED_PIN; + } else { + /* otherwise: red on, green off */ + LED_RED_PORT_OUT |= LED_RED_PIN; + LED_GREEN_PORT_OUT &= ~LED_GREEN_PIN; + } +} + + +/** + * @brief Short buzzer beep for invalid input. + */ +void buzz_invalid_input(void) +{ + /* beep 50ms */ + sound_alarm(); + sleep(50); + reset_alarm(); +} + + +/** + * @brief Sound the alarm continuously. + */ +void sound_alarm(void) +{ + TB2CCR0 = BUZZER_PERIOD; + TB2CCTL0 |= CCIE; // enable CCR0 interrupts + TB2CTL = TBSSEL__SMCLK | MC__UP | TBCLR; +} + + +/** + * @brief Stop any sounding alarm. + */ +void reset_alarm(void) +{ + TB2CCTL0 &= ~CCIE; // disable CCR0 interrupts + TB2CTL &= ~MC__UP; // stop TB2 + BUZZER_PORT_OUT &= ~BUZZER_PIN; // make sure buzzer pin is low +} /** * @brief Keypad interrupt handler. @@ -169,7 +288,7 @@ static void keypad_handler(char key) handle_input_edit_stock_set(key); break; case STATE_EDIT_STOCK_CONFIRMED: - transition(STATE_IDLE); + transition(STATE_EDIT_STOCK_SELECT); break; default: buzz_invalid_input(); @@ -221,13 +340,6 @@ void handle_input_sel_count(unsigned char key) } else if (!any_selected()) { transition(STATE_NONE_SELECTED); } else { - /* Deduct stock */ - switch (selected_bev) { - case BEV_A: stock_a -= count_a; break; - case BEV_B: stock_b -= count_b; break; - case BEV_C: stock_c -= count_c; break; - case BEV_D: stock_d -= count_d; break; - } transition(STATE_CONFIRMED); } } @@ -332,22 +444,22 @@ void update_amount(uint8_t digit) switch (selected_bev) { case BEV_A: tentative = count_a * 10 + digit; - if (tentative <= stock_a) count_a = (uint8_t)tentative; + if (tentative <= stock_a) count_a = tentative; else buzz_invalid_input(); break; case BEV_B: tentative = count_b * 10 + digit; - if (tentative <= stock_b) count_b = (uint8_t)tentative; + if (tentative <= stock_b) count_b = tentative; else buzz_invalid_input(); break; case BEV_C: tentative = count_c * 10 + digit; - if (tentative <= stock_c) count_c = (uint8_t)tentative; + if (tentative <= stock_c) count_c = tentative; else buzz_invalid_input(); break; case BEV_D: tentative = count_d * 10 + digit; - if (tentative <= stock_d) count_d = (uint8_t)tentative; + if (tentative <= stock_d) count_d = tentative; else buzz_invalid_input(); break; } @@ -413,12 +525,14 @@ bool any_selected(void) /** - * @brief Redraw the UI if the state has changed. + * @brief Redraw the UI and update LEDs if the state has changed. */ void handle_general_interrupt(void) { if (!state_chg) return; + update_leds(); + switch (current_state) { case STATE_IDLE: ui_draw_idle(); break; case STATE_SEL_COUNT: ui_draw_sel_count(); break; @@ -454,9 +568,13 @@ void transition(State_t next) */ static void door_opened_handler(void) { - if (current_state != STATE_CONFIRMED) { + if (!open_door_allowed()) { transition(STATE_UNAUTHORIZED); - } else { + } else if ( + current_state != STATE_EDIT_STOCK_SELECT && + current_state != STATE_EDIT_STOCK_SET && + current_state != STATE_EDIT_STOCK_CONFIRMED + ) { transition(STATE_OPEN); } door_open = true; @@ -470,17 +588,30 @@ static void door_opened_handler(void) */ static void door_closed_handler(void) { - reset_amounts(); - transition(STATE_IDLE); - door_open = false; + if ( + current_state != STATE_EDIT_STOCK_SELECT && + current_state != STATE_EDIT_STOCK_SET && + current_state != STATE_EDIT_STOCK_CONFIRMED + ) { + /* Deduct stock */ + stock_a -= count_a; + stock_b -= count_b; + stock_c -= count_c; + stock_d -= count_d; + + reset_amounts(); + transition(STATE_IDLE); + door_open = false; + } } +/* ——— UI Drawing Functions —————————————————————————————— */ + /** * @brief Draw the IDLE screen. * - * Line 1: “Ready!” centered. - * Line 2: “[A-D]/*” menu hint. + * [A-D]/* menu hint. */ void ui_draw_idle(void) { @@ -489,11 +620,8 @@ void ui_draw_idle(void) lcd_set_cursor(1,4); lcd_print("[A-D]/*"); } - /** * @brief Draw the order‐quantity selection screen. - * - * Shows A/B on line 1, C/D on line 2 with “*” marking the selected. */ void ui_draw_sel_count(void) { @@ -509,12 +637,8 @@ void ui_draw_sel_count(void) lcd_set_cursor(1,0); lcd_print(buffer_cd); } - /** * @brief Draw the “order confirmed” screen. - * - * Line 1: “Confirmed!” centered. - * Line 2: “Press [A-D]/*” for next action. */ void ui_draw_confirmed(void) { @@ -523,7 +647,6 @@ void ui_draw_confirmed(void) lcd_set_cursor(1,2); lcd_print("OPEN/[A-D]/*"); } - /** * @brief Draw the unauthorized‐open error. */ @@ -534,7 +657,6 @@ void ui_draw_error(void) lcd_set_cursor(1,3); lcd_print("DOOR OPENED"); } - /** * @brief Draw the “door is open” screen. */ @@ -545,7 +667,6 @@ void ui_draw_open(void) lcd_set_cursor(1,2); lcd_print("[*] to reset"); } - /** * @brief Draw when no beverage was selected at “#”. */ @@ -560,7 +681,6 @@ void ui_draw_none_selected(void) ui_draw_sel_count(); } - /** * @brief Transient “all reset” screen. */ @@ -580,11 +700,8 @@ void ui_draw_reset(void) } } - /** * @brief Draw the stock‐selection screen. - * - * [A-D] picks beverage, [*] cancels. */ void ui_draw_stock_select(void) { @@ -593,12 +710,8 @@ void ui_draw_stock_select(void) lcd_set_cursor(1,4); lcd_print("[A-D]/*"); } - /** * @brief Draw the “enter new stock” screen. - * - * Line 1: “X stock:YYY” current level. - * Line 2: “New:ZZZ /*” entry buffer and cancel. */ void ui_draw_stock_set(void) { @@ -617,7 +730,6 @@ void ui_draw_stock_set(void) lcd_set_cursor(1,0); lcd_print(buffer_cd); } - /** * @brief Draw the “stock updated” confirmation. */ @@ -627,12 +739,3 @@ void ui_draw_stock_confirmed(void) lcd_set_cursor(0,2); lcd_print("Stock Updated"); lcd_set_cursor(1,1); lcd_print("Press any key"); } - - -/** - * @brief Short buzzer tone or LED flash for invalid input. - */ -void buzz_invalid_input(void) -{ - /* stub: implement buzzer or LED indication */ -}