Statistics
| Branch: | Tag: | Revision:

amiro-blt / Target / Modules / PowerManagement_1-1 / Boot / main.c @ ea906b44

History | View | Annotate | Download (56.668 KB)

1
/************************************************************************************//**
2
* \file         Demo\ARMCM4_STM32_Olimex_STM32E407_GCC\Boot\main.c
3
* \brief        Bootloader application source file.
4
* \ingroup      Boot_ARMCM4_STM32_Olimex_STM32E407_GCC
5
* \internal
6
*----------------------------------------------------------------------------------------
7
*                          C O P Y R I G H T
8
*----------------------------------------------------------------------------------------
9
*   Copyright (c) 2013  by Feaser    http://www.feaser.com    All rights reserved
10
*
11
*----------------------------------------------------------------------------------------
12
*                            L I C E N S E
13
*----------------------------------------------------------------------------------------
14
* This file is part of OpenBLT. OpenBLT is free software: you can redistribute it and/or
15
* modify it under the terms of the GNU General Public License as published by the Free
16
* Software Foundation, either version 3 of the License, or (at your option) any later
17
* version.
18
*
19
* OpenBLT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
20
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
21
* PURPOSE. See the GNU General Public License for more details.
22
*
23
* You should have received a copy of the GNU General Public License along with OpenBLT.
24
* If not, see <http://www.gnu.org/licenses/>.
25
*
26
* A special exception to the GPL is included to allow you to distribute a combined work
27
* that includes OpenBLT without being obliged to provide the source code for any
28
* proprietary components. The exception text is included at the bottom of the license
29
* file <license.html>.
30
*
31
* \endinternal
32
****************************************************************************************/
33

    
34
/****************************************************************************************
35
* Include files
36
****************************************************************************************/
37
#include "boot.h"                                /* bootloader generic header          */
38
#include "com.h"
39
#include "ARMCM4_STM32/types.h"
40
#include "AMiRo/amiroblt.h"
41
#include "helper.h"
42
#include "iodef.h"
43

    
44
/****************************************************************************************
45
* Defines
46
****************************************************************************************/
47
#define HIBERNATE_TIME_MS       5000
48

    
49
/****************************************************************************************
50
* Function prototypes and static variables
51
****************************************************************************************/
52
static void Init(void);
53

    
54
static void initGpio();
55
static void initExti();
56
void configGpioForShutdown();
57
void systemPowerDown();
58

    
59
ErrorStatus handleColdReset();
60
ErrorStatus handleSoftwareReset();
61
ErrorStatus handleUartDnWakeup();
62
ErrorStatus handlePathDcWakeup();
63
ErrorStatus handleTouchWakeup();
64
ErrorStatus handleIwdgWakeup();
65

    
66
static void indicateHibernate();
67
static void AdcSingleMeasurement();
68

    
69
ADC_TypeDef* setupADC(ADC_TypeDef* adc, const uint16_t low_th, const uint16_t high_th);
70
uint16_t configIwdg(const uint16_t ms);
71

    
72
ErrorStatus shutdownDisambiguationProcedure(const uint8_t type);
73
void shutdownToTransportation();
74
void shutdownToDeepsleep();
75
void shutdownToHibernate();
76
void shutdownAndRestart();
77

    
78
volatile blBackupRegister_t backup_reg;
79

    
80
/****************************************************************************************
81
* Callback configuration
82
****************************************************************************************/
83
void blCallbackShutdownTransportation(void);
84
void blCallbackShutdownDeepsleep(void);
85
void blCallbackShutdownHibernate(void);
86
void blCallbackShutdownRestart(void);
87
void blCallbackHandleShutdownRequest(void);
88

    
89
const blCallbackTable_t cbtable __attribute__ ((section ("_callback_table"))) = {
90
  .magicNumber = BL_MAGIC_NUMBER,
91
  .vBootloader = {BL_VERSION_ID_AMiRoBLT_Beta, BL_VERSION_MAJOR, BL_VERSION_MINOR, 3},
92
  .vSSSP = {BL_VERSION_ID_SSSP, BL_SSSP_VERSION_MAJOR, BL_SSSP_VERSION_MINOR, 0},
93
  .vCompiler = {BL_VERSION_ID_GCC, __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__},  // currently only GCC is supported
94
  .cbShutdownHibernate = blCallbackShutdownHibernate,
95
  .cbShutdownDeepsleep = blCallbackShutdownDeepsleep,
96
  .cbShutdownTransportation = blCallbackShutdownTransportation,
97
  .cbShutdownRestart = blCallbackShutdownRestart,
98
  .cbHandleShutdownRequest = blCallbackHandleShutdownRequest,
99
  .cb5 = (void*)0,
100
  .cb6 = (void*)0,
101
  .cb7 = (void*)0,
102
  .cb8 = (void*)0,
103
  .cb9 = (void*)0,
104
  .cb10 = (void*)0,
105
  .cb11 = (void*)0
106
};
107

    
108
/************************************************************************************//**
109
** \brief     This is the entry point for the bootloader application and is called
110
**            by the reset interrupt vector after the C-startup routines executed.
111
** \return    none.
112
**
113
****************************************************************************************/
114
void main(void)
115
{
116
  /* initialize the microcontroller */
117
  Init();
118

    
119
  /* activate some required clocks */
120
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE);
121
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
122
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
123

    
124
  /* initialize GPIOs and EXTI lines */
125
  initGpio();
126
  setLed(BLT_TRUE);
127
  initExti();
128

    
129
  /* initialize the timer */
130
  TimerInit(); // do not use saTimerInit() in order to initialize the static variable.
131

    
132
  /* read the backup register */
133
  backup_reg.raw = RTC_ReadBackupRegister(BL_RTC_BACKUP_REG);
134

    
135
  /* detect the primary reason for this wakeup/restart */
136
  backup_reg.wakeup_pri_reason =
137
      ((RCC_GetFlagStatus(RCC_FLAG_LPWRRST) == SET) ? BL_WAKEUP_PRI_RSN_LPWRRST : 0) |
138
      ((RCC_GetFlagStatus(RCC_FLAG_WWDGRST) == SET) ? BL_WAKEUP_PRI_RSN_WWDGRST : 0) |
139
      ((RCC_GetFlagStatus(RCC_FLAG_IWDGRST) == SET) ? BL_WAKEUP_PRI_RSN_IWDGRST : 0) |
140
      ((RCC_GetFlagStatus(RCC_FLAG_SFTRST) == SET) ? BL_WAKEUP_PRI_RSN_SFTRST : 0)   |
141
      ((RCC_GetFlagStatus(RCC_FLAG_PORRST) == SET) ? BL_WAKEUP_PRI_RSN_PORRST : 0)   |
142
      ((RCC_GetFlagStatus(RCC_FLAG_PINRST) == SET) ? BL_WAKEUP_PRI_RSN_PINRST : 0)   |
143
      ((RCC_GetFlagStatus(RCC_FLAG_BORRST) == SET) ? BL_WAKEUP_PRI_RSN_BORRST : 0)   |
144
      ((PWR_GetFlagStatus(PWR_FLAG_WU) == SET) ? BL_WAKEUP_PRI_RSN_WKUP : 0);
145

    
146
  /* when woken from standby mode, detect the secondary reason for this wakeup/reset */
147
  if ( (backup_reg.wakeup_pri_reason & BL_WAKEUP_PRI_RSN_WKUP) && (PWR_GetFlagStatus(PWR_FLAG_SB) == SET) ) {
148
    if (GPIO_ReadInputDataBit(SYS_UART_DN_GPIO, SYS_UART_DN_PIN) == Bit_RESET) {
149
      backup_reg.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_UART;
150
    } else if (GPIO_ReadInputDataBit(PATH_DC_GPIO, PATH_DC_PIN) == Bit_SET) {
151
      backup_reg.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_PWRPLUG;
152
    } else {
153
      backup_reg.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_TOUCH;
154
    }
155
  } else {
156
    backup_reg.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_UNKNOWN;
157
  }
158

    
159
  /* store the information about this wakeup/restart in the backup register */
160
  PWR_BackupAccessCmd(ENABLE);
161
  RTC_WriteBackupRegister(BL_RTC_BACKUP_REG, backup_reg.raw);
162

    
163
  /* clear the flags */
164
  RCC_ClearFlag();
165
  PWR_ClearFlag(PWR_FLAG_WU);
166

    
167
  setLed(BLT_FALSE);
168

    
169
  /* handle different wakeup/reset reasons */
170
  ErrorStatus status = ERROR;
