refactor: State Machine Refinement 3

This commit is contained in:
Frederik Beimgraben 2025-07-03 07:57:54 +02:00
parent 7a7667171c
commit 276ac618b3
2 changed files with 162 additions and 59 deletions

View File

@ -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

View File

@ -4,7 +4,10 @@
* @brief State machine implementation for the beverage dispenser core program.
*
* Tracks pertype 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 <stdint.h>
#include <stdio.h>
#include <msp430.h>
#include "driverlib.h"
#include <driverlib.h>
#include <timer_a.h>
#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 upmode, 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, &param);
/* 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)
{
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 orderquantity 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 unauthorizedopen 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 stockselection 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 */
}