Main¶
Top-level Finite State Machine for controlling the MSP430 and cuplTag as a whole.
A Finite State Machine is defined in this file and run from main(). This has several features.
- Author
Malcolm Mackay
It makes calls to drivers for communicating with the HDC2022 humidity sensor and the NT3H2111 NFC EEPROM.
It controls entry into the ‘programming mode’ sub state machine, where configuration strings can be written using a serial port.
It reads configuration strings from the NFC EEPROM if any are present.
It collects samples from an HDC2022 at a fixed time interval and passes these to the cuplcodec encoder.
Defines
-
CS_SMCLK_DESIRED_FREQUENCY_IN_KHZ¶
Target frequency for SMCLK in kHz.
-
CS_XT1_CRYSTAL_FREQUENCY¶
Resonant frequency of the XT1 crystal in kHz.
-
CS_XT1_TIMEOUT¶
Timeout for XT1 to stabilise at the resonant frequency in SMCLK cycles.
-
CP10MS¶
ACLK Cycles Per 10 MilliSeconds. Assumes ACLK = 32768 kHz and a divide-by-8.
-
EXIT_STATE
State machine exit state.
-
ENTRY_STATE
State machine entry state.
Typedefs
-
typedef enum state_codes tstate¶
States in the Finite State Machine are represented by a code. This is used each time the state machine is run, to determine:
Which state function to call in main().
The next state in lookup_transitions().
-
typedef enum ret_codes tretcode¶
Each state function returns at least one code from the list below. This is used by lookup_transitions() to determine the next state.
-
typedef enum event_codes tevent¶
Events occur asynchronously to execution of the FSM. The MSP430 will typically wait for an event in sleep mode to save power. An example is an edge on the INT (interrupt) output from a temperature sensor. This is connected to an input on the MSP430, which is configured to call an Interrupt Service Routine (ISR). The ISR sets a flag and ‘wakes up’ the MSP430 from sleep mode.
The main() function is entered, flags are checked then cleared and the event variable is set according to the list of codes below. Finally, the state function is called with the event passed as an argument.
Multiple events can occur simultaneously, but only one at-a-time is passed to the FSM.
Enums
-
enum state_codes¶
States in the Finite State Machine are represented by a code. This is used each time the state machine is run, to determine:
Which state function to call in main().
The next state in lookup_transitions().
Values:
-
enumerator sc_init¶
State code for init_state()
-
enumerator sc_init_reqmemon¶
State code for init_reqmemon()
-
enumerator sc_init_waitmemon¶
State code for init_waitmemon()
-
enumerator sc_init_ntag¶
State code for init_ntag()
-
enumerator sc_init_progmode¶
State code for init_progmode()
-
enumerator sc_init_configcheck¶
State code for init_configcheck()
-
enumerator sc_init_errorcheck¶
State code for init_errorcheck()
-
enumerator sc_init_wakeupcheck¶
State code for init_wakeupcheck()
-
enumerator sc_init_batvwait¶
State code for init_batvwait()
-
enumerator sc_init_rtc_slow¶
State code for init_rtc_slow()
-
enumerator sc_init_rtc_1min¶
State code for init_rtc_1min()
-
enumerator sc_smpl_checkcounter¶
State code for smpl_checkcounter()
-
enumerator sc_smpl_hdcreq¶
State code for smpl_hdcreq()
-
enumerator sc_smpl_hdcwait¶
State code for smpl_hdcwait()
-
enumerator sc_smpl_hdcread¶
State code for smpl_hdcread()
-
enumerator sc_smpl_wait¶
State code for smpl_wait()
-
enumerator sc_end¶
State code for end_state()
-
enum ret_codes¶
Each state function returns at least one code from the list below. This is used by lookup_transitions() to determine the next state.
Values:
-
enumerator tr_ok¶
-
enumerator tr_prog¶
-
enumerator tr_newconfig¶
-
enumerator tr_hdcreq¶
Request a sample from the HDC2021 sensor.
-
enumerator tr_updatemin¶
-
enumerator tr_deepsleep¶
cuplTag should enter a deep sleep state LPM3.5
-
enumerator tr_lowbat¶
-
enumerator tr_fail¶
-
enumerator tr_samplingloop¶
cuplTag is in the sampling loop. The reset was caused by an exit from LPM3.5
-
enumerator tr_por¶
cuplTag is NOT in the sampling loop. A Power-On-Reset has occurred.
-
enumerator tr_wait¶
cuplTag should enter a sleep state, such as LPM0, to wait for an event.
-
enumerator tr_ok¶
-
enum event_codes¶
Events occur asynchronously to execution of the FSM. The MSP430 will typically wait for an event in sleep mode to save power. An example is an edge on the INT (interrupt) output from a temperature sensor. This is connected to an input on the MSP430, which is configured to call an Interrupt Service Routine (ISR). The ISR sets a flag and ‘wakes up’ the MSP430 from sleep mode.
The main() function is entered, flags are checked then cleared and the event variable is set according to the list of codes below. Finally, the state function is called with the event passed as an argument.
Multiple events can occur simultaneously, but only one at-a-time is passed to the FSM.
Values:
-
enumerator evt_none¶
No event has occurred.
-
enumerator evt_timerfinished¶
The timer peripheral has counted down to 0.
-
enumerator evt_hdcint¶
Pin change interrupt received from the HDC2021 temperature and humidity sensor.
-
enumerator evt_none¶
Functions
-
void fram_write_enable()¶
Enable writes to program FRAM.
Some variables are stored in program FRAM. RAM cannot be used because state is lost in deep sleep mode (LPM3.5). The Program FRAM Write Protect bit must be cleared (and interrupts disabled) before a write.
-
void fram_write_disable()¶
Disable writes to program FRAM.
Sets the Program FRAM Write Protect bit and re-enables interrupts.
-
tretcode init_state(tevent evt)¶
Initialise clocks and IOs on the MSP430.
All IOs are configured into an initial state. The number of IOs left as inputs (default) must be minimised to reduce power consumption.
The slow Auxiliary Clock (ACLK) is sourced form the external 32.768 kHz crystal. An internal 10 kHz source is used by default. This is power hungry and drifts with temperature.
Next, the Phased-Locked Loop (DCO) generates an output frequency of 1 MHz, by multiplying the external 32.768 kHz crystal frequency up by 31.
Internal clocks MCLK and SMCLK are connected to the DCO output.
1 MHz was selected to minimise current draw from the high impedance coin cell battery. This results in a lower voltage drop after exiting the sleep state. Battery life is limited by this voltage drop. This is not the case if the source impedance is lower. Then it is best to operate at a higher frequency: up to 24 MHz.
Finally, the cause of the reset is read. The program needs to know whether this is just a routine wake-up from sleep (LPM3.5) or the result of a fault.
-
tretcode init_reqmemon(tevent evt)¶
This state calls reqmemon().
-
tretcode init_waitmemon(tevent evt)¶
This state calls waitmemon().
-
tretcode init_ntag(tevent evt)¶
Initialise the dual-interface I2C+NFC EEPROM.
A call is made to ‘nt3h_check_address()’ to make sure the EEPROM is at device address 0x55.
The first EEPROM block is read to check for an NFC text record. If found, configuration strings are extracted and saved into non-volatile memory.
The capability container is written if it needs to be. These 4 bytes indicate that the tag contains an NDEF message.
The programming mode select pin (nPRG) is checked. If it is LOW, the return code is updated.
-
tretcode init_progmode(tevent evt)¶
Run the programming mode sub-state machine.
Enables the serial port (UART) and responds to text commands.
It is only intended that this state be entered in a production environment, not by the end user.
The only way to exit is to reset the microcontroller. This can be done by sending a soft reset command ‘<z>’.
-
tretcode init_configcheck(tevent evt)¶
Verifies that all configuration strings have been written.
The state machine cannot continue unless the tag is fully configured. There are no default settings.
When configuration is incomplete, a text-based error message is written to the tag and deep-sleep mode is entered.
-
tretcode init_errorcheck(tevent evt)¶
Check for an error condition before continuing startup.
An error occurs if the reset was caused by a reason other than a new battery insertion.
When the battery voltage is below a threshold (set in NVM). The state machine should not get stuck in a loop, where an attempt is made to write to the NFC EEPROM before the micro-controller resets. Reset loops can wear out the EEPROM.
In the event of 10 consecutive resets that result in an error, or a single low battery reading:
Report the most recent error in the cupl URL status word.
Do not include any samples in the cupl URL.
Go to a deep sleep state that prevents another reset cycle from occurring for some time.
Otherwise:
Report the most recent error in the cupl URL but treat it as spurious.
Allow the state machine to continue.
- Returns
tr_deepsleep when 10 consecutive errors have occurred or the battery voltage is low. Otherwise indicate no errors with tr_ok.
-
tretcode init_wakeupcheck(tevent evt)¶
Has the reset has been caused by a routine RTC wake-up?
In the sampling loop, Wake-ups from LPM3.5 occur every minute. These are invoked by an interrupt from the Real Time Clock peripheral.
This function first makes a call to stat_rstcause_is_lpm5wu(). Then it checks if the first integer in Backup Memory is 1. If it is, then the cuplTag is in the sampling loop.
- Returns
tr_samplingloop if the cuplTag is in the sampling loop. Otherwise, indicate a power-on-reset with tr_por.
-
tretcode init_batvwait(tevent evt)¶
Wait for the battery voltage to stabilise.
It takes some time for capacitors to charge after a battery is inserted. A timer is started in the previous state. The MSP430 waits here in low power mode.
- Parameters
evt – [in] Event. When set to evt_timerfinished the state machine progresses.
- Returns
-
tretcode init_rtc_slow(tevent evt)¶
Configure the Real Time Clock to peripheral to generate one interrupt every 30 minutes.
This is done to prevent the cuplTag from being stuck in the end_state when an error occurs during startup. This state must be entered before any possible transitions to the end_state.
Whatever the error, the cuplTag must wake up and try to start up again. A long time interval has been chosen so that the battery is not depleted.
-
tretcode init_rtc_1min(tevent evt)¶
Configure the Real Time Clock peripheral to generate one interrupt every minute.
The cuplTag spends most of the time in a deep sleep mode LPM3.5 to save power. Each minute, it wakes up for a few milliseconds to collect a sample or to increment minutecounter.
TURBO MODE is a special feature that is useful for test purposes. When enabled, the interrupt occurs every 3 seconds. To enable, set the time interval parameter to 0.
-
tretcode smpl_hdcreq(tevent evt)¶
Request a sample from the HDC2021 sensor.
A sample is requested by the MSP430 with hdc2020_startconv(). When data is ready, the HDC2021 makes a HIGH to LOW transition on its INT pin. To save power, the MSP430 sleeps whilst the measurement is taken.
The MSP430 pin connected to INT is made to raise an interrupt when a falling edge is detected.
-
tretcode smpl_hdcwait(tevent evt)¶
Wait in LPM3 whilst waiting for a data ready interrupt from the HDC2021 sensor.
-
tretcode err_msg(tevent evt)¶
Write the NDEF message ndefmsg_badtrns to the NFC EEPROM.
Notify the developer that an invalid state machine transition has been requested. The user should never see this. This will occur if a transition has been requested that does not exist in the state_transitions table.
-
tretcode end_state(tevent evt)¶
Put the MSP430 into deep sleep LPM3.5.
Minimise power consumption by powering down as much of the MSP430 (and cuplTag) as possible. Only the Real Time Clock and Backup Memory peripherals remain powered on.
The RTC can generate an interrupt to wake the MSP430 up. When this occurs, the program starts from a reset condition. Only the backup memory can be used to persist state.
This function disables GPIO interrupt sources. It stops any timers, in case these prevent LPM3.5 from being entered. It disables the watchdog, because this is power hungry and the RTC can be used instead (see init_rtc_slow()).
Most importantly it calls memoff() to make sure that the VMEM domain is powered down.
The Supply Voltage Supervisor is disabled to save power. It is not very useful in deep sleep mode. The battery voltage will decline very little between wake-ups from the RTC.
- Returns
tr_ok but this is to keep the compiler happy. Deep sleep is entered before the function returns.
-
tstate lookup_transitions(tstate curstate, tretcode rc)¶
Look up the next state in the Finite State Machine.
The look-up is performed by iterating through an array of transitions. An error state is returned if no match is found.
- Parameters
curstate – [in] Current state.
rc – [in] Code returned from the current state.
- Returns
Next state.
-
static void writetxt(const char *msgptr, int len)¶
Write an NDEF message to the NFC EEPROM.
The NDEF message normally contains one text record. It can be created with an external program and stored as a constant array. The function is used to display simple error messages to the end-user.
- Parameters
msgptr – [in] Pointer an NDEF message array.
len – [in] Length of the NDEF message.
-
static void wdog_kick()¶
Kick the watchdog, to prevent it from timing out.
This is done by writing to the watchdog control register.
-
static void start_timer(unsigned int intervalCycles)¶
Start a single-shot timer.
An interrupt fires when the timer has finished counting. The MSP430 can sleep in LPM3 whilst waiting for it. This saves power over delay loops.
The function is best suited to pausing execution for a short time (milliseconds).
- Parameters
intervalCycles – [in] Number of 4.096 kHz clock cycles to count.
-
static void memoff()¶
Power down the VMEM domain.
The load switch enable pin is set low, breaking the circuit between VDD and VMEM. This is done to save power in sleep mode. The NT3H2111 EEPROM will otherwise draw ~10uA.
-
static tretcode reqmemon(tevent evt)¶
Enable power to the VMEM domain.
Configure a pin to receive interrupts from the humidity sensor. Set the load switch enable pin HIGH to power up the VMEM domain from VDD.
After this function has been called, the MSP430 must sleep whilst waiting for a Timer interrupt. When this fires, the VMEM voltage should be stable.
-
static tretcode waitmemon(tevent evt)¶
Wait for the VMEM voltage to stabilise after power on.
Timer_B1 must be started with start_timer() prior to calling this function.
- Parameters
evt – [in] Event. When set to evt_timerfinished, I2C is enabled and the state machine progresses.
-
void main(void)¶
-
void TIMER1_B0_ISR(void)¶
- if ((P1IN &BIT1)==0)
Variables
-
int timerFlag = 0¶
Flag set by the Timer Interrupt Service Routine.
-
int hdcFlag = 0¶
Flag set by the HDC2021 humidity sensor data-ready Interrupt Service Routine.
-
int minutecounter = 0¶
Incremented each time the sampling loop is run.
-
const char ndefmsg_progmode[] = {0x03, 0x3D, 0xD1, 0x01, 0x39, 0x54, 0x02, 0x65, 0x6E, 0x50, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D, 0x6D, 0x69, 0x6E, 0x67, 0x20, 0x4D, 0x6F, 0x64, 0x65, 0x2E, 0x20, 0x43, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x20, 0x74, 0x6F, 0x20, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6C, 0x20, 0x70, 0x6F, 0x72, 0x74, 0x20, 0x61, 0x74, 0x20, 0x39, 0x36, 0x30, 0x30, 0x20, 0x62, 0x61, 0x75, 0x64, 0x2E, 0xFE}¶
Hard-coded NDEF message containing one text record “Programming Mode. Connect to serial port at 9600 baud.”
-
const char ndefmsg_noconfig[] = {0x03, 0x2D, 0xD1, 0x01, 0x29, 0x54, 0x02, 0x65, 0x6E, 0x43, 0x6F, 0x6E, 0x66, 0x69, 0x67, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6B, 0x20, 0x66, 0x61, 0x69, 0x6C, 0x65, 0x64, 0x2E, 0x20, 0x53, 0x65, 0x65, 0x20, 0x63, 0x75, 0x70, 0x6C, 0x54, 0x61, 0x67, 0x20, 0x64, 0x6F, 0x63, 0x73, 0x2E, 0xFE}¶
Hard-coded NDEF message containing one text record “Config check failed. See cuplTag docs.”
-
const char ndefmsg_badtrns[] = {0x03, 0x27, 0xD1, 0x01, 0x23, 0x54, 0x02, 0x65, 0x6E, 0x45, 0x72, 0x72, 0x6F, 0x72, 0x3A, 0x20, 0x49, 0x6E, 0x76, 0x61, 0x6C, 0x69, 0x64, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x74, 0x72, 0x61, 0x6E, 0x73, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x2E, 0xFE}¶
Hard-coded NDEF message containing one text record “Error: Invalid state transition.”
-
tretcode (*state_fcns[])(tevent) = {init_state, init_reqmemon, init_waitmemon, init_ntag, init_progmode, init_configcheck, init_errorcheck, init_wakeupcheck, init_batvwait, init_rtc_slow, init_rtc_1min, smpl_checkcounter, smpl_hdcreq, smpl_hdcwait, smpl_hdcread, smpl_wait, err_msg, end_state}¶
-
struct transition state_transitions[]¶
The state transition table.
-
struct transition¶