171
  if (backup_reg.wakeup_pri_reason & BL_WAKEUP_PRI_RSN_SFTRST) {
172
    /* system was reset by software */
173
    status = handleSoftwareReset();
174
  } else if (backup_reg.wakeup_pri_reason & BL_WAKEUP_PRI_RSN_WKUP) {
175
    /* system was woken via WKUP pin */
176
    /* differeciate between thre wakeup types */
177
    switch (backup_reg.wakeup_sec_reason) {
178
      case BL_WAKEUP_SEC_RSN_UART:
179
        status = handleUartDnWakeup();
180
        break;
181
      case BL_WAKEUP_SEC_RSN_PWRPLUG:
182
        status = handlePathDcWakeup();
183
        break;
184
      case BL_WAKEUP_SEC_RSN_TOUCH:
185
        status = handleTouchWakeup();
186
        break;
187
      default:
188
        status = ERROR;
189
        break;
190
    }
191
  } else if (backup_reg.wakeup_pri_reason & BL_WAKEUP_PRI_RSN_IWDGRST) {
192
    /* system was woken by IWDG */
193
    status = handleIwdgWakeup();
194
  } else if (backup_reg.wakeup_pri_reason == BL_WAKEUP_PRI_RSN_PINRST) {
195
    /* system was reset via NRST pin */
196
    status = handleColdReset();
197
  } else {
198
    /* system was woken/reset for an unexpected reason.
199
     * In this case the LED blinks "SOS" (... --- ...) and the system resets.
200
     */
201
    blinkSOS(1);
202
    status = ERROR;
203
    backup_reg.shutdown_pri_reason = BL_SHUTDOWN_PRI_RSN_RESTART;
204
    backup_reg.shutdown_sec_reason = BL_SHUTDOWN_SEC_RSN_UNKNOWN;
205
    RTC_WriteBackupRegister(BL_RTC_BACKUP_REG, backup_reg.raw);
206
    NVIC_SystemReset();
207
  }
208

    
209
  /* if something went wrong, signal this failure */
210
  if (status != SUCCESS) {
211
    blinkSOSinf();
212
  }
213

    
214
  return;
215
} /*** end of main ***/
216

    
217

    
218
/************************************************************************************//**
219
** \brief     Initializes the microcontroller.
220
** \return    none.
221
**
222
****************************************************************************************/
223
static void Init(void)
224
{
225
#if (BOOT_COM_UART_ENABLE > 0 || BOOT_GATE_UART_ENABLE > 0)
226
  GPIO_InitTypeDef  GPIO_InitStructure;
227
#elif (BOOT_FILE_SYS_ENABLE > 0)
228
  GPIO_InitTypeDef  GPIO_InitStructure;
229
  USART_InitTypeDef USART_InitStructure;
230
#elif (BOOT_COM_CAN_ENABLE > 0 || BOOT_GATE_CAN_ENABLE > 0)
231
  GPIO_InitTypeDef  GPIO_InitStructure;
232
#endif
233

    
234
  /* initialize the system and its clocks */
235
  SystemInit();
236
#if (BOOT_COM_UART_ENABLE > 0 || BOOT_GATE_UART_ENABLE > 0)
237
  /* enable UART peripheral clock */
238
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
239
  /* enable GPIO peripheral clock for transmitter and receiver pins */
240
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
241
  /* connect the pin to the peripherals alternate function */
242
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
243
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
244
  /* configure USART Tx as alternate function  */
245
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
246
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
247
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
248
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
249
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
250
  GPIO_Init(GPIOA, &GPIO_InitStructure);
251
  /* configure USART Rx as alternate function */
252
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
253
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
254
  GPIO_Init(GPIOA, &GPIO_InitStructure);
255
#endif
256

    
257
#if (BOOT_COM_BLUETOOTH_UART_ENABLE > 0)
258
  /* enable UART peripheral clock */
259
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
260

    
261
  /* enable GPIO peripheral clock for transmitter and receiver pins */
262
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
263
  /* connect the pin to the peripherals alternate function */
264
  GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_USART3);
265
  GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_USART3);
266
  /* configure USART Tx as alternate function  */
267
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
268
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
269
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
270
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
271
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
272
  GPIO_Init(GPIOC, &GPIO_InitStructure);
273
  /* configure USART Rx as alternate function */
274
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
275
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
276
  GPIO_Init(GPIOC, &GPIO_InitStructure);
277

    
278
  /* Configure Bluetooth reset pin */
279
  GPIO_InitTypeDef  gpio_init;
280
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
281
  gpio_init.GPIO_Pin   = BT_RST_PIN;
282
  gpio_init.GPIO_OType = GPIO_OType_OD;
283
  gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL;
284
  gpio_init.GPIO_Mode = GPIO_Mode_OUT;
285
  gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
286
  GPIO_Init(BT_RST_GPIO, &gpio_init);
287
  /* Reset Bluetooth reset pin */
288
  GPIO_ResetBits(BT_RST_GPIO, BT_RST_PIN);
289
#endif
290

    
291

    
292
#if (BOOT_COM_CAN_ENABLE > 0 || BOOT_GATE_CAN_ENABLE > 0)
293
  /* enable clocks for CAN transmitter and receiver pins */
294
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
295
  /* select alternate function for the CAN pins */
296
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource11, GPIO_AF_CAN1);
297
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource12, GPIO_AF_CAN1);
298
  /* configure CAN RX and TX pins */
299
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
300
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
301
  GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
302
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
303
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
304
  GPIO_Init(GPIOA, &GPIO_InitStructure);
305
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
306
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
307
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
308
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
309
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
310
  GPIO_Init(GPIOA, &GPIO_InitStructure);
311
#endif
312
} /*** end of Init ***/
313

    
314
/*
315
 * Initializes all GPIO used by the bootloader
316
 */
317
static void initGpio() {
318
  GPIO_InitTypeDef gpio_init;
319

    
320
  /*
321
   * OUTPUTS
322
   */
323

    
324
  /* initialize LED and push it up (inactive) */
325
  GPIO_SetBits(LED_GPIO, LED_PIN);
326
  gpio_init.GPIO_Pin    = LED_PIN;
327
  gpio_init.GPIO_Mode   = GPIO_Mode_OUT;
328
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
329
  gpio_init.GPIO_OType  = GPIO_OType_PP;
330
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
331
  GPIO_Init(LED_GPIO, &gpio_init);
332

    
333
  /* initialize SYS_PD_N and push it up (inactive) */
334
  GPIO_SetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
335
  gpio_init.GPIO_Pin    = SYS_PD_N_PIN;
336
  gpio_init.GPIO_Mode   = GPIO_Mode_OUT;
337
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
338
  gpio_init.GPIO_OType  = GPIO_OType_OD;
339
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
340
  GPIO_Init(SYS_PD_N_GPIO, &gpio_init);
341

    
342
  /* initialize SYS_SYNC_N and pull it down (active) */
343
  GPIO_ResetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
344
  gpio_init.GPIO_Pin    = SYS_SYNC_N_PIN;
345
  gpio_init.GPIO_Mode   = GPIO_Mode_OUT;
346
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
347
  gpio_init.GPIO_OType  = GPIO_OType_OD;
348
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
349
  GPIO_Init(SYS_SYNC_N_GPIO, &gpio_init);
350

    
351
  /* initialize SYS_WARMRST_N and pull it down (active) */
352
  GPIO_ResetBits(SYS_WARMRST_N_GPIO, SYS_WARMRST_N_PIN);
353
  gpio_init.GPIO_Pin    = SYS_WARMRST_N_PIN;
354
  gpio_init.GPIO_Mode   = GPIO_Mode_OUT;
355
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
356
  gpio_init.GPIO_OType  = GPIO_OType_OD;
357
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
358
  GPIO_Init(SYS_WARMRST_N_GPIO, &gpio_init);
359

    
360
  /* initialize SYS_UART_DN and push it up (inactive) */
361
  GPIO_SetBits(SYS_UART_DN_GPIO, SYS_UART_DN_PIN);
362
  gpio_init.GPIO_Pin    = SYS_UART_DN_PIN;
363
  gpio_init.GPIO_Mode   = GPIO_Mode_OUT;
364
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
365
  gpio_init.GPIO_OType  = GPIO_OType_OD;
366
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
367
  GPIO_Init(SYS_UART_DN_GPIO, &gpio_init);
368

    
369
  /* initialize POWER_EN and pull it down (inactive) */
370
  GPIO_ResetBits(POWER_EN_GPIO, POWER_EN_PIN);
371
  gpio_init.GPIO_Pin    = POWER_EN_PIN;
372
  gpio_init.GPIO_Mode   = GPIO_Mode_OUT;
373
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
374
  gpio_init.GPIO_OType  = GPIO_OType_PP;
375
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
376
  GPIO_Init(POWER_EN_GPIO, &gpio_init);
377

    
378
  /* initialize SYS_REG_EN and pull it down (inactive) */
379
  GPIO_ResetBits(SYS_REG_EN_GPIO, SYS_REG_EN_PIN);
380
  gpio_init.GPIO_Pin    = SYS_REG_EN_PIN;
381
  gpio_init.GPIO_Mode   = GPIO_Mode_OUT;
382
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
383
  gpio_init.GPIO_OType  = GPIO_OType_PP;
384
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
385
  GPIO_Init(SYS_REG_EN_GPIO, &gpio_init);
386

    
387
  /* initialize CHARGE_EN1_N and CHARGE_EN2_N and push them up (inactive) */
388
  GPIO_SetBits(CHARGE_EN1_N_GPIO, CHARGE_EN1_N_PIN);
389
  GPIO_SetBits(CHARGE_EN2_N_GPIO, CHARGE_EN2_N_PIN);
390
  gpio_init.GPIO_Pin    = CHARGE_EN1_N_PIN;
391
  gpio_init.GPIO_Mode   = GPIO_Mode_OUT;
392
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
393
  gpio_init.GPIO_OType  = GPIO_OType_PP;
394
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
395
  GPIO_Init(CHARGE_EN1_N_GPIO, &gpio_init);
396
  gpio_init.GPIO_Pin    = CHARGE_EN2_N_PIN;
397
  GPIO_Init(CHARGE_EN2_N_GPIO, &gpio_init);
398

    
399
  /*
400
   * INPUTS
401
   */
402

    
403
  /* initialize PATH_DC */
404
  gpio_init.GPIO_Pin    = PATH_DC_PIN;
405
  gpio_init.GPIO_Mode   = GPIO_Mode_IN;
406
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
407
  gpio_init.GPIO_OType  = GPIO_OType_PP;
408
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
409
  GPIO_Init(PATH_DC_GPIO, &gpio_init);
410

    
411
  /* initialize TOUCH_INT_N */
412
  gpio_init.GPIO_Pin    = TOUCH_INT_N_PIN;
413
  gpio_init.GPIO_Mode   = GPIO_Mode_IN;
414
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
415
  gpio_init.GPIO_OType  = GPIO_OType_PP;
416
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
417
  GPIO_Init(TOUCH_INT_N_GPIO, &gpio_init);
418

    
419
  /* initialize VSYS_SENSE as analog input */
420
  gpio_init.GPIO_Pin    = VSYS_SENSE_PIN;
421
  gpio_init.GPIO_Mode   = GPIO_Mode_AN;
422
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
423
  gpio_init.GPIO_OType  = GPIO_OType_PP;
424
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
425
  GPIO_Init(VSYS_SENSE_GPIO, &gpio_init);
426

    
427
  /* initialize GPIOB4, since it is configured in alternate function mode on reset */
428
  gpio_init.GPIO_Pin    = CHARGE_STAT2A_PIN;
429
  gpio_init.GPIO_Mode   = GPIO_Mode_IN;
430
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
431
  gpio_init.GPIO_OType  = GPIO_OType_PP;
432
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
433
  GPIO_Init(CHARGE_STAT2A_GPIO, &gpio_init);
434

    
435
  return;
436
} /*** end of initGpio ***/
437

    
438
/*
439
 * Initialize all EXTI lines
440
 */
