feat: Crude State Machine

This commit is contained in:
Frederik Beimgraben 2025-07-03 04:03:48 +02:00
parent 34a965f03a
commit aac5c5a44c
16 changed files with 554 additions and 135 deletions

View File

@ -27,7 +27,7 @@
</option>
<option id="com.ti.ccstudio.buildDefinitions.core.OPT_CODEGEN_VERSION.1502413104" superClass="com.ti.ccstudio.buildDefinitions.core.OPT_CODEGEN_VERSION" value="21.6.1.LTS" valueType="string"/>
<targetPlatform id="com.ti.ccstudio.buildDefinitions.MSP430_21.6.exe.targetPlatformDebug.1741833122" name="Platform" superClass="com.ti.ccstudio.buildDefinitions.MSP430_21.6.exe.targetPlatformDebug"/>
<builder buildPath="${BuildDirectory}" id="com.ti.ccstudio.buildDefinitions.MSP430_21.6.exe.builderDebug.235413854" name="GNU Make.Debug" parallelBuildOn="true" parallelizationNumber="optimal" superClass="com.ti.ccstudio.buildDefinitions.MSP430_21.6.exe.builderDebug"/>
<builder buildPath="${BuildDirectory}" id="com.ti.ccstudio.buildDefinitions.MSP430_21.6.exe.builderDebug.235413854" keepEnvironmentInBuildfile="false" name="GNU Make" parallelBuildOn="true" parallelizationNumber="optimal" superClass="com.ti.ccstudio.buildDefinitions.MSP430_21.6.exe.builderDebug"/>
<tool id="com.ti.ccstudio.buildDefinitions.MSP430_21.6.exe.compilerDebug.281898955" name="MSP430 Compiler" superClass="com.ti.ccstudio.buildDefinitions.MSP430_21.6.exe.compilerDebug">
<option id="com.ti.ccstudio.buildDefinitions.MSP430_21.6.compilerID.DEFINE.925722857" superClass="com.ti.ccstudio.buildDefinitions.MSP430_21.6.compilerID.DEFINE" valueType="definedSymbols">
<listOptionValue value="__MSP430FR2355__"/>
@ -40,7 +40,7 @@
<option id="com.ti.ccstudio.buildDefinitions.MSP430_21.6.compilerID.SILICON_ERRATA.CPU22.253852873" superClass="com.ti.ccstudio.buildDefinitions.MSP430_21.6.compilerID.SILICON_ERRATA.CPU22" value="true" valueType="boolean"/>
<option id="com.ti.ccstudio.buildDefinitions.MSP430_21.6.compilerID.SILICON_ERRATA.CPU40.916653631" superClass="com.ti.ccstudio.buildDefinitions.MSP430_21.6.compilerID.SILICON_ERRATA.CPU40" value="true" valueType="boolean"/>
<option id="com.ti.ccstudio.buildDefinitions.MSP430_21.6.compilerID.SILICON_VERSION.973894696" superClass="com.ti.ccstudio.buildDefinitions.MSP430_21.6.compilerID.SILICON_VERSION" value="com.ti.ccstudio.buildDefinitions.MSP430_21.6.compilerID.SILICON_VERSION.mspx" valueType="enumerated"/>
<option id="com.ti.ccstudio.buildDefinitions.MSP430_21.6.compilerID.PRINTF_SUPPORT.1310228180" superClass="com.ti.ccstudio.buildDefinitions.MSP430_21.6.compilerID.PRINTF_SUPPORT" value="com.ti.ccstudio.buildDefinitions.MSP430_21.6.compilerID.PRINTF_SUPPORT.minimal" valueType="enumerated"/>
<option id="com.ti.ccstudio.buildDefinitions.MSP430_21.6.compilerID.PRINTF_SUPPORT.1310228180" superClass="com.ti.ccstudio.buildDefinitions.MSP430_21.6.compilerID.PRINTF_SUPPORT" value="com.ti.ccstudio.buildDefinitions.MSP430_21.6.compilerID.PRINTF_SUPPORT.full" valueType="enumerated"/>
<option id="com.ti.ccstudio.buildDefinitions.MSP430_21.6.compilerID.DEBUGGING_MODEL.1031813406" superClass="com.ti.ccstudio.buildDefinitions.MSP430_21.6.compilerID.DEBUGGING_MODEL" value="com.ti.ccstudio.buildDefinitions.MSP430_21.6.compilerID.DEBUGGING_MODEL.SYMDEBUG__DWARF" valueType="enumerated"/>
<option id="com.ti.ccstudio.buildDefinitions.MSP430_21.6.compilerID.DISPLAY_ERROR_NUMBER.482096263" superClass="com.ti.ccstudio.buildDefinitions.MSP430_21.6.compilerID.DISPLAY_ERROR_NUMBER" value="true" valueType="boolean"/>
<option id="com.ti.ccstudio.buildDefinitions.MSP430_21.6.compilerID.DIAG_WARNING.1891170450" superClass="com.ti.ccstudio.buildDefinitions.MSP430_21.6.compilerID.DIAG_WARNING" valueType="stringList">

