-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.c
318 lines (284 loc) · 12.2 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
/***************************************************************************//**
* @file
* @brief Servo_2015
* @author Ralf Gerhauser
* @version 2016-10-05
*
* This application consists of the following modules:
* - Servo.c - The servo control module.
* - ExtInt.c - External interrupt handler.
* - Keys.c - Key interrupt handling and translation.
* - AlarmClock.c - Alarm clock and timers facility.
* - clock.c - An implementation of the POSIX time() function.
* - eeprom_emulation.c - EEPROM emulation via FLASH
*
* Parts of the code are based on the example code of AN0006 "tickless calender"
* and AN0019 "EEPROM emulation" from Energy Micro AS.
*
***************************************************************************//**
*
* Parts are Copyright 2013 Energy Micro AS, http://www.energymicro.com
*
*******************************************************************************
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
* 4. The source and compiled code may only be used on Energy Micro "EFM32"
* microcontrollers and "EFR4" radios.
*
* DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
* obligation to support this Software. Energy Micro AS is providing the
* Software "AS IS", with no express or implied warranties of any kind,
* including, but not limited to, any implied warranties of merchantability
* or fitness for any particular purpose or warranties against infringement
* of any proprietary rights of a third party.
*
* Energy Micro AS will not be liable for any consequential, incidental, or
* special damages, or any other relief, or for any claim by any third party,
* arising from your use of this Software.
*
****************************************************************************//*
Revision History:
2016-10-05,rage Completed version 1.0, added support for external inputs.
2016-09-09,rage Initial version.
*/
/*!
* @mainpage
* <b>Description</b><br>
* This is the firmware for project Servo_2015, a small board which allows to
* control a servo via its PWM input. Setup and control of the servo is done
* via 3 keys, and, for selecting the two end positions, also via two external
* inputs, please see below. The firmware supports soft-start, i.e. the servo
* speed will be ramped-up during the start phase.
*
* <b>Microcontroller</b><br>
* The heart of the board is an EFM32G230 microcontroller. It provides two
* different clock domains: All low-energy peripheral is clocked via a
* 32.768kHz external XTAL. The MCU and other high performance peripheral
* uses a high-frequency clock. The board must be configured to use the
* external 32MHz XTAL for that purpose, set define @ref USE_EXT_32MHZ_CLOCK
* to 1.
*
* <b>Keys (Push Buttons)</b><br>
* There exist 3 push buttons on the board. When asserted, the following
* action will be taken:
* - In setup mode, S1 increases the current value, this can be the servo
* position, or the servo speed. If the key is kept asserted, autorepeat
* gets active. During normal operation, S1 lets the servo move to end
* position 2.
* - In setup mode, S2 decreases the current value, this can be the servo
* position, or the servo speed. If the key is kept asserted, autorepeat
* gets active. During normal operation, S2 lets the servo move to end
* position 1.
* - S3 changes between the setup modes, these are: end position 1, end
* position 2, speed selection, exit (storing values in FLASH).
*
* If no key has been asserted for more than 30 seconds, the setup mode is
* aborted and the firmware returns to normal operation.
*
* <b>Setup Mode</b><br>
* The servo setup procedure consists of 4 steps:
* -# Adjust end position 1. This can be the most left or the most right end
* point. This position will be reached after power-up if valid servo data
* has been found in FLASH.
* -# Adjust end position 2. This is the opposite end position to position 1.
* -# Adjust servo speed. The servo permanently moves between the two end
* points. The speed can be adjusted via S1 and S2.
* -# Asserting S3 the 4th time stores the servo parameters into FLASH and
* returns to normal operation.
*
* <b>LEDs</b><br>
* There is just one red LED connected to GPIO port PC0.
* - It is enabled for a short time during power-up to show the board is
* healthy.
* - During the servo setup, the LED shows the current mode (1 to 3) by
* flashing the respective number of times, followed by a pause.
*
* <b>External Inputs</b><br>
* In normal operation mode, the servo can also be controlled via two external
* inputs, these are:
* - Port PC6 is equivalent to S1, a low edge signal lets the servo move to end
* position 2 (this port may also be used as SDA for an I2C connection).
* - Port PC7 is equivalent to S2, a low edge signal lets the servo move to end
* position 1 (this port may also be used as SCL for an I2C connection).
*
* <b>Firmware</b><br>
* The firmware consists of an initialization part and a main loop, also called
* service execution loop. The initialization part sets up all modules, enables
* devices and interrupts. The service execution loop handles all tasks that
* must not be executed in interrupt context.
*
* After power-up or reset the following actions are performed:
* -# Basic initialization of MCU and clocks
* -# LED is switched on for test purposes (lamp test)
* -# Further hardware initialization (Keys, Interrupts, Alarm Clock)
* -# LED is switched off
* -# If no valid servo parameters have been found, setup mode 1 is activated,
* the LED flashes one time, followed by a pause.
* -# If valid servo parameters exist, the servo moves to end position 1.
*
* The program then enters the Service Execution Loop which takes care of:
* - Servo setup or control (interrupt driven, runs in background)
* - Entering the right energy mode
*/
/*=============================== Header Files ===============================*/
#include <stdio.h>
#include "em_device.h"
#include "em_chip.h"
#include "em_cmu.h"
#include "em_emu.h"
#include "em_dma.h"
#include "config.h" // include project configuration parameters
#include "ExtInt.h"
#include "Keys.h"
#include "AlarmClock.h"
#include "Servo.h"
/*================================ Global Data ===============================*/
/*! @brief Flag to indicate that an Interrupt occurred in the meantime.
*
* This flag must be set <b>true</b> by any interrupt service routine that
* requires actions in the service execution loop of main(). This prevents
* the system from entering sleep mode, so the action can be taken before.
*/
volatile bool g_flgIRQ;
/*! @brief Modules that require EM1.
*
* This global variable is a bit mask for all modules that require EM1.
* Standard peripherals would stop working in EM2 because clocks, etc. are
* disabled. Therefore it is required for software modules that make use
* of such devices, to set the appropriate bit in this mask, as long as they
* need EM1. This prevents the power management of this application to enter
* EM2. The enumeration @ref EM1_MODULES lists those modules.
* Low-Power peripherals, e.g. the RTC still work in EM1.
*
* Examples:
*
@code
// Module Servo requires EM1, set bit in bit mask
Bit(g_EM1_ModuleMask, EM1_MOD_SERVO) = 1;
...
// Module Servo is no longer active, clear bit in bit mask
Bit(g_EM1_ModuleMask, EM1_MOD_SERVO) = 0;
@endcode
*/
volatile uint16_t g_EM1_ModuleMask;
/*================================ Local Data ================================*/
/*! EXTI initialization structure
*
* Connect the external interrupts of the push buttons to the key handler, the
* DCF77 signal to the atomic clock module, the outer and inner light barrier
* to their handler.
*/
static const EXTI_INIT l_ExtIntCfg[] =
{ // IntBitMask, IntFct
{ KEY_EXTI_MASK, KeyHandler }, // Keys
{ TRIG_EXTI_MASK, TrigHandler }, // external trigger
{ 0, NULL }
};
/*!
* Initialization structure to define a function to be called for each
* translated key.
*/
static const KEY_INIT l_KeyInit =
{
.AR_Threshold = 250, // Threshold in [ms] after autorepeat starts
.AR_Rate = 20, // Key rate in [ms] when autorepeat is active
.KeyFct = ServoKeyHdl // Fct. to be called for each translated key
};
/*=========================== Forward Declarations ===========================*/
static void cmuSetup(void);
/******************************************************************************
* @brief Main function
*****************************************************************************/
int main( void )
{
/* Initialize chip - handle erratas */
CHIP_Init();
/* Set up clocks */
cmuSetup();
#ifdef DEBUG
dbgInit();
#endif
/* Configure port to drive the red Power-On LED (LED1) - show we are alive */
GPIO_PinModeSet (POWER_LED_PORT, POWER_LED_PIN, gpioModePushPull, 1);
/* Initialize PWM Output for Servo */
ServoInit();
/*
* All modules that make use of external interrupts (EXTI) should be
* initialized before calling ExtIntInit() because this enables the
* interrupts, so IRQ handler may be executed immediately!
*/
/* Initialize key hardware */
KeyInit (&l_KeyInit);
/*
* Initialize External Interrupts
*/
ExtIntInit (l_ExtIntCfg);
/* Initialize the Alarm Clock module */
AlarmClockInit();
msDelay(500); // show LED for 0.5s, then switch it off
POWER_LED = 0;
/* Enable all other External Interrupts */
ExtIntEnableAll();
/* ============================================ *
* ========== Service Execution Loop ========== *
* ============================================ */
while (1)
{
/*
* Check for current power mode: If a minimum of one active module
* requires EM1, i.e. <g_EM1_ModuleMask> is not 0, this will be
* entered. If no one requires EM1 activity, EM2 is entered.
*/
if (! g_flgIRQ) // enter EM only if no IRQ occurred
{
if (g_EM1_ModuleMask)
EMU_EnterEM1(); // EM1 - Sleep Mode
else
EMU_EnterEM2(true); // EM2 - Deep Sleep Mode
}
else
{
g_flgIRQ = false; // clear flag to enter EM the next time
}
}
}
/******************************************************************************
* @brief Configure Clocks
*
* This local routine is called once from main() to configure all required
* clocks of the EFM32 device.
*
*****************************************************************************/
static void cmuSetup(void)
{
/* Start LFXO and wait until it is stable */
CMU_OscillatorEnable(cmuOsc_LFXO, true, true);
#if USE_EXT_32MHZ_CLOCK
/* Start HFXO and wait until it is stable */
CMU_OscillatorEnable(cmuOsc_HFXO, true, true);
/* Select HFXO as clock source for HFCLK */
CMU_ClockSelectSet(cmuClock_HF, cmuSelect_HFXO);
/* Disable HFRCO */
CMU_OscillatorEnable(cmuOsc_HFRCO, false, false);
#endif
/* Route the LFXO clock to the RTC and set the prescaler */
CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFXO); // RTC, LETIMER
CMU_ClockSelectSet(cmuClock_LFB, cmuSelect_LFXO); // LEUART0/1
CMU_ClockEnable(cmuClock_RTC, true);
/* Prescaler of 1 = 30 us of resolution and overflow each 8 min */
CMU_ClockDivSet(cmuClock_RTC, cmuClkDiv_1);
/* Enable clock to low energy modules */
CMU_ClockEnable(cmuClock_CORELE, true);
/* Enable clock for HF peripherals (ADC, DAC, I2C, TIMER, and USART) */
CMU_ClockEnable(cmuClock_HFPER, true);
/* Enable clock to GPIO */
CMU_ClockEnable(cmuClock_GPIO, true);
}