441
static void initExti() {
442
  /* configure EXTI lines */
443
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource0); // IR_INT1_N
444
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource0); // CHARGE_STAT1A
445
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource1); // GAUGE_BATLOW1
446
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource2); // GAUGE_BATGD1_N
447
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource3); // SYS_UART_DN
448
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource4); // CHARGE_STAT2A
449
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource4); // IR_INT2_N
450
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource5); // TOUCH_INT_N
451
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource6); // GAUGE_BATLOW2
452
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource7); // GAUGE_BATGD2_N
453
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource8); // PATH_DC
454
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource9); // SYS_SPI_DIR
455
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource12); // SYS_SYNC_N
456
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource13); // SYS_PD_N
457
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource14); // SYS_WARMRST_N
458
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource15); // SYS_UART_UP
459

    
460
  return;
461
} /*** end of initExti ***/
462

    
463
/*
464
 * Signals, which type of low-power mode the system shall enter after the shutdown sequence.
465
 */
466
ErrorStatus shutdownDisambiguationProcedure(const uint8_t type) {
467
  GPIO_SetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
468
  ErrorStatus ret_val = ERROR;
469

    
470
  switch (type) {
471
    case BL_SHUTDOWN_PRI_RSN_UNKNOWN:
472
    case BL_SHUTDOWN_PRI_RSN_HIBERNATE:
473
    case BL_SHUTDOWN_PRI_RSN_DEEPSLEEP:
474
    case BL_SHUTDOWN_PRI_RSN_TRANSPORT:
475
    {
476
      // broadcast a number of pulses, depending on the argument
477
      uint8_t pulse_counter = 0;
478
      for (pulse_counter = 0; pulse_counter < type; ++pulse_counter) {
479
        msleep(1);
480
        GPIO_ResetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
481
        msleep(1);
482
        GPIO_SetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
483
      }
484
      // wait for timeout
485
      msleep(10);
486
      ret_val = SUCCESS;
487
      break;
488
    }
489
    case BL_SHUTDOWN_PRI_RSN_RESTART:
490
    {
491
      // since there is no ambiguity for restart requests, no pulses are generated
492
      msleep(10);
493
      ret_val = SUCCESS;
494
      break;
495
    }
496
    default:
497
      ret_val = ERROR;
498
      break;
499
  }
500

    
501
  return ret_val;
502
} /*** end of shutdownDisambiguationProcedure ***/
503

    
504
/*
505
 * Final shutdown of the system to enter transportation mode.
506
 */
507
void shutdownToTransportation() {
508
  /* configure some criticpal GPIOs as input
509
   * This is required, because otherwise some hardware might be powered through these signals */
510
  configGpioForShutdown();
511

    
512
  /* power down the system */
513
  systemPowerDown();
514

    
515
  /* deactivate the WKUP pin */
516
  PWR_WakeUpPinCmd(DISABLE);
517

    
518
  /* deactivate any RTC related events */
519
  RTC_WakeUpCmd(DISABLE);
520
  RTC_TamperCmd(RTC_Tamper_1, DISABLE);
521
  RTC_TimeStampCmd(RTC_TimeStampEdge_Rising, DISABLE);
522
  RTC_TimeStampCmd(RTC_TimeStampEdge_Falling, DISABLE);
523
  RTC_ClearFlag(~0);
524

    
525
  /* disable the IWDG */
526
  IWDG_ReloadCounter();
527

    
528
  /* write some information to the backup register */
529
  blBackupRegister_t backup;
530
  backup.shutdown_pri_reason = BL_SHUTDOWN_PRI_RSN_TRANSPORT;
531
  backup.shutdown_sec_reason = BL_SHUTDOWN_SEC_RSN_UNKNOWN;
532
  backup.wakeup_pri_reason = BL_WAKEUP_PRI_RSN_UNKNOWN;
533
  backup.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_UNKNOWN;
534
  PWR_BackupAccessCmd(ENABLE);
535
  RTC_WriteBackupRegister(BL_RTC_BACKUP_REG, backup.raw);
536

    
537
  /* morse 'OK' via the LED to signal that shutdown was successful */
538
  blinkOK(1);
539

    
540
  /* enter standby mode */
541
  PWR_EnterSTANDBYMode();
542

    
543
  return;
544
} /*** end of shutdownToTransportation ***/
545

    
546
/*
547
 * Final shutdown of the system to enter deepsleep mode.
548
 */
549
void shutdownToDeepsleep() {
550
  /* configure some criticpal GPIOs as input
551
   * This is required, because otherwise some hardware might be powered through these signals */
552
  configGpioForShutdown();
553

    
554
  /* power down the system */
555
  systemPowerDown();
556

    
557
  /* activate the WKUP pin */
558
  PWR_WakeUpPinCmd(ENABLE);
559

    
560
  /*
561
   * Configuration of RTC and IWDG belongs to the OS.
562
   */
563

    
564
  /* write some information to the backup register */
565
  blBackupRegister_t backup;
566
  backup.shutdown_pri_reason = BL_SHUTDOWN_PRI_RSN_DEEPSLEEP;
567
  backup.shutdown_sec_reason = BL_SHUTDOWN_SEC_RSN_UNKNOWN;
568
  backup.wakeup_pri_reason = BL_WAKEUP_PRI_RSN_UNKNOWN;
569
  backup.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_UNKNOWN;
570
  PWR_BackupAccessCmd(ENABLE);
571
  RTC_WriteBackupRegister(BL_RTC_BACKUP_REG, backup.raw);
572

    
573
  /* morse 'OK' via the LED to signal that shutdown was successful */
574
  blinkOK(1);
575

    
576
  /* enter standby mode or restart the system in case a power plug is already present */
577
  if (GPIO_ReadInputDataBit(PATH_DC_GPIO, PATH_DC_PIN) != Bit_SET) {
578
    PWR_EnterSTANDBYMode();
579
  } else {
580
    NVIC_SystemReset();
581
  }
582

    
583
  return;
584
} /*** end of shutdownToDeepsleep ***/
585

    
586
/*
587
 * Final shutdown of the system to enter hibernate mode.
588
 */