View File

@ -148,5 +148,15 @@
"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/src/state_machine.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/src/timer.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/src/door_sensor.c"
}
]

111
main.c
View File

@ -18,72 +18,13 @@
#include "src/i2c.h"
#include "src/lcd.h"
#include "src/Board.h"
#include "src/timer.h"
#include "src/state_machine.h"
/** On-board LED port and pin (used by Morse module) */
#define LED_PORT GPIO_PORT_P1 /**< On-board LED port */
#define LED_PIN GPIO_PIN0 /**< On-board LED pin */
volatile char last_key_pressed = '\0';
volatile bool key_changed = false;
/**
* @brief Keypad callback invoked on key press interrupt.
* @param key ASCII character of pressed key
*
* Stores key, sets ready flag, and blinks Morse code on LED.
*/
static void myKeyHandler(char key)
{
last_key_pressed = key;
key_changed = true;
}
void init_timer(void) {
static Timer_B_initUpModeParam param = {0};
param.clockSource = TIMER_B_CLOCKSOURCE_SMCLK;
param.clockSourceDivider = TIMER_B_CLOCKSOURCE_DIVIDER_1;
param.timerPeriod = 999; // wenn 1000 Taktimpulse gezählt
// wurden, erfolgt ein Interrupt
// Periodendauer somit 1ms
param.timerInterruptEnable_TBIE =
TIMER_B_TBIE_INTERRUPT_DISABLE; // no interrupt on 0x0000
param.captureCompareInterruptEnable_CCR0_CCIE =
TIMER_B_CAPTURECOMPARE_INTERRUPT_ENABLE; // interrupt on TRmax
param.timerClear = TIMER_B_DO_CLEAR;
param.startTimer = true;
// start Timer:
Timer_B_initUpMode(TB0_BASE, &param);
}
void init_i2c(void) {
EUSCI_B_I2C_initMasterParam param = {0};
// Configure Pins for I2C
/*
* Select Port 1
* Set Pin 2, 3 to input with function, (UCB0SIMO/UCB0SDA, UCB0SOMI/UCB0SCL).
*/
GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_UCB0SCL, GPIO_PIN_UCB0SCL, GPIO_FUNCTION_UCB0SCL);
GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_UCB0SDA, GPIO_PIN_UCB0SDA, GPIO_FUNCTION_UCB0SDA);
param.selectClockSource = EUSCI_B_I2C_CLOCKSOURCE_SMCLK;
param.i2cClk = CS_getSMCLK();
param.dataRate = EUSCI_B_I2C_SET_DATA_RATE_100KBPS;
param.byteCounterThreshold = 1;
param.autoSTOPGeneration = EUSCI_B_I2C_NO_AUTO_STOP;
EUSCI_B_I2C_initMaster(EUSCI_B0_BASE, &param);
//Specify slave address
EUSCI_B_I2C_setSlaveAddress(EUSCI_B0_BASE, 0x27);
//Set in transmit mode
EUSCI_B_I2C_setMode(EUSCI_B0_BASE, EUSCI_B_I2C_TRANSMIT_MODE);
//Enable I2C Module to start operations
EUSCI_B_I2C_enable(EUSCI_B0_BASE);
}
/**
* @brief Main application entry point.
*
@ -102,48 +43,10 @@ int main(void)
GPIO_setAsOutputPin(LED_PORT, LED_PIN);
GPIO_setOutputLowOnPin(LED_PORT, LED_PIN);
/* Initialize keypad interrupt driver */
initKeypadInterrupts(myKeyHandler);
init_timer();
i2c_init();
lcd_init();
I2C_init(); /* I²C-Master initialisieren */
lcd_init(); /* LCD initialisieren */
lcd_set_cursor(0, 0);
lcd_print("Press any Key!");
/* Enter low-power mode and wait for key interrupts */
for (;;) {
__bis_SR_register(LPM0_bits | GIE); /* Enter LPM0 with interrupts enabled */
__no_operation();
if (key_changed) {
lcd_set_cursor(1, 0);
char output[20] = "Key pressed: ";
output[13] = last_key_pressed;
lcd_print(output);
key_changed = false;
}
}
}
/**
* @brief Führt eine blockierende Wartezeit aus.
* @param ms Zeit in Millisekunden
*/
void sleep(uint16_t ms) {
while (ms--) {
__bis_SR_register(LPM0_bits + GIE);
__no_operation();
}
}
// TimerB0 Interrupt Vector (TBxIV) handler
#pragma vector=TIMER0_B0_VECTOR
__interrupt void TIMER0_B0_ISR(void)
{
__bic_SR_register_on_exit(LPM0_bits);
}
sm_init();
sm_loop();
}

