refactor: State Machine Refinement 2

This commit is contained in:
Frederik Beimgraben 2025-07-03 04:43:11 +02:00
parent dc6198f587
commit 7a7667171c
3 changed files with 126 additions and 475 deletions

View File

@ -94,11 +94,6 @@
"command" : "clang++ -I\"/home/frederik/.ti/ccs2010/ccs/ccs_base/msp430/include\" -I\"/home/frederik/workspace_ccstheia/ESR-11\" -I\"/home/frederik/workspace_ccstheia/ESR-11/jsmn\" -I\"/home/frederik/workspace_ccstheia/ESR-11/driverlib/MSP430FR2xx_4xx\" -I\"/home/frederik/.ti/ccs2010/ccs/tools/compiler/ti-cgt-msp430_21.6.1.LTS/include\" -D__MSP430FR2355__ -D_FRWP_ENABLE -D_INFO_FRWP_ENABLE -xc", "command" : "clang++ -I\"/home/frederik/.ti/ccs2010/ccs/ccs_base/msp430/include\" -I\"/home/frederik/workspace_ccstheia/ESR-11\" -I\"/home/frederik/workspace_ccstheia/ESR-11/jsmn\" -I\"/home/frederik/workspace_ccstheia/ESR-11/driverlib/MSP430FR2xx_4xx\" -I\"/home/frederik/.ti/ccs2010/ccs/tools/compiler/ti-cgt-msp430_21.6.1.LTS/include\" -D__MSP430FR2355__ -D_FRWP_ENABLE -D_INFO_FRWP_ENABLE -xc",
"file" : "/home/frederik/workspace_ccstheia/ESR-11/driverlib/MSP430FR2xx_4xx/tia.c" "file" : "/home/frederik/workspace_ccstheia/ESR-11/driverlib/MSP430FR2xx_4xx/tia.c"
}, },
{
"directory" : "/home/frederik/workspace_ccstheia/ESR-11/Debug",
"command" : "clang++ -I\"/home/frederik/.ti/ccs2010/ccs/ccs_base/msp430/include\" -I\"/home/frederik/workspace_ccstheia/ESR-11\" -I\"/home/frederik/workspace_ccstheia/ESR-11/jsmn\" -I\"/home/frederik/workspace_ccstheia/ESR-11/driverlib/MSP430FR2xx_4xx\" -I\"/home/frederik/.ti/ccs2010/ccs/tools/compiler/ti-cgt-msp430_21.6.1.LTS/include\" -D__MSP430FR2355__ -D_FRWP_ENABLE -D_INFO_FRWP_ENABLE -xc",
"file" : "/home/frederik/workspace_ccstheia/ESR-11/driverlib/MSP430FR2xx_4xx/timer_a.c"
},
{ {
"directory" : "/home/frederik/workspace_ccstheia/ESR-11/Debug", "directory" : "/home/frederik/workspace_ccstheia/ESR-11/Debug",
"command" : "clang++ -I\"/home/frederik/.ti/ccs2010/ccs/ccs_base/msp430/include\" -I\"/home/frederik/workspace_ccstheia/ESR-11\" -I\"/home/frederik/workspace_ccstheia/ESR-11/jsmn\" -I\"/home/frederik/workspace_ccstheia/ESR-11/driverlib/MSP430FR2xx_4xx\" -I\"/home/frederik/.ti/ccs2010/ccs/tools/compiler/ti-cgt-msp430_21.6.1.LTS/include\" -D__MSP430FR2355__ -D_FRWP_ENABLE -D_INFO_FRWP_ENABLE -xc", "command" : "clang++ -I\"/home/frederik/.ti/ccs2010/ccs/ccs_base/msp430/include\" -I\"/home/frederik/workspace_ccstheia/ESR-11\" -I\"/home/frederik/workspace_ccstheia/ESR-11/jsmn\" -I\"/home/frederik/workspace_ccstheia/ESR-11/driverlib/MSP430FR2xx_4xx\" -I\"/home/frederik/.ti/ccs2010/ccs/tools/compiler/ti-cgt-msp430_21.6.1.LTS/include\" -D__MSP430FR2355__ -D_FRWP_ENABLE -D_INFO_FRWP_ENABLE -xc",

View File

@ -1,381 +0,0 @@
/* --COPYRIGHT--,BSD
* Copyright (c) 2016, Texas Instruments Incorporated
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Texas Instruments Incorporated nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* --/COPYRIGHT--*/
//*****************************************************************************
//
// timer_a.c - Driver for the timer_a Module.
//
//*****************************************************************************
//*****************************************************************************
//
//! \addtogroup timer_a_api timer_a
//! @{
//
//*****************************************************************************
#include "inc/hw_memmap.h"
#ifdef __MSP430_HAS_TxA7__
#include "timer_a.h"
#include <assert.h>
void Timer_A_startCounter(uint16_t baseAddress,
uint16_t timerMode)
{
HWREG16(baseAddress + OFS_TAxCTL) |= timerMode;
}
void Timer_A_initContinuousMode(uint16_t baseAddress,
Timer_A_initContinuousModeParam *param)
{
HWREG16(baseAddress +
OFS_TAxCTL) &= ~(TIMER_A_CLOCKSOURCE_INVERTED_EXTERNAL_TXCLK +
TIMER_A_UPDOWN_MODE +
TIMER_A_DO_CLEAR +
TIMER_A_TAIE_INTERRUPT_ENABLE +
ID__8
);
HWREG16(baseAddress + OFS_TAxEX0) &= ~TAIDEX_7;
HWREG16(baseAddress + OFS_TAxEX0) |= param->clockSourceDivider & 0x7;
HWREG16(baseAddress + OFS_TAxCTL) |= (param->clockSource +
param->timerClear +
param->timerInterruptEnable_TAIE +
((param->clockSourceDivider >>
3) << 6));
if(param->startTimer)
{
HWREG16(baseAddress + OFS_TAxCTL) |= TIMER_A_CONTINUOUS_MODE;
}
}
void Timer_A_initUpMode(uint16_t baseAddress,
Timer_A_initUpModeParam *param)
{
HWREG16(baseAddress + OFS_TAxCTL) &=
~(TIMER_A_CLOCKSOURCE_INVERTED_EXTERNAL_TXCLK +
TIMER_A_UPDOWN_MODE +
TIMER_A_DO_CLEAR +
TIMER_A_TAIE_INTERRUPT_ENABLE +
ID__8
);
HWREG16(baseAddress + OFS_TAxEX0) &= ~TAIDEX_7;
HWREG16(baseAddress + OFS_TAxEX0) |= param->clockSourceDivider & 0x7;
HWREG16(baseAddress + OFS_TAxCTL) |= (param->clockSource +
param->timerClear +
param->timerInterruptEnable_TAIE +
((param->clockSourceDivider >>
3) << 6));
if(param->startTimer)
{
HWREG16(baseAddress + OFS_TAxCTL) |= TIMER_A_UP_MODE;
}
if(TIMER_A_CCIE_CCR0_INTERRUPT_ENABLE ==
param->captureCompareInterruptEnable_CCR0_CCIE)
{
HWREG16(baseAddress +
OFS_TAxCCTL0) |= TIMER_A_CCIE_CCR0_INTERRUPT_ENABLE;
}
else
{
HWREG16(baseAddress +
OFS_TAxCCTL0) &= ~TIMER_A_CCIE_CCR0_INTERRUPT_ENABLE;
}
HWREG16(baseAddress + OFS_TAxCCR0) = param->timerPeriod;
}
void Timer_A_initUpDownMode(uint16_t baseAddress,
Timer_A_initUpDownModeParam *param)
{
HWREG16(baseAddress + OFS_TAxCTL) &=
~(TIMER_A_CLOCKSOURCE_INVERTED_EXTERNAL_TXCLK +
TIMER_A_UPDOWN_MODE +
TIMER_A_DO_CLEAR +
TIMER_A_TAIE_INTERRUPT_ENABLE +
ID__8
);
HWREG16(baseAddress + OFS_TAxEX0) &= ~TAIDEX_7;
HWREG16(baseAddress + OFS_TAxEX0) |= param->clockSourceDivider & 0x7;
HWREG16(baseAddress + OFS_TAxCTL) |= (param->clockSource +
param->timerClear +
param->timerInterruptEnable_TAIE +
((param->clockSourceDivider >>
3) << 6));
if(param->startTimer)
{
HWREG16(baseAddress + OFS_TAxCTL) |= TIMER_A_UPDOWN_MODE;
}
if(TIMER_A_CCIE_CCR0_INTERRUPT_ENABLE ==
param->captureCompareInterruptEnable_CCR0_CCIE)
{
HWREG16(baseAddress +
OFS_TAxCCTL0) |= TIMER_A_CCIE_CCR0_INTERRUPT_ENABLE;
}
else
{
HWREG16(baseAddress +
OFS_TAxCCTL0) &= ~TIMER_A_CCIE_CCR0_INTERRUPT_ENABLE;
}
HWREG16(baseAddress + OFS_TAxCCR0) = param->timerPeriod;
}
void Timer_A_initCaptureMode(uint16_t baseAddress,
Timer_A_initCaptureModeParam *param)
{
HWREG16(baseAddress + param->captureRegister) |= CAP;
HWREG16(baseAddress + param->captureRegister) &=
~(TIMER_A_CAPTUREMODE_RISING_AND_FALLING_EDGE +
TIMER_A_CAPTURE_INPUTSELECT_Vcc +
TIMER_A_CAPTURE_SYNCHRONOUS +
TIMER_A_DO_CLEAR +
TIMER_A_TAIE_INTERRUPT_ENABLE +
CM_3
);
HWREG16(baseAddress + param->captureRegister) |= (param->captureMode +
param->captureInputSelect
+
param->
synchronizeCaptureSource +
param->
captureInterruptEnable +
param->captureOutputMode
);
}
void Timer_A_initCompareMode(uint16_t baseAddress,
Timer_A_initCompareModeParam *param)
{
HWREG16(baseAddress + param->compareRegister) &= ~CAP;
HWREG16(baseAddress + param->compareRegister) &=
~(TIMER_A_CAPTURECOMPARE_INTERRUPT_ENABLE +
TIMER_A_OUTPUTMODE_RESET_SET
);
HWREG16(baseAddress +
param->compareRegister) |= (param->compareInterruptEnable +
param->compareOutputMode
);
HWREG16(baseAddress + param->compareRegister +
OFS_TAxR) = param->compareValue;
}
void Timer_A_enableInterrupt(uint16_t baseAddress)
{
HWREG16(baseAddress + OFS_TAxCTL) |= TAIE;
}
void Timer_A_disableInterrupt(uint16_t baseAddress)
{
HWREG16(baseAddress + OFS_TAxCTL) &= ~TAIE;
}
uint32_t Timer_A_getInterruptStatus(uint16_t baseAddress)
{
return (HWREG16(baseAddress + OFS_TAxCTL) & TAIFG);
}
void Timer_A_enableCaptureCompareInterrupt(uint16_t baseAddress,
uint16_t captureCompareRegister)
{
HWREG16(baseAddress + captureCompareRegister) |= CCIE;
}
void Timer_A_disableCaptureCompareInterrupt(uint16_t baseAddress,
uint16_t captureCompareRegister)
{
HWREG16(baseAddress + captureCompareRegister) &= ~CCIE;
}
uint32_t Timer_A_getCaptureCompareInterruptStatus(uint16_t baseAddress,
uint16_t captureCompareRegister,
uint16_t mask)
{
return (HWREG16(baseAddress + captureCompareRegister) & mask);
}
void Timer_A_clear(uint16_t baseAddress)
{
HWREG16(baseAddress + OFS_TAxCTL) |= TACLR;
}
uint8_t Timer_A_getSynchronizedCaptureCompareInput(uint16_t baseAddress,
uint16_t captureCompareRegister,
uint16_t synchronized)
{
if(HWREG16(baseAddress + captureCompareRegister) & synchronized)
{
return (TIMER_A_CAPTURECOMPARE_INPUT_HIGH);
}
else
{
return (TIMER_A_CAPTURECOMPARE_INPUT_LOW);
}
}
uint8_t Timer_A_getOutputForOutputModeOutBitValue(uint16_t baseAddress,
uint16_t captureCompareRegister)
{
if(HWREG16(baseAddress + captureCompareRegister) & OUT)
{
return (TIMER_A_OUTPUTMODE_OUTBITVALUE_HIGH);
}
else
{
return (TIMER_A_OUTPUTMODE_OUTBITVALUE_LOW);
}
}
uint16_t Timer_A_getCaptureCompareCount(uint16_t baseAddress,
uint16_t captureCompareRegister)
{
return (HWREG16(baseAddress + OFS_TAxR + captureCompareRegister));
}
void Timer_A_setOutputForOutputModeOutBitValue(uint16_t baseAddress,
uint16_t captureCompareRegister,
uint8_t outputModeOutBitValue)
{
HWREG16(baseAddress + captureCompareRegister) &= ~OUT;
HWREG16(baseAddress + captureCompareRegister) |= outputModeOutBitValue;
}
void Timer_A_outputPWM(uint16_t baseAddress,
Timer_A_outputPWMParam *param)
{
HWREG16(baseAddress + OFS_TAxCTL) &=
~(TIMER_A_CLOCKSOURCE_INVERTED_EXTERNAL_TXCLK +
TIMER_A_UPDOWN_MODE + TIMER_A_DO_CLEAR +
TIMER_A_TAIE_INTERRUPT_ENABLE +
ID__8
);
HWREG16(baseAddress + OFS_TAxEX0) &= ~TAIDEX_7;
HWREG16(baseAddress + OFS_TAxEX0) |= param->clockSourceDivider & 0x7;
HWREG16(baseAddress + OFS_TAxCTL) |= (param->clockSource +
TIMER_A_UP_MODE +
TIMER_A_DO_CLEAR +
((param->clockSourceDivider >>
3) << 6));
HWREG16(baseAddress + OFS_TAxCCR0) = param->timerPeriod;
HWREG16(baseAddress + OFS_TAxCCTL0) &=
~(TIMER_A_CAPTURECOMPARE_INTERRUPT_ENABLE +
TIMER_A_OUTPUTMODE_RESET_SET);
HWREG16(baseAddress + param->compareRegister) |= param->compareOutputMode;
HWREG16(baseAddress + param->compareRegister + OFS_TAxR) = param->dutyCycle;
}
void Timer_A_stop(uint16_t baseAddress)
{
HWREG16(baseAddress + OFS_TAxCTL) &= ~MC_3;
}
void Timer_A_setCompareValue(uint16_t baseAddress,
uint16_t compareRegister,
uint16_t compareValue)
{
HWREG16(baseAddress + compareRegister + OFS_TAxR) = compareValue;
}
void Timer_A_setOutputMode(uint16_t baseAddress,
uint16_t compareRegister,
uint16_t compareOutputMode)
{
uint16_t temp = HWREG16(baseAddress + compareRegister);
HWREG16(baseAddress +
compareRegister) = temp & ~(OUTMOD_7) | compareOutputMode;
}
void Timer_A_clearTimerInterrupt(uint16_t baseAddress)
{
HWREG16(baseAddress + OFS_TAxCTL) &= ~TAIFG;
}
void Timer_A_clearCaptureCompareInterrupt(uint16_t baseAddress,
uint16_t captureCompareRegister)
{
HWREG16(baseAddress + captureCompareRegister) &= ~CCIFG;
}
uint16_t Timer_A_getCounterValue(uint16_t baseAddress)
{
uint16_t voteOne, voteTwo, res;
voteTwo = HWREG16(baseAddress + OFS_TAxR);
do
{
voteOne = voteTwo;
voteTwo = HWREG16(baseAddress + OFS_TAxR);
if(voteTwo > voteOne)
{
res = voteTwo - voteOne;
}
else if(voteOne > voteTwo)
{
res = voteOne - voteTwo;
}
else
{
res = 0;
}
}
while(res > TIMER_A_THRESHOLD);
return(voteTwo);
}
#endif
//*****************************************************************************
//
//! Close the doxygen group for timer_a_api
//! @}
//
//*****************************************************************************

View File

@ -4,7 +4,7 @@
* @brief State machine implementation for the beverage dispenser core program. * @brief State machine implementation for the beverage dispenser core program.
* *
* Tracks pertype stock levels, enforces stock limits on orders, * Tracks pertype stock levels, enforces stock limits on orders,
* and provides an * menu in IDLE to edit stock via the keypad. * and provides an [A-D]/*” menu in IDLE to edit stock via the keypad.
* *
* @authors * @authors
* Frederik Beimgraben * Frederik Beimgraben
@ -24,20 +24,19 @@
#include "state_machine.h" #include "state_machine.h"
#include "timer.h" #include "timer.h"
/* ——— Configurable Defaults ———————————————————————————————— */ /** Default initial stock for each beverage. */
#define INITIAL_STOCK_DEFAULT 20U /**< Default stock for each beverage */ #define INITIAL_STOCK_DEFAULT 20U
#define MAX_STOCK 255U /**< Maximum stock representable */ /** Maximum stock representable (uint8_t limit). */
#define MAX_STOCK 255U
/* ——— Internal State Variables —————————————————————————————— */
/** Last key pressed on the keypad. */ /** Last key pressed on the keypad. */
volatile char keypad_last_key = '\0'; volatile char keypad_last_key = '\0';
/** Flag set when a new key is ready to handle. */ /** Flag set when a new key is ready to handle. */
volatile bool keypad_flag_chg = false; volatile bool keypad_flag_chg = false;
/** True if the door is currently open. */ /** True if the door is currently open. */
volatile bool door_open = false; volatile bool door_open = false;
/** True if the state has just changed and UI needs redrawing. */ /** True if the state has just changed and UI needs redrawing. */
volatile bool state_chg = false; volatile bool state_chg = false;
/** Current UI state. */ /** Current UI state. */
volatile State_t current_state = STATE_IDLE; volatile State_t current_state = STATE_IDLE;
@ -59,12 +58,14 @@ volatile uint8_t stock_d = INITIAL_STOCK_DEFAULT;
/** Temporary buffer for building a new stock value. */ /** Temporary buffer for building a new stock value. */
volatile uint8_t new_stock_count = 0; volatile uint8_t new_stock_count = 0;
/** LCD line buffers. */ /** LCD line buffers (must be at least 17 bytes for 16-char + null). */
char buffer_ab[17]; char buffer_ab[17];
char buffer_cd[17]; char buffer_cd[17];
/* ——— Function Prototypes ———————————————————————————————— */ /* —— Function Prototypes —— */
/* Keypad input handlers */ /* Keypad input handlers */
static void keypad_handler(char key);
void handle_input_idle(unsigned char key); void handle_input_idle(unsigned char key);
void handle_input_sel_count(unsigned char key); void handle_input_sel_count(unsigned char key);
void handle_input_confirmed(unsigned char key); void handle_input_confirmed(unsigned char key);
@ -103,24 +104,21 @@ void buzz_invalid_input(void);
static void door_opened_handler(void); static void door_opened_handler(void);
static void door_closed_handler(void); static void door_closed_handler(void);
/* Keypad Callback */
static void keypad_handler(char key);
/* General interrupt dispatcher */ /* General interrupt dispatcher */
void handle_general_interrupt(void); void handle_general_interrupt(void);
/* ——— Implementation ————————————————————————————————————— */
/** /**
* @brief Initialize peripherals and draw the initial UI. * @brief Initialize peripherals and draw the initial UI.
*
* Registers keypad and door callbacks, resets counts and stock,
* and displays the idle screen.
*/ */
void sm_init(void) void sm_init(void)
{ {
/* register keypad + door callbacks */ keypad_init(keypad_handler);
keypad_init(/* callback = */ keypad_handler);
door_init(door_opened_handler, door_closed_handler); door_init(door_opened_handler, door_closed_handler);
/* start in IDLE with default stocks */
reset_amounts(); reset_amounts();
reset_new_stock(); reset_new_stock();
stock_a = stock_b = stock_c = stock_d = INITIAL_STOCK_DEFAULT; stock_a = stock_b = stock_c = stock_d = INITIAL_STOCK_DEFAULT;
@ -128,23 +126,25 @@ void sm_init(void)
ui_draw_idle(); ui_draw_idle();
} }
/** /**
* @brief Main event loop: lowpower + interrupts. * @brief Main event loop: lowpower mode with interrupts.
*/ */
void sm_loop(void) void sm_loop(void)
{ {
for (;;) { for (;;) {
__bis_SR_register(LPM0_bits | GIE); /* sleep until interrupt */ __bis_SR_register(LPM0_bits | GIE);
__no_operation(); __no_operation();
handle_general_interrupt(); handle_general_interrupt();
} }
} }
/** /**
* @brief Keypad interrupt handler. * @brief Keypad interrupt handler.
* @param key ASCII code of the pressed key. * @param key ASCII code of the pressed key.
* *
* Routes key to the correct statespecific handler. * Routes key to the statespecific handler.
*/ */
static void keypad_handler(char key) static void keypad_handler(char key)
{ {
@ -169,7 +169,6 @@ static void keypad_handler(char key)
handle_input_edit_stock_set(key); handle_input_edit_stock_set(key);
break; break;
case STATE_EDIT_STOCK_CONFIRMED: case STATE_EDIT_STOCK_CONFIRMED:
/* any key closes confirmation */
transition(STATE_IDLE); transition(STATE_IDLE);
break; break;
default: default:
@ -178,12 +177,12 @@ static void keypad_handler(char key)
} }
} }
/** /**
* @brief Handle keypress when in STATE_IDLE. * @brief Handle keypress in STATE_IDLE.
* @param key ASCII of pressed key. * @param key ASCII of pressed key.
* *
* AD start ordering that beverage. * [A-D] to order, [*] to enter stockedit menu.
* * enters stockediting menu.
*/ */
void handle_input_idle(unsigned char key) void handle_input_idle(unsigned char key)
{ {
@ -202,12 +201,13 @@ void handle_input_idle(unsigned char key)
} }
} }
/** /**
* @brief Handle keypress when selecting order quantity. * @brief Handle keypress in STATE_SEL_COUNT.
* @param key ASCII of pressed key. * @param key ASCII of pressed key.
* *
* Digits build count (up to stock), # confirms, * clears, * Digits build an order (up to stock), [#] confirms, [*] clears,
* anything else falls back to idlehandler. * others fall back to idle.
*/ */
void handle_input_sel_count(unsigned char key) void handle_input_sel_count(unsigned char key)
{ {
@ -221,7 +221,7 @@ void handle_input_sel_count(unsigned char key)
} else if (!any_selected()) { } else if (!any_selected()) {
transition(STATE_NONE_SELECTED); transition(STATE_NONE_SELECTED);
} else { } else {
/* deduct stock before confirming */ /* Deduct stock */
switch (selected_bev) { switch (selected_bev) {
case BEV_A: stock_a -= count_a; break; case BEV_A: stock_a -= count_a; break;
case BEV_B: stock_b -= count_b; break; case BEV_B: stock_b -= count_b; break;
@ -240,12 +240,12 @@ void handle_input_sel_count(unsigned char key)
} }
} }
/** /**
* @brief Handle keypress in STATE_CONFIRMED (and STATE_OPEN). * @brief Handle keypress in STATE_CONFIRMED/STATE_OPEN.
* @param key ASCII of pressed key. * @param key ASCII of pressed key.
* *
* AD reselect beverage for new order. * [A-D] to start new order, [*] to reset all counts.
* * resets all order counts.
*/ */
void handle_input_confirmed(unsigned char key) void handle_input_confirmed(unsigned char key)
{ {
@ -264,11 +264,12 @@ void handle_input_confirmed(unsigned char key)
} }
} }
/** /**
* @brief Handle keypress in STATE_EDIT_STOCK_SELECT. * @brief Handle keypress in STATE_EDIT_STOCK_SELECT.
* @param key ASCII pressed. * @param key ASCII of pressed key.
* *
* AD pick which stock to edit, * cancels to IDLE. * Select which beverage stock to edit [A-D], or [*] to cancel.
*/ */
void handle_input_edit_stock_select(unsigned char key) void handle_input_edit_stock_select(unsigned char key)
{ {
@ -287,11 +288,12 @@ void handle_input_edit_stock_select(unsigned char key)
} }
} }
/** /**
* @brief Handle keypress in STATE_EDIT_STOCK_SET. * @brief Handle keypress in STATE_EDIT_STOCK_SET.
* @param key ASCII pressed. * @param key ASCII of pressed key.
* *
* Digits build new stock, # confirms update, * cancels. * Digits build new stock, [#] confirms, [*] cancels.
*/ */
void handle_input_edit_stock_set(unsigned char key) void handle_input_edit_stock_set(unsigned char key)
{ {
@ -300,7 +302,6 @@ void handle_input_edit_stock_set(unsigned char key)
transition(STATE_EDIT_STOCK_SET); transition(STATE_EDIT_STOCK_SET);
} }
else if (key == '#') { else if (key == '#') {
/* apply new stock */
switch (selected_bev) { switch (selected_bev) {
case BEV_A: stock_a = new_stock_count; break; case BEV_A: stock_a = new_stock_count; break;
case BEV_B: stock_b = new_stock_count; break; case BEV_B: stock_b = new_stock_count; break;
@ -318,11 +319,12 @@ void handle_input_edit_stock_set(unsigned char key)
} }
} }
/** /**
* @brief Update the order count for selected beverage. * @brief Append a digit to the order count for selected beverage.
* @param digit 09 to append. * @param digit Value 09.
* *
* Ensures you never exceed the stock level. * Prevents exceeding remaining stock.
*/ */
void update_amount(uint8_t digit) void update_amount(uint8_t digit)
{ {
@ -351,8 +353,9 @@ void update_amount(uint8_t digit)
} }
} }
/** /**
* @brief Reset the order count for the currently selected beverage. * @brief Reset the current beverages order count to zero.
*/ */
void reset_amount(void) void reset_amount(void)
{ {
@ -364,6 +367,7 @@ void reset_amount(void)
} }
} }
/** /**
* @brief Reset all order counts to zero. * @brief Reset all order counts to zero.
*/ */
@ -372,41 +376,44 @@ void reset_amounts(void)
count_a = count_b = count_c = count_d = 0; count_a = count_b = count_c = count_d = 0;
} }
/** /**
* @brief Build up new stock value from digit. * @brief Append a digit to the newstock entry.
* @param digit 09 to append. * @param digit Value 09.
* *
* Caps at MAX_STOCK. * Caps at MAX_STOCK.
*/ */
void update_new_stock(uint8_t digit) void update_new_stock(uint8_t digit)
{ {
uint16_t tentative = new_stock_count * 10 + digit; uint16_t tentative = new_stock_count * 10 + digit;
if (tentative <= MAX_STOCK) { if (tentative <= MAX_STOCK)
new_stock_count = (uint8_t)tentative; new_stock_count = (uint8_t)tentative;
} else { else
buzz_invalid_input(); buzz_invalid_input();
}
} }
/** /**
* @brief Reset the new stock entry buffer to zero. * @brief Reset the newstock entry buffer to zero.
*/ */
void reset_new_stock(void) void reset_new_stock(void)
{ {
new_stock_count = 0; new_stock_count = 0;
} }
/** /**
* @brief True if any order count > 0. * @brief Check if any order count is nonzero.
* @return bool * @return true if at least one beverage has a count.
*/ */
bool any_selected(void) bool any_selected(void)
{ {
return (count_a + count_b + count_c + count_d) > 0; return (count_a + count_b + count_c + count_d) > 0;
} }
/** /**
* @brief Perform the visual update when state changes. * @brief Redraw the UI if the state has changed.
*/ */
void handle_general_interrupt(void) void handle_general_interrupt(void)
{ {
@ -428,9 +435,10 @@ void handle_general_interrupt(void)
state_chg = false; state_chg = false;
} }
/** /**
* @brief Transition to a new state (requests a redraw). * @brief Transition to a new state and request redrawing.
* @param next Next state. * @param next The next state to enter.
*/ */
void transition(State_t next) void transition(State_t next)
{ {
@ -438,10 +446,11 @@ void transition(State_t next)
state_chg = true; state_chg = true;
} }
/* ——— Door Callbacks —————————————————————————————————————— */
/** /**
* @brief Called when door opens. * @brief Called when the door is opened.
*
* If not in CONFIRMED state, shows UNAUTHORIZED; otherwise OPEN.
*/ */
static void door_opened_handler(void) static void door_opened_handler(void)
{ {
@ -453,8 +462,11 @@ static void door_opened_handler(void)
door_open = true; door_open = true;
} }
/** /**
* @brief Called when door closes. * @brief Called when the door is closed.
*
* Resets orders and returns to IDLE.
*/ */
static void door_closed_handler(void) static void door_closed_handler(void)
{ {
@ -463,105 +475,130 @@ static void door_closed_handler(void)
door_open = false; door_open = false;
} }
/* ——— UI Drawing Functions —————————————————————————————— */
/** /**
* @brief Draw the IDLE screen. * @brief Draw the IDLE screen.
*
* Line 1: Ready! centered.
* Line 2: [A-D]/*” menu hint.
*/ */
void ui_draw_idle(void) void ui_draw_idle(void)
{ {
lcd_clear(); lcd_clear();
lcd_set_cursor(0,5); lcd_print("Ready!"); lcd_set_cursor(0,5); lcd_print("Ready!");
lcd_set_cursor(1,0); lcd_print("A-D to order * to edit"); lcd_set_cursor(1,4); lcd_print("[A-D]/*");
} }
/** /**
* @brief Draw the quantityselection screen. * @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) void ui_draw_sel_count(void)
{ {
snprintf(buffer_ab, 17, "A:%c%3u B:%c%3u", snprintf(buffer_ab, 17, "A:%c%3u B:%c%3u",
(selected_bev==BEV_A?'*':' '), count_a, (selected_bev==BEV_A?'*':' '), count_a,
(selected_bev==BEV_B?'*':' '), count_b); (selected_bev==BEV_B?'*':' '), count_b);
snprintf(buffer_cd, 17, "C:%c%3u D:%c%3u", snprintf(buffer_cd, 17, "C:%c%3u D:%c%3u",
(selected_bev==BEV_C?'*':' '), count_c, (selected_bev==BEV_C?'*':' '), count_c,
(selected_bev==BEV_D?'*':' '), count_d); (selected_bev==BEV_D?'*':' '), count_d);
lcd_clear(); lcd_clear();
lcd_set_cursor(0,0); lcd_print(buffer_ab); lcd_set_cursor(0,0); lcd_print(buffer_ab);
lcd_set_cursor(1,0); lcd_print(buffer_cd); lcd_set_cursor(1,0); lcd_print(buffer_cd);
} }
/** /**
* @brief Draw the order confirmed screen. * @brief Draw the order confirmed screen.
*
* Line 1: Confirmed! centered.
* Line 2: Press [A-D]/*” for next action.
*/ */
void ui_draw_confirmed(void) void ui_draw_confirmed(void)
{ {
lcd_clear(); lcd_clear();
lcd_set_cursor(0,3); lcd_print("Confirmed!"); lcd_set_cursor(0,3); lcd_print("Confirmed!");
lcd_set_cursor(1,1); lcd_print("Press A-D or *"); lcd_set_cursor(1,2); lcd_print("OPEN/[A-D]/*");
} }
/** /**
* @brief Draw the unauthorizedopen error. * @brief Draw the unauthorizedopen error.
*/ */
void ui_draw_error(void) void ui_draw_error(void)
{ {
lcd_clear(); lcd_clear();
lcd_set_cursor(0,2); lcd_print("UNAUTHORIZED"); lcd_set_cursor(0,2); lcd_print("UNAUTHORIZED");
lcd_set_cursor(1,1); lcd_print("DOOR OPENED!"); lcd_set_cursor(1,3); lcd_print("DOOR OPENED");
} }
/** /**
* @brief Draw the door is open screen. * @brief Draw the door is open screen.
*/ */
void ui_draw_open(void) void ui_draw_open(void)
{ {
lcd_clear(); lcd_clear();
lcd_set_cursor(0,2); lcd_print("DOOR OPEN"); lcd_set_cursor(0,4); lcd_print("DOOR OPEN");
lcd_set_cursor(1,0); lcd_print("Close then *"); lcd_set_cursor(1,2); lcd_print("[*] to reset");
} }
/** /**
* @brief Draw when no beverage was selected at #. * @brief Draw when no beverage was selected at #.
*/ */
void ui_draw_none_selected(void) void ui_draw_none_selected(void)
{ {
lcd_clear(); lcd_clear();
lcd_set_cursor(0,4); lcd_print("SELECT!"); lcd_set_cursor(0,4); lcd_print("SELECT!");
lcd_set_cursor(1,1); lcd_print("Choose A-D"); lcd_set_cursor(1,1); lcd_print("Choose [A-D]");
sleep(1000); sleep(1000);
transition(STATE_SEL_COUNT); transition(STATE_SEL_COUNT);
ui_draw_sel_count(); ui_draw_sel_count();
} }
/** /**
* @brief Transient amounts reset screen. * @brief Transient all reset screen.
*/ */
void ui_draw_reset(void) void ui_draw_reset(void)
{ {
lcd_clear(); lcd_clear();
lcd_set_cursor(0,2); lcd_print("All Reset"); lcd_set_cursor(0,3); lcd_print("All Reset");
lcd_set_cursor(1,1); lcd_print("Ready"); lcd_set_cursor(1,5); lcd_print("Ready");
sleep(1000); sleep(1000);
transition(STATE_IDLE); if (door_open) {
ui_draw_idle(); transition(STATE_OPEN);
ui_draw_open();
} else {
transition(STATE_IDLE);
ui_draw_idle();
}
} }
/** /**
* @brief Draw the stockselection screen. * @brief Draw the stockselection screen.
*
* [A-D] picks beverage, [*] cancels.
*/ */
void ui_draw_stock_select(void) void ui_draw_stock_select(void)
{ {
lcd_clear(); lcd_clear();
lcd_set_cursor(0,3); lcd_print("Edit Stock"); lcd_set_cursor(0,3); lcd_print("Edit Stock");
lcd_set_cursor(1,1); lcd_print("A-D:*cancel"); lcd_set_cursor(1,4); lcd_print("[A-D]/*");
} }
/** /**
* @brief Draw the enter new stock screen. * @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) void ui_draw_stock_set(void)
{ {
@ -574,28 +611,28 @@ void ui_draw_stock_set(void)
} }
lcd_clear(); lcd_clear();
snprintf(buffer_ab,17,"%c stock:%3u", 'A'+selected_bev, cur); snprintf(buffer_ab,17,"%c stock:%3u", 'A'+selected_bev, cur);
lcd_set_cursor(0,0); lcd_print(buffer_ab); lcd_set_cursor(0,0); lcd_print(buffer_ab);
snprintf(buffer_cd,17,"New:%3u *cancel", new_stock_count); snprintf(buffer_cd,17,"New:%3u /*", new_stock_count);
lcd_set_cursor(1,0); lcd_print(buffer_cd); lcd_set_cursor(1,0); lcd_print(buffer_cd);
} }
/** /**
* @brief Draw the stock updated confirmation. * @brief Draw the stock updated confirmation.
*/ */
void ui_draw_stock_confirmed(void) void ui_draw_stock_confirmed(void)
{ {
lcd_clear(); lcd_clear();
lcd_set_cursor(0,1); lcd_print("Stock Updated!"); lcd_set_cursor(0,2); lcd_print("Stock Updated");
lcd_set_cursor(1,1); lcd_print("Press any key"); lcd_set_cursor(1,1); lcd_print("Press any key");
} }
/* ——— Feedback ——————————————————————————————————————————— */
/** /**
* @brief Short buzzer tone for invalid input. * @brief Short buzzer tone or LED flash for invalid input.
*/ */
void buzz_invalid_input(void) void buzz_invalid_input(void)
{ {
/* stub: beep or flash LED */ /* stub: implement buzzer or LED indication */
} }