589
void shutdownToHibernate() {
590
  /* configure some criticpal GPIOs as input
591
   * This is required, because otherwise some hardware might be powered through these signals */
592
  configGpioForShutdown();
593

    
594
  /* power down the system */
595
  systemPowerDown();
596

    
597
  /* write some information to the backup register */
598
  blBackupRegister_t backup;
599
  backup.shutdown_pri_reason = BL_SHUTDOWN_PRI_RSN_HIBERNATE;
600
  backup.shutdown_sec_reason = BL_SHUTDOWN_SEC_RSN_UNKNOWN;
601
  backup.wakeup_pri_reason = BL_WAKEUP_PRI_RSN_UNKNOWN;
602
  backup.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_UNKNOWN;
603
  PWR_BackupAccessCmd(ENABLE);
604
  RTC_WriteBackupRegister(BL_RTC_BACKUP_REG, backup.raw);
605

    
606
  /* morse 'OK' via the LED to signal that shutdown was successful */
607
  blinkOK(1);
608

    
609
  /* reset the MCU */
610
  NVIC_SystemReset();
611

    
612
  return;
613
} /*** end of shutdownToHibernate ***/
614

    
615
/*
616
 * Final shutdown of the system and restart.
617
 */
618
void shutdownAndRestart() {
619
  /* configure some criticpal GPIOs as input
620
   * This is required, because otherwise some hardware might be powered through these signals */
621
  configGpioForShutdown();
622

    
623
  /* power down the system */
624
  systemPowerDown();
625

    
626
  /* write some information to the backup register */
627
  blBackupRegister_t backup;
628
  backup.shutdown_pri_reason = BL_SHUTDOWN_PRI_RSN_RESTART;
629
  backup.shutdown_sec_reason = BL_SHUTDOWN_SEC_RSN_UNKNOWN;
630
  backup.wakeup_pri_reason = BL_WAKEUP_PRI_RSN_UNKNOWN;
631
  backup.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_UNKNOWN;
632
  PWR_BackupAccessCmd(ENABLE);
633
  RTC_WriteBackupRegister(BL_RTC_BACKUP_REG, backup.raw);
634

    
635
  /* morse 'OK' via the LED to signal that shutdown was successful */
636
  blinkOK(1);
637

    
638
  /* reset the MCU */
639
  NVIC_SystemReset();
640

    
641
  return;
642
} /*** end of shutdownAndRestart ***/
643

    
644
/*
645
 * Configures some GPIO pins as inputs for safety reasons.
646
 * Under certain circumstances, these pins might power hardware that is supposed to be shut down.
647
 */
648
void configGpioForShutdown() {
649
  /* setup the configuration */
650
  GPIO_InitTypeDef gpio_init;
651
  gpio_init.GPIO_Mode   = GPIO_Mode_IN;
652
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
653
  gpio_init.GPIO_OType  = GPIO_OType_PP;
654
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
655

    
656
  /* configure SYS_UART_TX */
657
  gpio_init.GPIO_Pin = SYS_UART_TX_PIN;
658
  GPIO_Init(SYS_UART_TX_GPIO, &gpio_init);
659

    
660
  /* configure all SYS_SPI signals */
661
  gpio_init.GPIO_Pin = SYS_SPI_SS0_N_PIN;
662
  GPIO_Init(SYS_SPI_SS0_N_GPIO, &gpio_init);
663
  gpio_init.GPIO_Pin = SYS_SPI_SCLK_PIN;
664
  GPIO_Init(SYS_SPI_SCLK_GPIO, &gpio_init);
665
  gpio_init.GPIO_Pin = SYS_SPI_MISO_PIN;
666
  GPIO_Init(SYS_SPI_MISO_GPIO, &gpio_init);
667
  gpio_init.GPIO_Pin = SYS_SPI_MOSI_PIN;
668
  GPIO_Init(SYS_SPI_MOSI_GPIO, &gpio_init);
669
  gpio_init.GPIO_Pin = SYS_SPI_SS1_N_PIN;
670
  GPIO_Init(SYS_SPI_SS1_N_GPIO, &gpio_init);
671
  gpio_init.GPIO_Pin = SYS_SPI_DIR_PIN;
672
  GPIO_Init(SYS_SPI_DIR_GPIO, &gpio_init);
673

    
674
  /* configure CAN_TX */
675
  gpio_init.GPIO_Pin = CAN_TX_PIN;
676
  GPIO_Init(CAN_TX_GPIO, &gpio_init);
677

    
678
  /* configure all Bluetooth signals */
679
  gpio_init.GPIO_Pin = BT_CTS_PIN;
680
  GPIO_Init(BT_CTS_GPIO, &gpio_init);
681
  gpio_init.GPIO_Pin = BT_RX_PIN;
682
  GPIO_Init(BT_RX_GPIO, &gpio_init);
683

    
684
  return;
685
} /*** end of configGpioForShutdown ***/
686

    
687
/*
688
 * Disables all regulated voltages and finally cuts power to the rest of the system.
689
 */
690
void systemPowerDown() {
691
  setLed(BLT_TRUE);
692

    
693
  /* make sure that all other modules are shut down */
694
  msleep(10);
695

    
696
  /* reset slave modules */
697
  GPIO_ResetBits(SYS_WARMRST_N_GPIO, SYS_WARMRST_N_PIN);
698

    
699
  /* disable voltage regulators */
700
  GPIO_ResetBits(SYS_REG_EN_GPIO, SYS_REG_EN_PIN);
701

    
702
  /* cut power */
703
  GPIO_ResetBits(POWER_EN_GPIO, POWER_EN_PIN);
704

    
705
  /* make sure, all capacitors are discharged */
706
  msleep(100);
707

    
708
  setLed(BLT_FALSE);
709

    
710
  return;
711
} /*** end of systemPowerDown ***/
712

    
713
/*
714
 * Cofigures the independent watchdog (IWDG) to fire after the specified time when it is enabled.
715
 * The argument is the requested time in milliseconds.
716
 * The time that was actually set for the IWDG is returned by the function (again in milliseconds).
717
 * In some cases the returned value might differ from the requested one, but if so, it will alwyas be smaller.
718
 * Although the IWDG provides higher resolutions than milliseconds, these are not supported by this function.
719
 */
720
uint16_t configIwdg(const uint16_t ms) {
721
  /* apply an upper bound to the ms argument */
722
  uint16_t ms_capped = (ms >= 0x8000) ? 0x7FFF : ms;
723

    
724
  /* detect the best fitting prescaler and compute the according reload value */
725
  uint8_t prescaler = 0;
726
  uint16_t reload_val = 0;
727
  if (ms_capped >= 0x4000) {
728
    prescaler = IWDG_Prescaler_256;
729
    reload_val = ms_capped >> 3;  // note: this corresponds to a floor function
730
    ms_capped = reload_val << 3;  // this applies the floor function to ms_capped
731
  } else if (ms_capped >= 0x2000) {
732
    prescaler = IWDG_Prescaler_128;
733
    reload_val = ms_capped >> 2;  // note: this corresponds to a floor function
734
    ms_capped = reload_val << 2;  // this applies the floor function to ms_capped
735
  } else if (ms_capped >= 0x1000) {
736
    ms_capped &= ~(0x0001);
737
    prescaler = IWDG_Prescaler_64;
738
    reload_val = ms_capped >> 1;  // note: this corresponds to a floor function
739
    ms_capped = reload_val << 1;  // this applies the floor function to ms_capped
740
  } else {
741
    prescaler = IWDG_Prescaler_32;
742
    reload_val = ms_capped;
743
  }
744

    
745
  /* configure the IWDG */
746
  if (reload_val > 0) {
747
    IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
748
    IWDG_SetPrescaler(prescaler);
749
    IWDG_SetReload(reload_val);
750
    IWDG_WriteAccessCmd(IWDG_WriteAccess_Disable);
751
  }
752

    
753
  return ms_capped;
754
} /*** end of configIWDG ***/
755

    
756
/*
757
 * System was reset via the NRST pin or the reason could not be detected.
758
 * In this case, everything is started up.
759
 * If an attempt for an OS update is detected, flashing mode is entered.
760
 * Otherwise, the system will boot the OS.
761
 */
762
ErrorStatus handleColdReset() {
763
  /* activate system power and wait some time to ensure stable voltages */
764
  setLed(BLT_TRUE);
765
  GPIO_SetBits(POWER_EN_GPIO, POWER_EN_PIN);
766
  msleep(10);
767
  GPIO_SetBits(SYS_REG_EN_GPIO, SYS_REG_EN_PIN);
768
  msleep(10);
769
  setLed(BLT_FALSE);
770

    
771
  /* drive SYS_WARMRST_N high (inactive) */
772
  GPIO_SetBits(SYS_WARMRST_N_GPIO, SYS_WARMRST_N_PIN);
773

    
774
  /* enable CAN clock
775
   * Note that CAN1 shares reception filters with CAN1 so for CAN2 the CAN1 peripheral also needs to be enabled. */
776
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN2 | RCC_APB1Periph_CAN1, ENABLE);
777

    
778
  /* wait 1ms to make sure that all modules are running and started the bootloader */
779
  msleep(1);
780

    
781
  /* initialize the bootloader */
782
  BootInit();
783

    
784
  /* start the infinite program loop */
785
  uint32_t loopStartTime = 0;
786
  saTimerUpdate(&loopStartTime);
787
  uint32_t currentTime = loopStartTime;