55
src/door_sensor.c Normal file
View File

@ -0,0 +1,55 @@
#include <stdint.h>
#include <msp430.h>
#include <driverlib.h>
#include <stdbool.h>
#include "door_sensor.h"
#include "constants.h"
#define SENSOR_PORT GPIO_PORT_P2
#define SENSOR_PIN GPIO_PIN0
#define SENSOR_VECTOR PORT2_VECTOR
/** User callback invoked on confirmed key press */
static DoorOpenedCallback_t doorOpenedCallback = NULL;
static DoorClosedCallback_t doorClosedCallback = NULL;
void door_init(DoorOpenedCallback_t ocb, DoorClosedCallback_t ccb) {
doorOpenedCallback = ocb;
doorClosedCallback = ccb;
/* Configure port as pull up with interrupt */
GPIO_setAsInputPinWithPullUpResistor(GPIO_PORT_P2, GPIO_PIN0);
GPIO_clearInterrupt(GPIO_PORT_P2, GPIO_PIN0);
GPIO_selectInterruptEdge(GPIO_PORT_P2, GPIO_PIN0, GPIO_LOW_TO_HIGH_TRANSITION);
GPIO_enableInterrupt(GPIO_PORT_P2, GPIO_PIN0);
/* Enable global interrupts */
__enable_interrupt();
}
volatile bool door_last_open = false;
/**
* @brief Interrupt Service Routine for PORT2
*/
#pragma vector=SENSOR_VECTOR
__interrupt void SENSOR_ISR(void)
{
uint16_t status = GPIO_getInterruptStatus(SENSOR_PORT, SENSOR_PIN);
if (status) {
if (door_last_open) {
if (doorClosedCallback)
doorClosedCallback();
GPIO_selectInterruptEdge(GPIO_PORT_P2, GPIO_PIN0, GPIO_LOW_TO_HIGH_TRANSITION);
} else {
if (doorOpenedCallback)
doorOpenedCallback();
GPIO_selectInterruptEdge(GPIO_PORT_P2, GPIO_PIN0, GPIO_HIGH_TO_LOW_TRANSITION);
}
}
GPIO_clearInterrupt(SENSOR_PORT, SENSOR_PIN);
door_last_open = !door_last_open;
}

