-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.c
345 lines (314 loc) · 10.4 KB
/
main.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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
//******************************************************************************
// MSP430F5529 SSD1306 OLED Display
//
// Description: This demo connects two MSP430's via the I2C bus. The master
// transmits to the slave. This is the MASTER CODE. It cntinuously
// transmits an array of data and demonstrates how to implement an I2C
// master transmitter sending multiple bytes using the USCI_B0 TX interrupt.
// ACLK = n/a, MCLK = SMCLK = BRCLK = default DCO = ~1.045MHz
//
//
// /|\ /|\
// MSP430F5529 10k 10k SSD1306 OLED
// master | | Display
// ----------------- | | -----------------
// -|XIN P4.1/UCB0SDA|<-|----+->|SDA |-
// | | | | |
// -|XOUT | | | |-
// | P4.2/UCB0SCL|<-+------>|SCL |
// | | | |
// | | | |
// | P6.0 |----------> relay control
// | P6.1 |----------> relay control
//
//******************************************************************************
#include "main.h"
extern unsigned char *PTxData; // Pointer to TX data, defined in i2c.h
extern unsigned char TXByteCtr; // number of bytes to transmit, defined in i2c.h
/*---------- global variables ----------*/
uint8_t S1buttonCounter = 0;
uint8_t S2buttonCounter = 0;
volatile bool displayClearRequest = false;
volatile unsigned int seconds = 0;
volatile unsigned int minutes = 0;
volatile unsigned int hours = 0;
volatile unsigned int days = 0;
volatile unsigned int remainingMinutes = 0;
volatile unsigned int setTime = 1;
volatile unsigned int remainingTime = 0;
volatile unsigned int numberOfIrrigation = 0;
EState state = INIT;
int main(void)
{
WDTCTL = WDTPW | WDTHOLD; // Stop the watchdog timer during initialization
clock_init(); // Initialize clock system
gpio_init(); // Initialize GPIO
i2c_init(); // Initialize UCB1 I2C, port 4 pins 1, 2
ssd1306_init(); // Initialize SSD1306 OLED
watchdog_init(); // Initialize watchdog
__enable_interrupt(); // Enable global interrupts
turn_relays_off(); // Initialize the relays
ssd1306_clearDisplay(); // Clear garbage data
__no_operation();
__bis_SR_register(GIE); // Enter LPM0, enable interrupts
while(1)
{
// Clear display between state changes
if (true == displayClearRequest)
{
ssd1306_clearDisplay();
}
#ifdef DEBUG
ssd1306_printUI32(0, 0, seconds, HCENTERUL_OFF);
ssd1306_printUI32(0, 2, minutes, HCENTERUL_OFF);
ssd1306_printUI32(0, 4, setTime, HCENTERUL_OFF);
#endif
switch(state)
{
case IRRIGATION:
{
displayClearRequest = false;
ssd1306_printText(0,6, "Status: Irrigation");
if (seconds <= IRRIGATION_TIME)
{
#if defined(DEBUG)
turn_led_on(RED);
#endif
turn_relays_on();
}
else
{
#ifdef DEBUG
turn_led_off(RED);
#endif
turn_relays_off();
state = COUNTING_DOWN;
}
break;
}
case COUNTING_DOWN:
{
displayClearRequest = false;
ssd1306_printText(0, 6, "Status: Counting down");
ssd1306_printUI32(0, 0, numberOfIrrigation, HCENTERUL_OFF);
break;
}
case UNDER_CONFIGURATION:
{
displayClearRequest = false;
ssd1306_printText(0, 6, "Status: Configuration");
ssd1306_printUI32(0, 0, seconds, HCENTERUL_OFF);
ssd1306_printText(14, 0, "seconds");
ssd1306_printUI32(0, 2, minutes, HCENTERUL_OFF);
ssd1306_printText(14, 2, "minutes");
ssd1306_printUI32(0, 4, setTime, HCENTERUL_OFF);
ssd1306_printText(14, 4, "set time");
break;
}
case INIT:
{
displayClearRequest = false;
ssd1306_printText(0,6, "Status: Init");
break;
}
default:
{
ssd1306_printText(0,6, "Status: Error");
break;
}
}
watchdog_feed(); // Feed the watchdog timer in the main loop
}
return 0;
}
/*!
* \brief Initialize watchdog.
* \param[in]: None
* \param[out]: None
* \return void
* \retval None
*/
void watchdog_init()
{
WDTCTL = WDTPW | WDTCNTCL | WDTSSEL0 | WDTTMSEL | WDTIS1; // Configure watchdog timer
SFRIE1 |= WDTIE; // Enable watchdog interrupt
}
/*!
* \brief Reset the watchdog timer and prevent it from triggering the interrupt.
* \param[in]: None
* \param[out]: None
* \return void
* \retval None
*/
void watchdog_feed()
{
WDTCTL = (WDTPW | WDTCNTCL | WDTSSEL0 | WDTTMSEL | WDTIS1); // Clear watchdog timer
}
//------------------------------------------------------------------------------
// Watchdog_interrupt is called when the watchdog timer times out, generating a software reset.
//------------------------------------------------------------------------------
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=WDT_VECTOR
__interrupt void watchdog_interrupt()
#elif defined(__GNUC__)
void __attribute__ ((interrupt(WDT_VECTOR))) USCI_B0_ISR (void)
#else
#error Compiler not supported!
#endif
{
// Code execution reached watchdog interrupt
// Perform any necessary operations here
// Reset the microcontroller
WDTCTL = 0xDEAD; // Invalid value to generate a software reset
}
//------------------------------------------------------------------------------
// The USCIAB1TX_ISR is structured such that it can be used to transmit any
// number of bytes by pre-loading TXByteCtr with the byte count. Also, TXData
// points to the next byte to transmit.
//------------------------------------------------------------------------------
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = USCI_B1_VECTOR
__interrupt void USCI_B1_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(USCI_B0_VECTOR))) USCI_B0_ISR (void)
#else
#error Compiler not supported!
#endif
{
switch(__even_in_range(UCB1IV,12))
{
case 0: break; // Vector 0: No interrupts
case 2: break; // Vector 2: ALIFG
case 4: break; // Vector 4: NACKIFG
case 6: break; // Vector 6: STTIFG
case 8: break; // Vector 8: STPIFG
case 10: break; // Vector 10: RXIFG
case 12: // Vector 12: TXIFG
if (TXByteCtr) // Check TX byte counter
{
UCB1TXBUF = *PTxData++; // Load TX buffer
TXByteCtr--; // Decrement TX byte counter
}
else
{
UCB1CTL1 |= UCTXSTP; // I2C stop condition
UCB1IFG &= ~UCTXIFG; // Clear USCI_B1 TX int flag
__bic_SR_register_on_exit(LPM0_bits); // Exit LPM0
}
default: break;
}
}
//------------------------------------------------------------------------------
// Port 2 interrupt service routine
//------------------------------------------------------------------------------
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = PORT2_VECTOR // P2.1 push button is used as hardware interrupt
__interrupt void port_2(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(PORT2_VECTOR))) Port_2 (void)
#else
#error Compiler not supported!
#endif
{
if (INIT == state)
{
state = UNDER_CONFIGURATION;
displayClearRequest = true;
}
else if (UNDER_CONFIGURATION == state)
{
state = COUNTING_DOWN;
displayClearRequest = true;
}
else if ((COUNTING_DOWN == state) ||
(IRRIGATION == state))
{
state = UNDER_CONFIGURATION;
displayClearRequest = true;
}
else
{}
S1buttonCounter++;
if (S2buttonCounter >= UINT8_MAX)
{
S2buttonCounter = UINT8_MIN;
}
__delay_cycles(782000);
P2IFG &= ~BIT1; // Clear P2.1 IFG
}
//------------------------------------------------------------------------------
// Port 1 interrupt service routine
//------------------------------------------------------------------------------
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = PORT1_VECTOR // P1.1 push button is used as hardware interrupt
__interrupt void port_1(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(PORT1_VECTOR))) Port_1 (void)
#else
#error Compiler not supported!
#endif
{
S2buttonCounter++;
setTime = S2buttonCounter;
if (S2buttonCounter >= UINT8_MAX)
{
S2buttonCounter = UINT8_MIN;
}
__delay_cycles(782000);
P1IFG &= ~BIT1; // Clear P1.1 IFG
}
//------------------------------------------------------------------------------
// Timer1 A0 interrupt service routine
//------------------------------------------------------------------------------
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=TIMER1_A0_VECTOR
__interrupt void TIMER1_A0_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(TIMER1_A0_VECTOR))) TIMER1_A0_ISR (void)
#else
#error Compiler not supported!
#endif
{
seconds++;
if (seconds >= 60)
{
#ifdef DEBUG
remainingMinutes++;
#endif
minutes++;
seconds = 0;
}
if (minutes >= 60)
{
hours++;
minutes = 0;
}
if (hours >= 24)
{
days++;
}
if ((setTime == days) &&
(IRRIGATION != state) &&
(UNDER_CONFIGURATION != state) &&
(INIT != state))
{
state = IRRIGATION;
days = 0;
numberOfIrrigation++;
displayClearRequest = true;
}
#ifdef DEBUG
// for debugging
if ((setTime == remainingMinutes) &&
(IRRIGATION != state) &&
(UNDER_CONFIGURATION != state) &&
(INIT != state))
{
state = IRRIGATION;
remainingMinutes = 0;
numberOfIrrigation++;
displayClearRequest = true;
}
#endif
TA1CCTL0 &= ~CCIFG; // Clear interrupt flag
}