788
  while (1)
789
  {
790
//    /* make the LED "double-blink" */
791
//    saTimerUpdate(&currentTime);
792
//    if (currentTime < loopStartTime + 50) {
793
//      setLed(BLT_TRUE);
794
//    } else if (currentTime < loopStartTime + 50+100) {
795
//      setLed(BLT_FALSE);
796
//    } else if (currentTime < loopStartTime + 50+100+50) {
797
//      setLed(BLT_TRUE);
798
//    } else if (currentTime < loopStartTime + 50+100+50+300) {
799
//      setLed(BLT_FALSE);
800
//    } else {
801
//      loopStartTime = currentTime;
802
//    }
803

    
804
    /* run the bootloader task */
805
    BootTask();
806

    
807
    /* check the SYS_PD_N signal */
808
    if (GPIO_ReadInputDataBit(SYS_PD_N_GPIO, SYS_PD_N_PIN) == Bit_RESET) {
809
      blCallbackHandleShutdownRequest();
810
      return SUCCESS;
811
    }
812
  }
813

    
814
  return ERROR;
815
} /*** end of handleColdReset ***/
816

    
817
/*
818
 * System was reset by software.
819
 * Depending on the argument, which was read from the 1st backup register (see main function) the effect of this function differs.
820
 * There are three cases that can occur:
821
 * - The system was reset to enter hibernate mode.
822
 *   In this case the system will enter a medium power saving mode (hibernate mode), but can be charged via the charging pins.
823
 *   The system can be woken up in the same way as in deepsleep mode (cf. blCallbackShutdownDeepsleep() function).
824
 * - The system was reset to reboot.
825
 *   In this case the system will restart in the same way as after a cold reset.
826
 * - The reason is unknown.
827
 *   This case will cause an error.
828
 */
829
ErrorStatus handleSoftwareReset() {
830
  /* action depends on original shutdown reason */
831
  switch (backup_reg.shutdown_pri_reason) {
832
    case BL_SHUTDOWN_PRI_RSN_HIBERNATE:
833
    {
834
      /* activate the WKUP pin */
835
      PWR_WakeUpPinCmd(ENABLE);
836

    
837
      /* deactivate any RTC related events */
838
      RTC_WakeUpCmd(DISABLE);
839
      RTC_TamperCmd(RTC_Tamper_1, DISABLE);
840
      RTC_TimeStampCmd(RTC_TimeStampEdge_Rising, DISABLE);
841
      RTC_TimeStampCmd(RTC_TimeStampEdge_Falling, DISABLE);
842

    
843
      /* configure the IWDG to wake the system from standby mode */
844
      uint16_t iwdg_ms = 1;
845
      if (GPIO_ReadInputDataBit(PATH_DC_GPIO, PATH_DC_PIN) != Bit_SET) {
846
        /* if a power plug is detected, fire immediately (1ms), else fire after the defined hibernate time */
847
        iwdg_ms = HIBERNATE_TIME_MS;
848
      }
849
      configIwdg(iwdg_ms);
850
      IWDG_Enable();
851

    
852
      /* enter standby mode */
853
      PWR_EnterSTANDBYMode();
854

    
855
      return SUCCESS;
856
      break;
857
    }
858
    case BL_SHUTDOWN_PRI_RSN_RESTART:
859
    {
860
      return handleColdReset();
861
      break;
862
    }
863
    case BL_SHUTDOWN_PRI_RSN_DEEPSLEEP:
864
    {
865
      if (GPIO_ReadInputDataBit(PATH_DC_GPIO, PATH_DC_PIN) == Bit_SET) {
866
        return handlePathDcWakeup();
867
      } else {
868
        blCallbackShutdownDeepsleep();
869
      }
870
      break;
871
    }
872
    default:
873
      return ERROR;
874
  }
875
  return ERROR;
876
} /*** end of handleSoftwareReset ***/
877

    
878
/*
879
 * System was woken up via the WKUP pin and the SYS_UART_DN signal was found to be responsible.
880
 * In this case, the system starts as after a cold reset.
881
 * this function is identical to handleTouchWakeup().
882
 */
883
ErrorStatus handleUartDnWakeup() {
884
  return handleColdReset();
885
} /*** end of hanldeUartDnWakeup ***/
886

    
887
/*
888
 * System was woken up via the WKUP pin and the PATH_DC signal was found to be responsible.
889
 * If the system was woken from deepsleep mode, it will enter hibernate mode to enable charging as long as the power plug is present.
890
 * In any other case, the system will just enter the previous low-power mode again.
891
 */
892
ErrorStatus handlePathDcWakeup() {
893
  /* reenter the previous low-power mode */
894
  switch (backup_reg.shutdown_pri_reason) {
895
    case BL_SHUTDOWN_PRI_RSN_HIBERNATE:
896
      blCallbackShutdownHibernate();
897
      return SUCCESS;
898
      break;
899
    case BL_SHUTDOWN_PRI_RSN_DEEPSLEEP:
900
      /* visualize that the power plug was detected
901
       * This is helpful for feedback, and required for the follwing reason:
902
       * When the power plug is detected, it takes some additional time for the ADC to detect a high voltage.
903
       * If the ADC detects a low voltage at the first attempt, the system will enter hibernate mode.
904
       * Thus, the ADC will measure the voltage again after several seconds and charging will start.
905
       * However, this behaviour does not meet the user expection.
906
       * Hence, the voltage has some to adapt at this point
907
       */
908
      setLed(BLT_TRUE);
909
      msleep(500);
910
      setLed(BLT_FALSE);
911

    
912
      return handleIwdgWakeup();
913
      break;
914
    case BL_SHUTDOWN_PRI_RSN_TRANSPORT:
915
      blCallbackShutdownTransportation();
916
      return SUCCESS;
917
      break;
918
    default:
919
      return ERROR;
920
      break;
921
  }
922

    
923
  return ERROR;
924
} /*** end of handlePathDcWakeup ***/
925

    
926
/*
927
 * System was woken up via the WKUP pin and the touch sensors were found to be responsible.
928
 * In this case the system starts as after an cold reset.
929
 * This function is identical to handleUartDnWakeup().
930
 */
931
ErrorStatus handleTouchWakeup() {
932
  return handleColdReset();
933
} /*** end of handleTouchWakeup ***/
934

    
935
/*
936
 * System was woken up via the IWDG.
937
 * In this case the ADC is configured and VSYS is measured once.
938
 * If VSYS is found to be high enough to charge the batteries, the system will stay active until VSYS drops or an EXTI event occurs.
939
 * Otherwise, the system will configure the IWDG to wake the system again after five seconds and enter standby mode.
940
 */