13
src/door_sensor.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef DOOR_H
#define DOOR_H
/**
* @brief Callback invoked on a confirmed key press.
* @param key ASCII character of the pressed key.
*/
typedef void (*DoorOpenedCallback_t)(void);
typedef void (*DoorClosedCallback_t)(void);
void door_init(DoorOpenedCallback_t ocb, DoorClosedCallback_t ccb);
#endif // DOOR_H

View File

@ -28,7 +28,7 @@ static char dataIn;
static volatile bool i2cDone = false;
void I2C_init(void)
void i2c_init(void)
{
// USCI in Reset setzen um Konfiguration zu ermöglichen
UCB0CTLW0 |= UCSWRST;
@ -56,7 +56,7 @@ void I2C_init(void)
__enable_interrupt();
}
void I2C_write(uint8_t slaveAddress, char data[], uint8_t length)
void i2c_write(uint8_t slaveAddress, char data[], uint8_t length)
{
UCB0I2CSA = slaveAddress;
packet = data;
@ -77,11 +77,11 @@ void I2C_write(uint8_t slaveAddress, char data[], uint8_t length)
}
}
char I2C_read_reg(uint8_t slaveAddress, uint8_t registerAddress)
char i2c_read_reg(uint8_t slaveAddress, uint8_t registerAddress)
{
// Registeradresse zuerst senden
char addressBuffer[1] = {registerAddress};
I2C_write(slaveAddress, addressBuffer, 1);
i2c_write(slaveAddress, addressBuffer, 1);
// In Empfangsmodus wechseln und 1 Byte anfordern
UCB0CTLW0 &= ~UCTR;

View File

@ -21,7 +21,7 @@
/**
* @brief Initialize the I2C module as master (SMCLK/20 50 kHz SCL).
*/
void I2C_init(void);
void i2c_init(void);
/**
* @brief Send a data packet to an I2C slave.
@ -29,7 +29,7 @@ void I2C_init(void);
* @param data Pointer to the data buffer to send.
* @param length Number of bytes to send.
*/
void I2C_write(uint8_t slaveAddress, char data[], uint8_t length);
void i2c_write(uint8_t slaveAddress, char data[], uint8_t length);
/**
* @brief Read a single byte from a specific register of an I2C slave.
@ -37,6 +37,6 @@ void I2C_write(uint8_t slaveAddress, char data[], uint8_t length);
* @param registerAddress Register address to read.
* @return The byte read from the device.
*/
char I2C_read_reg(uint8_t slaveAddress, uint8_t registerAddress);
char i2c_read_reg(uint8_t slaveAddress, uint8_t registerAddress);
#endif /* I2C_H */

View File

@ -54,7 +54,7 @@ static KeypadCallback_t keyCallback = NULL;
* @param colIdx Index of column (03) that triggered interrupt
* @return ASCII character of pressed key, or 0 if none detected
*/
static char scanRowForColumn(uint8_t colIdx)
static char scan_row_for_column(uint8_t colIdx)
{
char result = 0;
unsigned int r;
@ -85,7 +85,7 @@ static char scanRowForColumn(uint8_t colIdx)
* wait for release, then re-arm interrupts.
* @param colIdx Index of column that triggered the interrupt
*/
static void handleColumnInterrupt(uint8_t colIdx)
static void handle_column_interrupt(uint8_t colIdx)
{
unsigned int i;
@ -99,7 +99,7 @@ static void handleColumnInterrupt(uint8_t colIdx)
/* Identify pressed key and invoke callback if set */
{
char key = scanRowForColumn(colIdx);
char key = scan_row_for_column(colIdx);
if (key && keyCallback) {
keyCallback(key);
}
@ -137,12 +137,12 @@ __interrupt void PORT1_ISR(void)
uint16_t status = GPIO_getInterruptStatus(GPIO_PORT_P1, mask);
if (status & colPin[COL1]) {
handleColumnInterrupt(COL1);
handle_column_interrupt(COL1);
__bic_SR_register_on_exit(LPM0_bits);
}
if (status & colPin[COL3]) {
handleColumnInterrupt(COL3);
handle_column_interrupt(COL3);
__bic_SR_register_on_exit(LPM0_bits);
}
@ -159,12 +159,12 @@ __interrupt void PORT3_ISR(void)
uint16_t status = GPIO_getInterruptStatus(GPIO_PORT_P3, mask);
if (status & colPin[COL0]) {
handleColumnInterrupt(COL0);
handle_column_interrupt(COL0);
__bic_SR_register_on_exit(LPM0_bits);
}
if (status & colPin[COL2]) {
handleColumnInterrupt(COL2);
handle_column_interrupt(COL2);
__bic_SR_register_on_exit(LPM0_bits);
}
@ -179,7 +179,7 @@ __interrupt void PORT3_ISR(void)
* @brief Initialize keypad GPIOs and interrupts.
* @param cb Callback invoked with ASCII key on press
*/
void initKeypadInterrupts(KeypadCallback_t cb)
void keypad_init(KeypadCallback_t cb)
{
unsigned int i;
keyCallback = cb;

View File

@ -27,6 +27,6 @@ typedef void (*KeypadCallback_t)(char key);
* @brief Initialize the keypad pins and interrupts.
* @param cb User callback for key events.
*/
void initKeypadInterrupts(KeypadCallback_t cb);
void keypad_init(KeypadCallback_t cb);
#endif /* KEYPAD_H */

View File

@ -11,13 +11,13 @@ static void lcd_write_nibble(uint8_t nibble, uint8_t control)
/* High-Nibble in die oberen 4 Bits */
buf[0] = (nibble << 4) | control | LCD_BACKLIGHT;
/* EN=1 */
I2C_write(LCD_I2C_ADDR, buf, 1);
i2c_write(LCD_I2C_ADDR, buf, 1);
buf[0] |= LCD_ENABLE;
I2C_write(LCD_I2C_ADDR, buf, 1);
i2c_write(LCD_I2C_ADDR, buf, 1);
__delay_cycles(500); /* ca. 50 µs @ 1 MHz */
/* EN=0 */
buf[0] &= ~LCD_ENABLE;
I2C_write(LCD_I2C_ADDR, buf, 1);
i2c_write(LCD_I2C_ADDR, buf, 1);
__delay_cycles(500);
}

View File

@ -32,7 +32,7 @@
/**
* @brief Blink the LED for a dot: on 1 unit, off 1 unit.
*/
static void blinkDot(void)
static void blink_dot(void)
{
GPIO_setOutputHighOnPin(LED_PORT, LED_PIN);
__delay_cycles(MORSE_UNIT_CYCLES);
@ -43,7 +43,7 @@ static void blinkDot(void)
/**
* @brief Blink the LED for a dash: on 3 units, off 1 unit.
*/
static void blinkDash(void)
static void blink_dash(void)
{
GPIO_setOutputHighOnPin(LED_PORT, LED_PIN);
__delay_cycles(3 * MORSE_UNIT_CYCLES);
@ -102,7 +102,7 @@ static const char *lookupMorse(char in)
* Unlocks FRAM I/O power-on default high-impedance lock,
* then configures LED pin as output low.
*/
void morseInit(void)
void morse_init(void)
{
PMM_unlockLPM5();
GPIO_setAsOutputPin(LED_PORT, LED_PIN);
@ -118,7 +118,7 @@ void morseInit(void)
*
* @param c Character to blink (AZ, az, 09)
*/
void blinkMorseChar(char c)
void blink_morse_char(char c)
{
const char *pattern = lookupMorse(c);
const char *p;
@ -130,9 +130,9 @@ void blinkMorseChar(char c)
/* Blink each element in pattern */
for (p = pattern; *p; p++) {
if (*p == '.') {
blinkDot();
blink_dot();
} else if (*p == '-') {
blinkDash();
blink_dash();
}
}

View File

@ -10,7 +10,7 @@
/**
* @brief Initialize LED output pin (P1.0) for Morse blinking.
*/
void morseInit(void);
void morse_init(void);
/**
* @brief Blink an alphanumeric character in Morse code.
@ -18,6 +18,6 @@ void morseInit(void);
* Supports AZ (case-insensitive) and 09. Others are ignored.
* @param c Character to blink
*/
void blinkMorseChar(char c);
void blink_morse_char(char c);
#endif // MORSE_H

View File

@ -12,4 +12,365 @@
* @date 2025-07-02
*/
#include <stdbool.h>
#include <msp430.h>
#include <driverlib.h>
#include <stdio.h>
#include <ctype.h>
#include "keypad.h"
#include "lcd.h"
#include "morse.h"
#include "door_sensor.h"
#include "state_machine.h"
#include "timer.h"
volatile char keypad_last_key = '\0';
volatile bool keypad_flag_chg = false;
volatile bool allow_open = false;
/* Function Declarations */
void handle_interrupt(void);
void buzz_invalid_input(void);
void sound_alarm(void);
void reset_alarm(void);
void transition(State_t state);
void ui_draw_idle(void);
void ui_draw_sel_count(void);
void ui_draw_confirmed(void);
void ui_draw_error(void);
void ui_draw_open(void);
void ui_draw_none_selected(void);
void ui_draw_reset(void);
void reset_amount();
void reset_amounts();
bool any_selected(void);
volatile State_t current_state = STATE_IDLE;
volatile bool state_chg = false;
volatile uint8_t count_a = 0;
volatile uint8_t count_b = 0;
volatile uint8_t count_c = 0;
volatile uint8_t count_d = 0;
volatile SelectedBev_t selected_bev = BEV_A;
volatile bool door_open = false;
void handle_input_idle(unsigned char key) {
switch (key) {
case 'A':
selected_bev = BEV_A;
transition(STATE_SEL_COUNT);
break;
case 'B':
selected_bev = BEV_B;
transition(STATE_SEL_COUNT);
break;
case 'C':
selected_bev = BEV_C;
transition(STATE_SEL_COUNT);
break;
case 'D':
selected_bev = BEV_D;
transition(STATE_SEL_COUNT);
break;
default:
buzz_invalid_input();
}
}
void handle_input_confirmed(unsigned char key) {
switch (key) {
case 'A':
selected_bev = BEV_A;
transition(STATE_SEL_COUNT);
break;
case 'B':
selected_bev = BEV_B;
transition(STATE_SEL_COUNT);
break;
case 'C':
selected_bev = BEV_C;
transition(STATE_SEL_COUNT);
break;
case 'D':
selected_bev = BEV_D;
transition(STATE_SEL_COUNT);
break;
case '*':
reset_amounts();
transition(STATE_RESET);
break;
default:
buzz_invalid_input();
}
}
void update_amount(uint8_t new_digit) {
switch (selected_bev) {
case BEV_A:
if (count_a * 10 + new_digit < 255)
count_a = count_a * 10 + new_digit;
break;
case BEV_B:
if (count_b * 10 + new_digit < 255)
count_b = count_b * 10 + new_digit;
break;
case BEV_C:
if (count_c * 10 + new_digit < 255)
count_c = count_c * 10 + new_digit;
break;
case BEV_D:
if (count_d * 10 + new_digit < 255)
count_d = count_d * 10 + new_digit;
break;
}
}
void reset_amount() {
switch (selected_bev) {
case BEV_A:
count_a = 0;
break;
case BEV_B:
count_b = 0;
break;
case BEV_C:
count_c = 0;
break;
case BEV_D:
count_d = 0;
break;
}
}
void reset_amounts() {
count_a = 0;
count_b = 0;
count_c = 0;
count_d = 0;
}
void handle_input_sel_count(unsigned char key) {
// Check if input is number
if (key >= '0' && key <= '9') {
update_amount((uint8_t) key - '0');
transition(current_state);
} else if (key == '#') {
if (door_open) {
transition(STATE_OPEN);
} else {
if (!any_selected()) {
transition(STATE_NONE_SELECTED);
} else {
transition(STATE_CONFIRMED);
}
}
} else if (key == '*') {
reset_amount();
transition(current_state);
} else {
handle_input_idle(key);
}
}
/**
* @brief Keypad callback invoked on key press interrupt.
* @param key ASCII character of pressed key
*
* Stores key, sets ready flag, and blinks Morse code on LED.
*/
static void keypad_handler(char key)
{
keypad_last_key = key;
keypad_flag_chg = true;
switch (current_state) {
case STATE_IDLE:
handle_input_idle(key);
break;
case STATE_SEL_COUNT:
handle_input_sel_count(key);
break;
case STATE_CONFIRMED:
handle_input_confirmed(key);
break;
case STATE_OPEN:
handle_input_confirmed(key);
break;
default:
buzz_invalid_input();
}
}
bool any_selected(void) {
return count_a + count_b + count_c + count_d > 0;
}
static void door_opened_handler(void) {
if (current_state != STATE_CONFIRMED && current_state != STATE_UNLOCKED) {
transition(STATE_UNAUTHORIZED);
} else {
transition(STATE_OPEN);
}
door_open = true;
}
static void door_closed_handler(void) {
reset_amounts();
transition(STATE_IDLE);
door_open = false;
}
void buzz_invalid_input(void) {
// FIXME: Implement this later
}
void sound_alarm(void) {
// FIXME: Implement this later
}
void reset_alarm(void) {
// FIXME: Implement this later
}
void transition(State_t state) {
state_chg = true;
current_state = state;
}
void sm_init(void) {
keypad_init(keypad_handler);
door_init(door_opened_handler, door_closed_handler);
ui_draw_idle();
}
void sm_loop(void) {
for (;;) {
__bis_SR_register(LPM0_bits | GIE); /* Enter LPM0 with interrupts enabled */
__no_operation();
handle_general_interrupt();
}
}
void handle_general_interrupt(void) {
if (state_chg) {
switch (current_state) {
case STATE_IDLE:
ui_draw_idle();
break;
case STATE_SEL_COUNT:
ui_draw_sel_count();
break;
case STATE_CONFIRMED:
ui_draw_confirmed();
break;
case STATE_UNAUTHORIZED:
ui_draw_error();
break;
case STATE_OPEN:
ui_draw_open();
break;
case STATE_NONE_SELECTED:
ui_draw_none_selected();
break;
case STATE_RESET:
ui_draw_reset();
break;
}
state_chg = false;
}
}
char buffer_ab[16];
char buffer_cd[16];
void ui_draw_idle(void) {
lcd_clear();
lcd_set_cursor(0, 5);
lcd_print("Ready!");
lcd_set_cursor(1, 1);
lcd_print("Press A/B/C/D");
}
void ui_draw_sel_count(void) {
snprintf(buffer_ab, 17, "A:%c%4d B:%c%4d ",
selected_bev == BEV_A ? '*' : ' ',
count_a,
selected_bev == BEV_B ? '*' : ' ',
count_b
);
snprintf(buffer_cd, 17, "C:%c%4d D:%c%4d ",
selected_bev == BEV_C ? '*' : ' ',
count_c,
selected_bev == BEV_D ? '*' : ' ',
count_d
);
lcd_set_cursor(0, 0);
lcd_print(buffer_ab);
lcd_set_cursor(1, 0);
lcd_print(buffer_cd);
}
void ui_draw_confirmed(void) {
lcd_clear();
lcd_set_cursor(0, 3);
lcd_print("Confirmed!");
lcd_set_cursor(1, 1);
lcd_print("OPEN/[A-D]/[*]");
}
void ui_draw_error(void) {
lcd_clear();
lcd_set_cursor(0, 2);
lcd_print("UNAUTHORIZED");
lcd_set_cursor(1, 1);
lcd_print("CLOSE DOOR NOW");
}
void ui_draw_open(void) {
lcd_clear();
lcd_set_cursor(0, 2);
lcd_print("DOOR IS OPEN");
lcd_set_cursor(1, 0);
lcd_print("CLOSE /[A-D]/[*]");
}
void ui_draw_none_selected(void) {
lcd_clear();
lcd_set_cursor(0, 5);
lcd_print("SELECT");
lcd_set_cursor(1, 1);
lcd_print("ANY BEVERAGES!");
sleep(1000);
transition(STATE_SEL_COUNT);
ui_draw_sel_count();
}
void ui_draw_reset(void) {
lcd_clear();
lcd_set_cursor(0, 2);
lcd_print("AMOUNTS HAVE");
lcd_set_cursor(1, 3);
lcd_print("BEEN RESET");
sleep(1000);
if (door_open) {
transition(STATE_OPEN);
ui_draw_open();
} else {
transition(STATE_IDLE);
ui_draw_idle();
}
}

View File

@ -12,4 +12,33 @@
* @date 2025-07-02
*/
static void keypad_handler(char key);
#ifndef STATE_MACHINE_H
#define STATE_MACHINE_H
static void keypad_handler(char key);
void sm_init(void);
void sm_loop(void);
typedef enum {
STATE_IDLE,
STATE_SEL_COUNT,
STATE_CONFIRMED,
STATE_OPEN,
STATE_SECRET_INPUT,
STATE_UNLOCKED,
STATE_UNAUTHORIZED,
STATE_NONE_SELECTED,
STATE_RESET
} State_t;
typedef enum {
BEV_A,
BEV_B,
BEV_C,
BEV_D
} SelectedBev_t;
#endif // STATE_MACHINE_H

41
src/timer.c Normal file
View File

@ -0,0 +1,41 @@
#include <stdint.h>
#include <msp430.h>
#include <driverlib.h>
#include "timer.h"
/**
* @brief Führt eine blockierende Wartezeit aus.
* @param ms Zeit in Millisekunden
*/
void sleep(uint16_t ms) {
while (ms--) {
__bis_SR_register(LPM0_bits + GIE);
__no_operation();
}
}
void init_timer(void) {
static Timer_B_initUpModeParam param = {0};
param.clockSource = TIMER_B_CLOCKSOURCE_SMCLK;
param.clockSourceDivider = TIMER_B_CLOCKSOURCE_DIVIDER_1;
param.timerPeriod = 999; // wenn 1000 Taktimpulse gezählt
// wurden, erfolgt ein Interrupt
// Periodendauer somit 1ms
param.timerInterruptEnable_TBIE =
TIMER_B_TBIE_INTERRUPT_DISABLE; // no interrupt on 0x0000
param.captureCompareInterruptEnable_CCR0_CCIE =
TIMER_B_CAPTURECOMPARE_INTERRUPT_ENABLE; // interrupt on TRmax
param.timerClear = TIMER_B_DO_CLEAR;
param.startTimer = true;
// start Timer:
Timer_B_initUpMode(TB0_BASE, &param);
}
// TimerB0 Interrupt Vector (TBxIV) handler
#pragma vector=TIMER0_B0_VECTOR
__interrupt void TIMER0_B0_ISR(void)
{
__bic_SR_register_on_exit(LPM0_bits);
}

7
src/timer.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef TIMER_H
#define TIMER_H
void sleep(uint16_t ms);
void init_timer(void);
#endif // TIMER_H