-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfinite_state_machine.c
179 lines (141 loc) · 4.13 KB
/
finite_state_machine.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/*
* finite_state_machine.c
*
* Created: 18.12.2019 13:00:00
* Author : Jan
*
* Simple cyclic finite state machine (FSM) with an LED
* Uses Interrupts for the state changes
* States:
* 1) LED off
* 2) LED on
* 3) LED blinks (1 s on, 1 s off)
* 4) LED blinks (250 ms on, 250 ms off)
* 5) LED dims (going on from 0-100% and going of from 0-100%)
*
* Connections:
* --> LED to Pin 10 on Arduino
* --> Pin 12 --> Button --> GND
*/
#define F_CPU 16000000L // Specify oscillator frequency; Needed for _delay_ms()
#include <avr/io.h> // The header files come with the drivers for the Arduino
#include <avr/sleep.h>
#include <util/delay.h> // They include the _delay_ms() function and some defines (Right click --> Go to implementation)
#include <avr/interrupt.h>
int b = 1; // cyclic switch variable; gets raised by 1 everytime the button is pressed (and back to 1 at the end); needs to be global, so that it can be changed by the interrupt!
#define LED_ON PORTB |= (1 << 2)
#define LED_OFF PORTB &= ~(1 << 2)
static void led_init(void)
{
DDRB = (1 << 2); // Configures Pin 2 of PORTB as output (should be Pin 10 on arduino); Code: 1 gets shifted by 2
}
static void button_init(void)
{
DDRB = ~(1 << 4); // Configures Pin 4 of PORTB as input (should be Pin 12 on arduino; must be connected to GND via a switch)
PINB |= (1 << 4); // Pull input Pin at 5V as default
PCICR |= (1 << PCIE0); // PCICR = Pin Change Interrupt Control Register; see OneNote for details, short: selects which Port is used for the interrupt
PCMSK0 |= (1 << PCINT4); // makes Pin 4 to interrupt pin; PCMSK = Pin Change Enable Mask Register
}
void PWM_init(void) // Use Timer1 as this one can be used for my used Pin
{
TCCR1B |= (1 << WGM12); // CTC timer mode
TIMSK1 |= (1 << OCIE1A) | (1 << OCIE1B); // Output compare values
OCR1A = 800;
OCR1B = 0; // Changing the two values changes duty cycle!
}
void fading_init(uint8_t milliS) // Use Timer0 for changing the duty cycle
{
TCCR0A |= (1 << WGM01); // set to CTC mode
// =milliS*7.8125-1 works out with formular from video with pre-scalar = 1024
OCR0A = milliS*7.8125-1; // changes the top value
TIMSK0 |= (1 << OCIE0A); // enable interrupt
}
void led_blink_1s(void)
{
PORTB ^= (1 << 2); // changes state of bit
_delay_ms(1000);
}
void led_blink_250ms(void)
{
PORTB ^= (1 << 2); // changes state of bit
_delay_ms(250);
}
void led_dim(void)
{
TCCR1B |= (1 << CS10); // starts PWM (with timer with no pre-scalar)
TCCR0B |= (1 << CS02) | (1 << CS00); // starts fading Timer; sets pre-scalar to 1024
}
ISR(PCINT0_vect) /* pin change interrupt service routine */
{
if (!(PINB & (1 << 4))) // Button pressed
{
b++;
_delay_ms(50); // SW entprellen! Keep it short, so that one can press the button very quickly!
}
}
ISR(TIMER1_COMPA_vect) // Timer1 used for PWM
{
LED_ON;
}
ISR(TIMER1_COMPB_vect) // Timer1 used for PWM
{
LED_OFF;
}
ISR(TIMER0_COMPA_vect) // Timer0 for fading (changing duty cycle)
{
uint16_t period = OCR1A;
uint16_t duty = OCR1B;
if (duty < period)
{
duty++;
}
else
{
duty = 0;
}
OCR1B = duty;
}
int main(void)
{
led_init();
button_init();
PWM_init();
fading_init(2); // wait 2ms for each step (up to 800 --> ~1.6s in total)
sei(); // enables interrupts globally
int number_of_states = 5;
while (1)
{
if (b > number_of_states || b <= 0) // resets switch variable b to initial value
{
b = 1;
}
switch (b) // Main switch routine that selects the different states of the FSM
{
case 1:
TCCR1B &= ~(1 << CS10); // stops timer1!
TCCR0B &= ~(1 << CS02); // stops timer0!
TCCR0B &= ~(1 << CS00);
LED_OFF;
break;
case 2:
LED_ON; // Could add an additional if to only execute led_on() if the LED is OFF.
break;
case 3:
led_blink_1s(); // 1s blink; it seems that _delay_ms() does not work with a variable as input, that's why I wrote seperate functions!
break;
case 4:
led_blink_250ms();
break;
case 5:
led_dim();
break;
default:
LED_OFF;
TCCR1B &= ~(1 << CS10); // stops timer1!
TCCR0B &= ~(1 << CS02); // stops timer0!
TCCR0B &= ~(1 << CS00);
b = 1;
break;
}
}
}