941
ErrorStatus handleIwdgWakeup() {
942
  /* handle different situations, depending on the backup data */
943
  if ((backup_reg.shutdown_pri_reason == BL_SHUTDOWN_PRI_RSN_HIBERNATE) ||
944
      (backup_reg.shutdown_pri_reason == BL_SHUTDOWN_PRI_RSN_DEEPSLEEP)) {
945
    /* handle periodic wakeup in hibernate mode and in deepsleep mode when a power plug was detetced */
946

    
947
    /* if in hibernate mode, indicate the DiWheelDrive to enter hibernate mode as well, so it will activate the charging pins */
948
    if (backup_reg.shutdown_pri_reason == BL_SHUTDOWN_PRI_RSN_HIBERNATE) {
949
      indicateHibernate();
950
    }
951

    
952
    /* measure the current voltage of VSYS */
953
    AdcSingleMeasurement();
954

    
955
    /* evaluate the value
956
     * The ADC value represents the analog voltage between Vref- (= GND = 0.0V) and Vref+ (= VDD = 3.3V) as 12-bit value.
957
     * Hence, the value read from the register is first scaled to [0V .. 3.3V].
958
     * Then, an additional factor 5.33 is applied to account the downscaling on the board.
959
     * Actually, the factor should be 5.0, but due to too large resistors it was corrected to 5.33.
960
     */
961
    if ( (((float)(ADC_GetConversionValue(ADC1)) / (float)(0x0FFF)) * 3.3f * 5.33f) < 9.0f ) {
962
      /* VSYS was found to be < 9V */
963

    
964
      /* re-enter power saving mode
965
       * If the system was shut down to deepsleep mode and the power plug was removed, re-enter deepsleep mode.
966
       * (This could be done earlier in this function, but since charging via the pins of the DeWheelDrive may be
967
       *  supported in the future, this is done after measuring VSYS)
968
       */
969
      if (backup_reg.shutdown_pri_reason == BL_SHUTDOWN_PRI_RSN_DEEPSLEEP &&
970
          GPIO_ReadInputDataBit(PATH_DC_GPIO, PATH_DC_PIN) == Bit_RESET) {
971
        blCallbackShutdownDeepsleep();
972
      } else {
973
        /* reconfigure the IWDG and power down for five seconds */
974
        configIwdg(HIBERNATE_TIME_MS);
975
        IWDG_Enable();
976

    
977
        /* enter standby mode */
978
        PWR_EnterSTANDBYMode();
979
      }
980

    
981
      return SUCCESS;
982
    } else {
983
      /* VSYS was found to be >= 9V */
984
      setLed(BLT_TRUE);
985

    
986
      /* charge the battieries */
987
      GPIO_ResetBits(CHARGE_EN1_N_GPIO, CHARGE_EN1_N_PIN);
988
      GPIO_ResetBits(CHARGE_EN2_N_GPIO, CHARGE_EN2_N_PIN);
989

    
990
      /* configure analog watchdoch to fire as soon as the voltage drops below 9V */
991
      ADC_DeInit();
992
      setupADC(ADC1, (uint16_t)(9.0f / 5.33f / 3.3f * (float)0x0FFF), 0x0FFF);
993

    
994
      EXTI_InitTypeDef exti;
995
      /* configure UART_DN EXTI */
996
      exti.EXTI_Line = EXTI_Line3;
997
      exti.EXTI_Mode = EXTI_Mode_Interrupt;
998
      exti.EXTI_Trigger = EXTI_Trigger_Falling;
999
      exti.EXTI_LineCmd = ENABLE;
1000
      EXTI_Init(&exti);
1001

    
1002
      /* configure TOUCH_INT_N EXTI */
1003
      exti.EXTI_Line = EXTI_Line5;
1004
      exti.EXTI_Mode = EXTI_Mode_Interrupt;
1005
      exti.EXTI_Trigger = EXTI_Trigger_Falling;
1006
      exti.EXTI_LineCmd = ENABLE;
1007
      EXTI_Init(&exti);
1008

    
1009
      /* configure PATH_DC EXTI */
1010
      if (backup_reg.shutdown_pri_reason == BL_SHUTDOWN_PRI_RSN_DEEPSLEEP) {
1011
        exti.EXTI_Line = EXTI_Line8;
1012
        exti.EXTI_Mode = EXTI_Mode_Interrupt;
1013
        exti.EXTI_Trigger = EXTI_Trigger_Falling;
1014
        exti.EXTI_LineCmd = ENABLE;
1015
        EXTI_Init(&exti);
1016
      }
1017

    
1018
      /* configure the NVIC so ADC and EXTI will be handled */
1019
      NVIC_InitTypeDef nvic;
1020
      nvic.NVIC_IRQChannel = ADC_IRQn;
1021
      nvic.NVIC_IRQChannelPreemptionPriority = 6;
1022
      nvic.NVIC_IRQChannelSubPriority = 6;
1023
      nvic.NVIC_IRQChannelCmd = ENABLE;
1024
      NVIC_Init(&nvic);
1025
      nvic.NVIC_IRQChannel = EXTI3_IRQn;
1026
      nvic.NVIC_IRQChannelPreemptionPriority = 6;
1027
      nvic.NVIC_IRQChannelSubPriority = 6;
1028
      nvic.NVIC_IRQChannelCmd = ENABLE;
1029
      NVIC_Init(&nvic);
1030
      NVIC_EnableIRQ(EXTI3_IRQn);
1031
      nvic.NVIC_IRQChannel = EXTI9_5_IRQn;
1032
      nvic.NVIC_IRQChannelPreemptionPriority = 6;
1033
      nvic.NVIC_IRQChannelSubPriority = 6;
1034
      nvic.NVIC_IRQChannelCmd = ENABLE;
1035
      NVIC_Init(&nvic);
1036
      NVIC_EnableIRQ(EXTI9_5_IRQn);
1037

    
1038
      /* activate the ADC */
1039
      ADC_SoftwareStartConv(ADC1);
1040

    
1041
      /* sleep until something happens */
1042
      __WFI();
1043

    
1044
      /* disable the chargers */
1045
      GPIO_SetBits(CHARGE_EN1_N_GPIO, CHARGE_EN1_N_PIN);
1046
      GPIO_SetBits(CHARGE_EN2_N_GPIO, CHARGE_EN2_N_PIN);
1047
      setLed(BLT_FALSE);
1048

    
1049
      /* evaluate wakeup reason */
1050
      // note: since I (tschoepp) don't know the difference between 'pending' and 'active' IRQs, both flags are ORed.
1051
      uint8_t wkup_rsn = BL_WAKEUP_SEC_RSN_UNKNOWN;
1052
      if ((NVIC_GetActive(ADC_IRQn) != 0 || NVIC_GetPendingIRQ(ADC_IRQn) != 0) &&
1053
          ADC_GetITStatus(ADC1, ADC_IT_AWD) == SET &&
1054
          ADC_GetFlagStatus(ADC1, ADC_FLAG_AWD) == SET) {
1055
        wkup_rsn |= BL_WAKEUP_SEC_RSN_VSYSLOW;
1056
      }
1057
      if ((NVIC_GetActive(EXTI3_IRQn) != 0 || NVIC_GetPendingIRQ(EXTI3_IRQn) != 0) &&
1058
          EXTI_GetFlagStatus(EXTI_Line3) == SET) {
1059
        wkup_rsn |= BL_WAKEUP_SEC_RSN_UART;
1060
      }
1061
      if ((NVIC_GetActive(EXTI9_5_IRQn) != 0 || NVIC_GetPendingIRQ(EXTI9_5_IRQn) != 0) &&
1062
          EXTI_GetFlagStatus(EXTI_Line5) == SET) {
1063
        wkup_rsn |= BL_WAKEUP_SEC_RSN_TOUCH;
1064
      }
1065
      if ((NVIC_GetActive(EXTI9_5_IRQn) != 0 || NVIC_GetPendingIRQ(EXTI9_5_IRQn) != 0) &&
1066
          EXTI_GetFlagStatus(EXTI_Line8) == SET) {
1067
        wkup_rsn |= BL_WAKEUP_SEC_RSN_PWRPLUG;
1068
      }
1069

    
1070
      /* since only the first interrupt will be handles, clear any pending ones */
1071
      NVIC_DisableIRQ(ADC_IRQn);
1072
      NVIC_DisableIRQ(EXTI3_IRQn);
1073
      NVIC_DisableIRQ(EXTI9_5_IRQn);
1074
      NVIC_ClearPendingIRQ(ADC_IRQn);
1075
      NVIC_ClearPendingIRQ(EXTI3_IRQn);
1076
      NVIC_ClearPendingIRQ(EXTI9_5_IRQn);
1077

    
1078
      /* clear all pending EXTI events */
1079
      EXTI_DeInit();
1080
      EXTI_ClearFlag(EXTI_Line3);
1081
      EXTI_ClearFlag(EXTI_Line5);
1082
      EXTI_ClearFlag(EXTI_Line8);
1083

    
1084
      /* make sure the LED was visibly turned off */
1085
      msleep(100);
1086

    
1087
      /* depending on the wakup reason, handle accordingly */
1088
      if (wkup_rsn & BL_WAKEUP_SEC_RSN_TOUCH) {
1089
        /* the system was interrupted via the TOUCH_INT_N signal */
1090

    
1091
        /* act as if this was a normal touch wakeup */
1092
        backup_reg.wakeup_pri_reason = BL_WAKEUP_PRI_RSN_WKUP;
1093
        backup_reg.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_TOUCH;
1094
        RTC_WriteBackupRegister(BL_RTC_BACKUP_REG, backup_reg.raw);
1095
        return handleTouchWakeup();
1096
      } else if (wkup_rsn & BL_WAKEUP_SEC_RSN_UART) {
1097
        /* the system was interrupted via the SYS_UARTDN signal */
1098

    
1099
        /* act as if this was a normal UART wakeup */
1100
        backup_reg.wakeup_pri_reason = BL_WAKEUP_PRI_RSN_WKUP;
1101
        backup_reg.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_UART;
1102
        RTC_WriteBackupRegister(BL_RTC_BACKUP_REG, backup_reg.raw);
1103
        return handleUartDnWakeup();
1104
      } else if (wkup_rsn & BL_WAKEUP_SEC_RSN_VSYSLOW) {
1105
        /* VSYS has dropped below 9V */
1106

    
1107
        /* depending on the original reason for shutdown, act differenty */
1108
        switch (backup_reg.shutdown_pri_reason) {
1109
          case BL_SHUTDOWN_PRI_RSN_HIBERNATE:
1110
          {
1111
            blCallbackShutdownHibernate();
1112
            return SUCCESS;
1113
          }
1114
          case BL_SHUTDOWN_PRI_RSN_DEEPSLEEP:
1115
          {
1116
            NVIC_SystemReset();
1117
            return SUCCESS;
1118
          }
1119
          default:
1120
            return ERROR;
1121
        }
1122
      } else if (wkup_rsn & BL_WAKEUP_SEC_RSN_PWRPLUG) {
1123
        /* system was interrupted because the power plug was removed
1124
         * note: when a power cord is plugged in, this will not trigger an interrupt because the NVIC is configured for a falling edge only */
1125
        if (backup_reg.shutdown_pri_reason == BL_SHUTDOWN_PRI_RSN_DEEPSLEEP) {
1126
          blCallbackShutdownDeepsleep();
1127
          return SUCCESS;
1128
        } else {
1129
          /* this state is undefined, because the PATH_DC inerrupt is only configured when the primary shutdown reason was to enter deepsleep mode */
1130
          return ERROR;
1131
        }
1132
      } else {
1133
        /* the system was interrupted for an unknown reason */
1134
        return ERROR;
1135
      }
1136
    } // end of ADC evaluation
1137
  } else {
1138
    /* since it is unknown why the IWDG was configured, act as after a cold reset */
1139
    return handleColdReset();
1140
  }
1141

    
1142
  return ERROR;
1143
} /*** end of handleIwdgWakeup ***/
1144

    
1145
/*
1146
 * Indicates the DiWheelDrive module to enter hibernate mode at wakeup.
1147
 * This function should be called quite at the beginning of the according handleXXXReset/Wakeup() methods.
1148
 */
