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",
"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",
"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.
*
* 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
* Frederik Beimgraben
@ -24,20 +24,19 @@
#include "state_machine.h"
#include "timer.h"
/* ——— Configurable Defaults ———————————————————————————————— */
#define INITIAL_STOCK_DEFAULT 20U /**< Default stock for each beverage */
#define MAX_STOCK 255U /**< Maximum stock representable */
/* ——— Internal State Variables —————————————————————————————— */
/** Default initial stock for each beverage. */
#define INITIAL_STOCK_DEFAULT 20U
/** Maximum stock representable (uint8_t limit). */
#define MAX_STOCK 255U
/** 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. */
volatile bool keypad_flag_chg = false;
volatile bool keypad_flag_chg = false;
/** 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. */
volatile bool state_chg = false;
volatile bool state_chg = false;
/** Current UI state. */
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. */
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_cd[17];
/* ——— Function Prototypes ———————————————————————————————— */
/* —— Function Prototypes —— */
/* Keypad input handlers */
static void keypad_handler(char key);
void handle_input_idle(unsigned char key);
void handle_input_sel_count(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_closed_handler(void);
/* Keypad Callback */
static void keypad_handler(char key);
/* General interrupt dispatcher */
void handle_general_interrupt(void);
/* ——— Implementation ————————————————————————————————————— */
/**
* @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)
{
/* register keypad + door callbacks */
keypad_init(/* callback = */ keypad_handler);
keypad_init(keypad_handler);
door_init(door_opened_handler, door_closed_handler);
/* start in IDLE with default stocks */
reset_amounts();
reset_new_stock();
stock_a = stock_b = stock_c = stock_d = INITIAL_STOCK_DEFAULT;
@ -128,23 +126,25 @@ void sm_init(void)
ui_draw_idle();
}
/**
* @brief Main event loop: lowpower + interrupts.
* @brief Main event loop: lowpower mode with interrupts.
*/
void sm_loop(void)
{
for (;;) {
__bis_SR_register(LPM0_bits | GIE); /* sleep until interrupt */
__bis_SR_register(LPM0_bits | GIE);
__no_operation();
handle_general_interrupt();
}
}
/**
* @brief Keypad interrupt handler.
* @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)
{
@ -169,7 +169,6 @@ static void keypad_handler(char key)
handle_input_edit_stock_set(key);
break;
case STATE_EDIT_STOCK_CONFIRMED:
/* any key closes confirmation */
transition(STATE_IDLE);
break;
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.
*
* AD start ordering that beverage.
* * enters stockediting menu.
* [A-D] to order, [*] to enter stockedit menu.
*/
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.
*
* Digits build count (up to stock), # confirms, * clears,
* anything else falls back to idlehandler.
* Digits build an order (up to stock), [#] confirms, [*] clears,
* others fall back to idle.
*/
void handle_input_sel_count(unsigned char key)
{
@ -221,7 +221,7 @@ void handle_input_sel_count(unsigned char key)
} else if (!any_selected()) {
transition(STATE_NONE_SELECTED);
} else {
/* deduct stock before confirming */
/* Deduct stock */
switch (selected_bev) {
case BEV_A: stock_a -= count_a; 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.
*
* AD reselect beverage for new order.
* * resets all order counts.
* [A-D] to start new order, [*] to reset all counts.
*/
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.
* @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)
{
@ -287,11 +288,12 @@ void handle_input_edit_stock_select(unsigned char key)
}
}
/**
* @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)
{
@ -300,7 +302,6 @@ void handle_input_edit_stock_set(unsigned char key)
transition(STATE_EDIT_STOCK_SET);
}
else if (key == '#') {
/* apply new stock */
switch (selected_bev) {
case BEV_A: stock_a = 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.
* @param digit 09 to append.
* @brief Append a digit to the order count for selected beverage.
* @param digit Value 09.
*
* Ensures you never exceed the stock level.
* Prevents exceeding remaining stock.
*/
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)
{
@ -364,6 +367,7 @@ void reset_amount(void)
}
}
/**
* @brief Reset all order counts to zero.
*/
@ -372,41 +376,44 @@ void reset_amounts(void)
count_a = count_b = count_c = count_d = 0;
}
/**
* @brief Build up new stock value from digit.
* @param digit 09 to append.
* @brief Append a digit to the newstock entry.
* @param digit Value 09.
*
* Caps at MAX_STOCK.
*/
void update_new_stock(uint8_t digit)
{
uint16_t tentative = new_stock_count * 10 + digit;
if (tentative <= MAX_STOCK) {
if (tentative <= MAX_STOCK)
new_stock_count = (uint8_t)tentative;
} else {
else
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)
{
new_stock_count = 0;
}
/**
* @brief True if any order count > 0.
* @return bool
* @brief Check if any order count is nonzero.
* @return true if at least one beverage has a count.
*/
bool any_selected(void)
{
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)
{
@ -428,9 +435,10 @@ void handle_general_interrupt(void)
state_chg = false;
}
/**
* @brief Transition to a new state (requests a redraw).
* @param next Next state.
* @brief Transition to a new state and request redrawing.
* @param next The next state to enter.
*/
void transition(State_t next)
{
@ -438,10 +446,11 @@ void transition(State_t next)
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)
{
@ -453,8 +462,11 @@ static void door_opened_handler(void)
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)
{
@ -463,105 +475,130 @@ static void door_closed_handler(void)
door_open = false;
}
/* ——— UI Drawing Functions —————————————————————————————— */
/**
* @brief Draw the IDLE screen.
*
* Line 1: Ready! centered.
* Line 2: [A-D]/*” menu hint.
*/
void ui_draw_idle(void)
{
lcd_clear();
lcd_set_cursor(0,5); lcd_print("Ready!");
lcd_set_cursor(1,0); lcd_print("A-D to order * to edit");
lcd_set_cursor(0,5); lcd_print("Ready!");
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)
{
snprintf(buffer_ab, 17, "A:%c%3u B:%c%3u",
(selected_bev==BEV_A?'*':' '), count_a,
(selected_bev==BEV_B?'*':' '), count_b);
(selected_bev==BEV_A?'*':' '), count_a,
(selected_bev==BEV_B?'*':' '), count_b);
snprintf(buffer_cd, 17, "C:%c%3u D:%c%3u",
(selected_bev==BEV_C?'*':' '), count_c,
(selected_bev==BEV_D?'*':' '), count_d);
(selected_bev==BEV_C?'*':' '), count_c,
(selected_bev==BEV_D?'*':' '), count_d);
lcd_clear();
lcd_set_cursor(0,0); lcd_print(buffer_ab);
lcd_set_cursor(1,0); lcd_print(buffer_cd);
lcd_set_cursor(0,0); lcd_print(buffer_ab);
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)
{
lcd_clear();
lcd_set_cursor(0,3); lcd_print("Confirmed!");
lcd_set_cursor(1,1); lcd_print("Press A-D or *");
lcd_set_cursor(0,3); lcd_print("Confirmed!");
lcd_set_cursor(1,2); lcd_print("OPEN/[A-D]/*");
}
/**
* @brief Draw the unauthorizedopen error.
*/
void ui_draw_error(void)
{
lcd_clear();
lcd_set_cursor(0,2); lcd_print("UNAUTHORIZED");
lcd_set_cursor(1,1); lcd_print("DOOR OPENED!");
lcd_set_cursor(0,2); lcd_print("UNAUTHORIZED");
lcd_set_cursor(1,3); lcd_print("DOOR OPENED");
}
/**
* @brief Draw the door is open screen.
*/
void ui_draw_open(void)
{
lcd_clear();
lcd_set_cursor(0,2); lcd_print("DOOR OPEN");
lcd_set_cursor(1,0); lcd_print("Close then *");
lcd_set_cursor(0,4); lcd_print("DOOR OPEN");
lcd_set_cursor(1,2); lcd_print("[*] to reset");
}
/**
* @brief Draw when no beverage was selected at #.
*/
void ui_draw_none_selected(void)
{
lcd_clear();
lcd_set_cursor(0,4); lcd_print("SELECT!");
lcd_set_cursor(1,1); lcd_print("Choose A-D");
lcd_set_cursor(0,4); lcd_print("SELECT!");
lcd_set_cursor(1,1); lcd_print("Choose [A-D]");
sleep(1000);
transition(STATE_SEL_COUNT);
ui_draw_sel_count();
}
/**
* @brief Transient amounts reset screen.
* @brief Transient all reset screen.
*/
void ui_draw_reset(void)
{
lcd_clear();
lcd_set_cursor(0,2); lcd_print("All Reset");
lcd_set_cursor(1,1); lcd_print("Ready");
lcd_set_cursor(0,3); lcd_print("All Reset");
lcd_set_cursor(1,5); lcd_print("Ready");
sleep(1000);
transition(STATE_IDLE);
ui_draw_idle();
if (door_open) {
transition(STATE_OPEN);
ui_draw_open();
} else {
transition(STATE_IDLE);
ui_draw_idle();
}
}
/**
* @brief Draw the stockselection screen.
*
* [A-D] picks beverage, [*] cancels.
*/
void ui_draw_stock_select(void)
{
lcd_clear();
lcd_set_cursor(0,3); lcd_print("Edit Stock");
lcd_set_cursor(1,1); lcd_print("A-D:*cancel");
lcd_set_cursor(0,3); lcd_print("Edit Stock");
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)
{
@ -574,28 +611,28 @@ void ui_draw_stock_set(void)
}
lcd_clear();
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);
lcd_set_cursor(1,0); lcd_print(buffer_cd);
snprintf(buffer_cd,17,"New:%3u /*", new_stock_count);
lcd_set_cursor(1,0); lcd_print(buffer_cd);
}
/**
* @brief Draw the stock updated confirmation.
*/
void ui_draw_stock_confirmed(void)
{
lcd_clear();
lcd_set_cursor(0,1); lcd_print("Stock Updated!");
lcd_set_cursor(1,1); lcd_print("Press any key");
lcd_set_cursor(0,2); lcd_print("Stock Updated");
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)
{
/* stub: beep or flash LED */
/* stub: implement buzzer or LED indication */
}