ledsw1_cn.c - Example of implementing a FSM using a timer and change notification interrupt.¶
Demonstrates the use of a events to create an energy-efficient FSM implementation. All of the FSM work is done in the ISR.
#include <stdio.h>
#include "pic24_all.h"
void update_state(void);
Configuration¶
LED1 configuration and access¶
#define CONFIG_LED1() CONFIG_RB14_AS_DIG_OUTPUT()
#define LED1 (_LATB14)
Pushbutton configuration and access¶
void config_pb() {
CONFIG_RB13_AS_DIG_INPUT();
ENABLE_RB13_PULLUP();
Give the pullup some time to take effect.
DELAY_US(1);
}
#if (HARDWARE_PLATFORM == EMBEDDED_C1)
# define PB_PRESSED() (_RB7 == 0)
# define PB_RELEASED() (_RB7 == 1)
#else
# define PB_PRESSED() (_RB13 == 0)
# define PB_RELEASED() (_RB13 == 1)
#endif
Switch configuration and access¶
void config_sw() {
CONFIG_RB12_AS_DIG_INPUT();
ENABLE_RB12_PULLUP();
Give the pullup some time to take effect.
DELAY_US(1);
}
#define SW (_RB12)
Change notification interrupt configuration¶
Enable change-notification interrupts on the pushbutton.
void config_cn(void) {
Enable change notifications on RB13 specifically.
ENABLE_RB13_CN_INTERRUPT();
Clear the interrupt flag.
_CNIF = 0;
Choose a priority.
_CNIP = 1;
Enable the Change Notification general interrupt.
_CNIE = 1;
}
Timer3 interrupt configuration¶
Configure the timer to produce interrupts. Do not yet turn it on.
void configTimer3(void) {
Ensure that Timer2,3 configured as separate timers by turning 32-bit mode off.
T2CONbits.T32 = 0;
T3CON set like this for documentation purposes. This could be replaced by T3CON = 0x0020.
T3CON = T3_OFF | T3_IDLE_CON | T3_GATE_OFF
| T3_SOURCE_INT
| T3_PS_1_256;
Clear the interrupt flag.
_T3IF = 0;
Choose a priority.
_T3IP = 1;
Enable the timer3 interrupt.
_T3IE = 1;
}
Interrupts¶
Timer arming¶
This function prepares the timer to interrupt after the given delay (in ms).
void timer3_arm(uint16_t u16_time_ms) {
Convert arm time to timer3 ticks.
PR3 = msToU16Ticks(u16_time_ms, getTimerPrescale(T3CONbits)) - 1;
Zero then start the timer.
TMR3 = 0;
T3CONbits.TON = 1;
If a timer interrupt has occurred but not been processed, discard it.
_T3IF = 0;
}
Change notification¶
Interrupt service routine for change notification interrupts. Disable this interrupt to avoid bounce, then arm the timer to update the FSM state after a debounce delay.
void _ISR _CNInterrupt(void) {
Acknowledge the interrupt, then disable it. Otherwise, any switch bounce would cause spurious interrupts.
_CNIF = 0;
_CNIE = 0;
Schedule a timer interrupt after a debounce delay.
timer3_arm(DEBOUNCE_DLY);
}
Timer¶
Interrupt Service Routine for Timer3
void _ISR _T3Interrupt(void) {
Clear the interrupt flag.
_T3IF = 0;
Stop the timer; the debounce delay is done.
T3CONbits.TON = 0;
Clear the change notification interrupt because switch bounce may have set it.
_CNIF = 0;
Re-enable change notification interrupts, since the debounce delay is done.
_CNIE = 1;
Run our state machine.
update_state();
}
State machine¶
First, define the states, along with a human-readable version.
typedef enum {
STATE_RELEASED1,
STATE_PRESSED1,
STATE_RELEASED2,
STATE_PRESSED2,
STATE_RELEASED3_BLINK,
STATE_PRESSED3,
} state_t;
const char* apsz_state_names[] = {
"STATE_RELEASED1 - LED is off",
"STATE_PRESSED1",
"STATE_RELEASED2 - LED is on",
"STATE_PRESSED2 - SW2 on goes to blink else go to RELEASED1",
"STATE_RELEASED3_BLINK - LED blinks, waiting for SW1 press",
"STATE_PRESSED3 - LED is on",
};
Provide a convenient function to print out the state.
void print_state(state_t e_state) {
Verify that the state has a string representation before printing it.
ASSERT(e_state <= N_ELEMENTS(apsz_state_names));
outString(apsz_state_names[e_state]);
outChar('\n');
}
This function defines the state machine.
void update_state(void) {
static state_t e_state = STATE_RELEASED1;
The number of times the LED was toggled in the blink state
static uint16_t u16_led_toggles;
switch (e_state) {
case STATE_RELEASED1:
LED1 = 0;
if (PB_PRESSED()) {
e_state = STATE_PRESSED1;
}
break;
case STATE_PRESSED1:
if (PB_RELEASED()) {
e_state = STATE_RELEASED2;
}
break;
case STATE_RELEASED2:
LED1 = 1;
if (PB_PRESSED()) {
e_state = STATE_PRESSED2;
}
break;
case STATE_PRESSED2:
if (PB_RELEASED() && SW) {
e_state = STATE_RELEASED3_BLINK;
Zero the toggled count when entering the blink state.
u16_led_toggles = 0;
Schedule a timer interrupt to start the blinking.
timer3_arm(250);
}
if (PB_RELEASED() && !SW) {
e_state = STATE_RELEASED1;
}
break;
case STATE_RELEASED3_BLINK:
Toggle the LED.
LED1 = !LED1;
u16_led_toggles++;
printf("toggles = %d\n", u16_led_toggles);
Schedule a timer interrupt to start the blinking.
timer3_arm(250);
if (u16_led_toggles >= 10) {
e_state = STATE_RELEASED1;
}
if (PB_PRESSED()) {
e_state = STATE_PRESSED3;
}
break;
case STATE_PRESSED3:
LED1 = 1;
if (PB_RELEASED()) {
e_state = STATE_RELEASED1;
}
break;
default:
ASSERT(0);
}
print_state(e_state);
}
Main¶
int main(void) {
configBasic(HELLO_MSG);
config_pb();
config_sw();
CONFIG_LED1();
configTimer3();
config_cn();
while (1) {
Enter a low-power state, which still keeps timer3 and uart1 running.
IDLE();
}
}