1149
static void indicateHibernate() {
1150
  /* signal the DiWheelDrive to enter hibernate mode as well, so it will activate the charging pins */
1151
  GPIO_ResetBits(SYS_UART_DN_GPIO, SYS_UART_DN_PIN);
1152
  msleep(10); // this must be that long, because the DiWheelDrive sleeps some time before evaluating any signals
1153
  GPIO_SetBits(SYS_UART_DN_GPIO, SYS_UART_DN_PIN);
1154

    
1155
  /* if the DiWheeDrive needs some time for setup it may pull down the signal */
1156
  waitForSignal(SYS_UART_DN_GPIO, SYS_UART_DN_PIN, Bit_SET);
1157

    
1158
  return;
1159
} /*** end of indicateHibernate ***/
1160

    
1161
/*
1162
 *Performs a one-shot measurement of the VSYS voltage.
1163
 */
1164
static void AdcSingleMeasurement() {
1165
  /* reset and initialize ADC for single-shot measurement */
1166
//    ADC_DeInit();
1167
  setupADC(ADC1, 0, 0);
1168

    
1169
  /* initialize the NVIC so ADC interrupts are handled */
1170
  NVIC_InitTypeDef nvic;
1171
  nvic.NVIC_IRQChannel = ADC_IRQn;
1172
  nvic.NVIC_IRQChannelPreemptionPriority = 6;
1173
  nvic.NVIC_IRQChannelSubPriority = 6;
1174
  nvic.NVIC_IRQChannelCmd = ENABLE;
1175
  NVIC_Init(&nvic);
1176

    
1177
  /* measure the voltage once */
1178
  setLed(BLT_TRUE);
1179
  ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
1180
  ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
1181
  NVIC_EnableIRQ(ADC_IRQn);
1182
  ADC_SoftwareStartConv(ADC1);
1183
  while (ADC_GetITStatus(ADC1, ADC_IT_EOC) != SET && ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) != SET) {
1184
    __WFI();
1185
  }
1186
  NVIC_DisableIRQ(ADC_IRQn);
1187
  ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
1188
  ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
1189
  NVIC_ClearPendingIRQ(ADC_IRQn);
1190
  setLed(BLT_FALSE);
1191

    
1192
  return;
1193
} /*** end of AdcSingleMeasurement ***/
1194

    
1195
/*
1196
 * Configures the ADC for measuring VSYS.
1197
 * ADCx is the ADC object to initialize.
1198
 * low_th and high_th are the threshold values for the analog watchdor (must be 12-bit!).
1199
 * If low_th >= high_th, the ADC is configured for single-shot measurements.
1200
 * Otherwise, the watchdog is configured with the corresponding thresholds.
1201
 */
1202
ADC_TypeDef* setupADC(ADC_TypeDef* adc, const uint16_t low_th, const uint16_t high_th) {
1203
  /* evaluate the arguments */
1204
  blt_bool awd_enable = BLT_FALSE;
1205
  if (low_th < high_th) {
1206
    awd_enable = BLT_TRUE;
1207
  }
1208

    
1209
  /* enable the clock */
1210
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
1211

    
1212
  /* enable the ADC (wakes it from low-power mode) */
1213
  ADC_Cmd(adc, ENABLE);
1214

    
1215
  /* initialize the common registers */
1216
  ADC_CommonInitTypeDef adc_cinit;
1217
  ADC_CommonStructInit(&adc_cinit);
1218
  adc_cinit.ADC_Prescaler = ADC_Prescaler_Div8; // clock as slow as possible
1219
  ADC_CommonInit(&adc_cinit);
1220

    
1221
  /* initialize the ADC */
1222
  ADC_InitTypeDef adc_init;
1223
  ADC_StructInit(&adc_init);
1224
  adc_init.ADC_ContinuousConvMode = (awd_enable == BLT_TRUE) ? ENABLE : DISABLE;
1225
  ADC_Init(adc, &adc_init);
1226

    
1227
  /* disable internal sensors */
1228
  ADC_TempSensorVrefintCmd(DISABLE);
1229
  ADC_VBATCmd(DISABLE);
1230

    
1231
  /* configure ADC channel and speed */
1232
  ADC_RegularChannelConfig(adc, ADC_Channel_9, 1, ADC_SampleTime_480Cycles);
1233
  ADC_EOCOnEachRegularChannelCmd(adc, (awd_enable == BLT_TRUE) ? DISABLE : ENABLE);
1234
  ADC_DiscModeCmd(adc, DISABLE);
1235

    
1236
  /* disable DMA */
1237
  ADC_DMACmd(adc, DISABLE);
1238

    
1239
  /* disable injected mode */
1240
  ADC_AutoInjectedConvCmd(adc, DISABLE);
1241
  ADC_InjectedDiscModeCmd(adc, DISABLE);
1242

    
1243
  /* configure the analog watchdog */
1244
  if (awd_enable == BLT_TRUE) {
1245
    ADC_AnalogWatchdogSingleChannelConfig(adc, ADC_Channel_9);
1246
    ADC_AnalogWatchdogThresholdsConfig(adc, high_th, low_th);
1247
    ADC_AnalogWatchdogCmd(adc, ADC_AnalogWatchdog_SingleRegEnable);
1248
  } else {
1249
    ADC_AnalogWatchdogCmd(adc, ADC_AnalogWatchdog_None);
1250
  }
1251

    
1252
  /* configure the interrupts to be generated by the ADC */
1253
  ADC_ITConfig(adc, ADC_IT_EOC, (awd_enable == BLT_TRUE) ? DISABLE : ENABLE);
1254
  ADC_ITConfig(adc, ADC_IT_AWD, (awd_enable == BLT_TRUE) ? ENABLE : DISABLE);
1255
  ADC_ITConfig(adc, ADC_IT_JEOC, DISABLE);
1256
  ADC_ITConfig(adc, ADC_IT_OVR, DISABLE);
1257

    
1258
  return adc;
1259
}
1260

    
1261
/*
1262
 * Callback function that handles the system shutdown and enters transportation mode.
1263
 * When called from a multithreaded environment, it must be ensured that no other thread will preempt this function.
1264
 * In transportation low-power mode the system can only be woken up by pulling down the NRST signal.
1265
 * Furthermore, the system can not be charged when in transportation mode.
1266
 */
1267
void blCallbackShutdownTransportation(void) {
1268
  /* make sure that the required clocks are activated */
1269
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE);
1270
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
1271

    
1272
  /* set/keep the SYS_SYNC and SYS_PD signals active */
1273
  GPIO_ResetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1274
  GPIO_ResetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
1275

    
1276
  /* initialized the standalone timer */
1277
  saTimerInit();
1278

    
1279
  setLed(BLT_TRUE);
1280

    
1281
  /* wait for all boards to be ready for shutdown */
1282
  GPIO_SetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1283
  if (GPIO_ReadOutputDataBit(SYS_REG_EN_GPIO, SYS_REG_EN_PIN) == Bit_SET) {
1284
    // this must skipped if the pullup voltage (VIO3.3) is not active
1285
    setLed(BLT_TRUE);
1286
    waitForSignal(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN, Bit_SET);
1287
    setLed(BLT_FALSE);
1288
  }
1289

    
1290
  /* execute disambiguation procedure and signal all modules to enter transportation mode */
1291
  if (shutdownDisambiguationProcedure(BL_SHUTDOWN_PRI_RSN_TRANSPORT) != SUCCESS) {
1292
    blinkSOS(1);
1293
    msleep(10);
1294
  }
1295

    
1296
  shutdownToTransportation();
1297

    
1298
  return;
1299
} /*** end of blCallbackTransportation ***/
1300

    
1301
/*
1302
 * Callback function that handles the system shutdown and enters deepsleep mode.
1303
 * When called from a multithreaded environment, it must be ensured that no other thread will preempt this function.
1304
 * In deepsleep low-power mode the system can only be woken up via the NRST or the WKUP signal, or the RTC or IWDG, if configured.
1305
 * When a power plug is detected, the system will switch to hibernate mode, to provide charging capabilities (cf. handlePathDcWakeup()).
1306
 * As soon as the plug is removed again, however, the system will return to deppsleep mode (cf. handleIwdgWakeup()).
1307
 */
1308
void blCallbackShutdownDeepsleep(void) {
1309
  /* make sure that the required clocks are activated */
1310
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE);
1311
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
1312

    
1313
  /* set/keep the SYS_SYNC and SYS_PD signals active */
1314
  GPIO_ResetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1315
  GPIO_ResetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
1316

    
1317
  /* initialized the standalone timer */
1318
  saTimerInit();
1319

    
1320
  setLed(BLT_TRUE);
1321

    
1322
  /* wait for all boards to be ready for shutdown */
1323
  GPIO_SetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1324
  if (GPIO_ReadOutputDataBit(SYS_REG_EN_GPIO, SYS_REG_EN_PIN) == Bit_SET) {
1325
    // this must skipped if the pullup voltage (VIO3.3) is not active
1326
    setLed(BLT_TRUE);
1327
    waitForSignal(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN, Bit_SET);
1328
    setLed(BLT_FALSE);
1329
  }
1330

    
1331
  /* execute disambiguation procedure and signal all modules to enter deepsleep mode */
1332
  if (shutdownDisambiguationProcedure(BL_SHUTDOWN_PRI_RSN_DEEPSLEEP) != SUCCESS) {
1333
    blinkSOS(1);
1334
    msleep(10);
1335
  }
1336

    
1337
  shutdownToDeepsleep();
1338

    
1339
  return;
1340
} /*** end of blCallbackDeepsleep ***/
1341

    
1342
/*
1343
 * Callback function that handles the system shutdown and enters hibernate mode.
1344
 * When called from a multithreaded environment, it must be ensured that no other thread will preempt this function.
1345
 * Since this function actually just configures the system in a way, that it will enter hibernate mode after the next reset and rests it,
1346
 * see the handleSoftwareReset() function for more details about the hibernate low-power mode.
1347
 */
1348
void blCallbackShutdownHibernate(void) {
1349
  /* make sure that the required clocks are activated */
1350
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE);
1351
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
1352

    
1353
  /* set/keep the SYS_SYNC and SYS_PD signals active */
1354
  GPIO_ResetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1355
  GPIO_ResetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
1356

    
1357
  /* initialized the standalone timer */
1358
  saTimerInit();
1359

    
1360
  setLed(BLT_TRUE);
1361

    
1362
  /* wait for all boards to be ready for shutdown */
1363
  GPIO_SetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1364
  if (GPIO_ReadOutputDataBit(SYS_REG_EN_GPIO, SYS_REG_EN_PIN) == Bit_SET) {
1365
    // this must skipped if the pullup voltage (VIO3.3) is not active
1366
    setLed(BLT_TRUE);
1367
    waitForSignal(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN, Bit_SET);
1368
    setLed(BLT_FALSE);
1369
  }
1370

    
1371
  /* execute disambiguation procedure and signal all modules to enter hibernate mode */
1372
  if (shutdownDisambiguationProcedure(BL_SHUTDOWN_PRI_RSN_DEEPSLEEP) != SUCCESS) {
1373
    blinkSOS(1);
1374
    msleep(10);
1375
  }
1376

    
1377
  shutdownToHibernate();
1378

    
1379
  return;
1380
} /*** end of blCallbackShutdownHibernate ***/
1381

    
1382
/*
1383
 * Callback function that handles the system shutdown and initializes a restart.
1384
 * When called from a multithreaded environment, it must be ensured that no other thread will preempt this function.
1385
 * By configuration it is ensured, that the system will end up executing the handleSoftwareReset() function after reset.
1386
 */
1387
void blCallbackShutdownRestart(void) {
1388
  /* make sure that the required clocks are activated */
1389
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE);
1390
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
1391

    
1392
  /* set/keep the SYS_SYNC and SYS_PD signals active */
1393
  GPIO_ResetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1394
  GPIO_ResetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
1395

    
1396
  /* initialized the standalone timer */
1397
  saTimerInit();
1398

    
1399
  setLed(BLT_TRUE);
1400

    
1401
  /* deactivate SYS_PD_N and ensure that all modules had a chance to detect the falling edge */
1402
  msleep(1);
1403
  GPIO_SetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
1404
  msleep(1);
1405

    
1406
  /* wait for all boards to be ready for shutdown */
1407
  GPIO_SetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1408
  if (GPIO_ReadOutputDataBit(SYS_REG_EN_GPIO, SYS_REG_EN_PIN) == Bit_SET) {
1409
    // this must skipped if the pullup voltage (VIO3.3) is not active
1410
    setLed(BLT_TRUE);
1411
    waitForSignal(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN, Bit_SET);
1412
    setLed(BLT_FALSE);
1413
  }
1414

    
1415
  /* execute disambiguation procedure and signal all modules to restart normally */
1416
  if (shutdownDisambiguationProcedure(BL_SHUTDOWN_PRI_RSN_RESTART) != SUCCESS) {
1417
    blinkSOS(1);
1418
    msleep(10);
1419
  }
1420

    
1421
  /* restart the system */
1422
  shutdownAndRestart();
1423

    
1424
  return;
1425
} /*** end of blCallbackRestart ***/
1426

    
1427
/*
1428
 * Callback function that handles a system shutdown/restart request from another module.
1429
 * Depending on the result of the disambiguation procedure, the module will enter the according low-power mode or restart.
1430
 * When called from a multithreaded environment, it must be ensured that no other thread will preempt this function.
1431
 */
1432
void blCallbackHandleShutdownRequest(void) {
1433
  /* make sure that the required clocks are activated */
1434
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE);
1435
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
1436

    
1437
  /* set/keep the SYS_SYNC and SYS_PD signals active */
1438
  GPIO_ResetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1439
  GPIO_ResetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
1440

    
1441
  /* initialized the standalone timer */
1442
  saTimerInit();
1443

    
1444
  setLed(BLT_TRUE);
1445

    
1446
  /* deactivate SYS_PD_N and ensure that all modules had a chance to detect the falling edge */
1447
  msleep(1);
1448
  GPIO_SetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
1449
  msleep(1);
1450

    
1451
  /* wait for all boards to be ready for shutdown */
1452
  GPIO_SetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1453
  if (GPIO_ReadOutputDataBit(SYS_REG_EN_GPIO, SYS_REG_EN_PIN) == Bit_SET) {
1454
    // this must be skipped if the pullup voltage (VIO3.3) is not active
1455
    setLed(BLT_TRUE);
1456
    waitForSignal(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN, Bit_SET);
1457
    setLed(BLT_FALSE);
1458
  }
1459

    
1460
  /* check ths SYS_PD_N signal, whether the system shall shutdown or restart */
1461
  blt_bool shutdown_nrestart = (GPIO_ReadInputDataBit(SYS_PD_N_GPIO, SYS_PD_N_PIN) == Bit_RESET) ? BLT_TRUE : BLT_FALSE;
1462

    
1463
  /* disambiguation procedure (passive) */
1464
  uint32_t pulse_counter = 0;
1465
  while (waitForSignalTimeout(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN, Bit_RESET, 10)) {
1466
    waitForSignal(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN, Bit_SET);
1467
    ++pulse_counter;
1468
  }
1469

    
1470
  /* evaluate and hanlde disambiguation result */
1471
  if (shutdown_nrestart == BLT_TRUE) {
1472
    /* shutdown request */
1473

    
1474
    /* handle special cases */
1475
    if (pulse_counter == BL_SHUTDOWN_PRI_RSN_UNKNOWN) {
1476
      /* no pulse at all was received */
1477
      pulse_counter = BL_SHUTDOWN_PRI_RSN_DEFAULT;
1478
    } else if (pulse_counter != BL_SHUTDOWN_PRI_RSN_HIBERNATE &&
1479
               pulse_counter != BL_SHUTDOWN_PRI_RSN_DEEPSLEEP &&
1480
               pulse_counter != BL_SHUTDOWN_PRI_RSN_TRANSPORT) {
1481
      /* invalid number of pulses received */
1482
      blinkSOS(1);
1483
      pulse_counter = BL_SHUTDOWN_PRI_RSN_DEFAULT;
1484
    }
1485

    
1486
    switch (pulse_counter) {
1487
      case BL_SHUTDOWN_PRI_RSN_HIBERNATE:
1488
        shutdownToHibernate();
1489
        break;
1490
      case BL_SHUTDOWN_PRI_RSN_DEEPSLEEP:
1491
        shutdownToDeepsleep();
1492
        break;
1493
      case BL_SHUTDOWN_PRI_RSN_TRANSPORT:
1494
        shutdownToTransportation();
1495
        break;
1496
    }
1497
  } else {
1498
    /* restart request */
1499

    
1500
    /* there is no ambiguity for restart, so it is ignored */
1501
    shutdownAndRestart();
1502
  }
1503

    
1504
  /* if this code is reached, the system did neither shut down, nor restart.
1505
   * This must never be the case!
1506
   */
1507
  blinkSOSinf();
1508
  return;
1509
} /*** end of blCallbackHandleShutdownRequest ***/
1510

    
1511
/*********************************** end of main.c *************************************/