Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (57.195 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(void);
55
static void initExti(void);
56
void configGpioForShutdown(void);
57
void systemPowerDown(void);
58

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

    
66
static void indicateHibernate(void);
67
static void AdcSingleMeasurement(void);
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(void);
74
void shutdownToDeepsleep(void);
75
void shutdownToHibernate(void);
76
void shutdownAndRestart(void);
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_Release, BL_VERSION_MAJOR, BL_VERSION_MINOR, 0},
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
  /*
170
   * Measure the current voltage of VSYS and enable the chargers if it was found to be > 9V.
171
   */
172
  AdcSingleMeasurement();
173
  if ( (((float)(ADC_GetConversionValue(ADC1)) / (float)0x0FFF) * 3.3f * 5.33f) > 9.0f ) {
174
    /* VSYS was found to be > 9V */
175
    setLed(BLT_TRUE);
176
    GPIO_ResetBits(CHARGE_EN1_N_GPIO, CHARGE_EN1_N_PIN);
177
    GPIO_ResetBits(CHARGE_EN2_N_GPIO, CHARGE_EN2_N_PIN);
178
  }
179

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

    
220
  /* if something went wrong, signal this failure */
221
  if (status != SUCCESS) {
222
    blinkSOSinf();
223
  }
224

    
225
  return;
226
} /*** end of main ***/
227

    
228

    
229
/************************************************************************************//**
230
** \brief     Initializes the microcontroller.
231
** \return    none.
232
**
233
****************************************************************************************/
234
static void Init(void)
235
{
236
#if (BOOT_COM_UART_ENABLE > 0 || BOOT_GATE_UART_ENABLE > 0)
237
  GPIO_InitTypeDef  GPIO_InitStructure;
238
#elif (BOOT_FILE_SYS_ENABLE > 0)
239
  GPIO_InitTypeDef  GPIO_InitStructure;
240
  USART_InitTypeDef USART_InitStructure;
241
#elif (BOOT_COM_CAN_ENABLE > 0 || BOOT_GATE_CAN_ENABLE > 0)
242
  GPIO_InitTypeDef  GPIO_InitStructure;
243
#endif
244

    
245
  /* initialize the system and its clocks */
246
  SystemInit();
247
#if (BOOT_COM_UART_ENABLE > 0 || BOOT_GATE_UART_ENABLE > 0)
248
  /* enable UART peripheral clock */
249
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
250
  /* enable GPIO peripheral clock for transmitter and receiver pins */
251
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
252
  /* connect the pin to the peripherals alternate function */
253
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
254
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
255
  /* configure USART Tx as alternate function  */
256
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
257
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
258
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
259
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
260
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
261
  GPIO_Init(GPIOA, &GPIO_InitStructure);
262
  /* configure USART Rx as alternate function */
263
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
264
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
265
  GPIO_Init(GPIOA, &GPIO_InitStructure);
266
#endif
267

    
268
#if (BOOT_COM_BLUETOOTH_UART_ENABLE > 0)
269
  /* enable UART peripheral clock */
270
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
271

    
272
  /* enable GPIO peripheral clock for transmitter and receiver pins */
273
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
274
  /* connect the pin to the peripherals alternate function */
275
  GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_USART3);
276
  GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_USART3);
277
  /* configure USART Tx as alternate function  */
278
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
279
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
280
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
281
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
282
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
283
  GPIO_Init(GPIOC, &GPIO_InitStructure);
284
  /* configure USART Rx as alternate function */
285
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
286
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
287
  GPIO_Init(GPIOC, &GPIO_InitStructure);
288

    
289
  /* Configure Bluetooth reset pin */
290
  GPIO_InitTypeDef  gpio_init;
291
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
292
  gpio_init.GPIO_Pin   = BT_RST_PIN;
293
  gpio_init.GPIO_OType = GPIO_OType_OD;
294
  gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL;
295
  gpio_init.GPIO_Mode = GPIO_Mode_OUT;
296
  gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
297
  GPIO_Init(BT_RST_GPIO, &gpio_init);
298
  /* Reset Bluetooth reset pin */
299
  GPIO_ResetBits(BT_RST_GPIO, BT_RST_PIN);
300
#endif
301

    
302

    
303
#if (BOOT_COM_CAN_ENABLE > 0 || BOOT_GATE_CAN_ENABLE > 0)
304
  /* enable clocks for CAN transmitter and receiver pins */
305
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
306
  /* select alternate function for the CAN pins */
307
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource11, GPIO_AF_CAN1);
308
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource12, GPIO_AF_CAN1);
309
  /* configure CAN RX and TX pins */
310
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
311
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
312
  GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
313
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
314
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
315
  GPIO_Init(GPIOA, &GPIO_InitStructure);
316
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
317
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
318
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
319
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
320
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
321
  GPIO_Init(GPIOA, &GPIO_InitStructure);
322
#endif
323
} /*** end of Init ***/
324

    
325
/*
326
 * Initializes all GPIO used by the bootloader
327
 */
328
static void initGpio(void) {
329
  GPIO_InitTypeDef gpio_init;
330

    
331
  /*
332
   * OUTPUTS
333
   */
334

    
335
  /* initialize LED and push it up (inactive) */
336
  GPIO_SetBits(LED_GPIO, LED_PIN);
337
  gpio_init.GPIO_Pin    = LED_PIN;
338
  gpio_init.GPIO_Mode   = GPIO_Mode_OUT;
339
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
340
  gpio_init.GPIO_OType  = GPIO_OType_PP;
341
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
342
  GPIO_Init(LED_GPIO, &gpio_init);
343

    
344
  /* initialize SYS_PD_N and push it up (inactive) */
345
  GPIO_SetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
346
  gpio_init.GPIO_Pin    = SYS_PD_N_PIN;
347
  gpio_init.GPIO_Mode   = GPIO_Mode_OUT;
348
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
349
  gpio_init.GPIO_OType  = GPIO_OType_OD;
350
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
351
  GPIO_Init(SYS_PD_N_GPIO, &gpio_init);
352

    
353
  /* initialize SYS_SYNC_N and pull it down (active) */
354
  GPIO_ResetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
355
  gpio_init.GPIO_Pin    = SYS_SYNC_N_PIN;
356
  gpio_init.GPIO_Mode   = GPIO_Mode_OUT;
357
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
358
  gpio_init.GPIO_OType  = GPIO_OType_OD;
359
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
360
  GPIO_Init(SYS_SYNC_N_GPIO, &gpio_init);
361

    
362
  /* initialize SYS_WARMRST_N and pull it down (active) */
363
  GPIO_ResetBits(SYS_WARMRST_N_GPIO, SYS_WARMRST_N_PIN);
364
  gpio_init.GPIO_Pin    = SYS_WARMRST_N_PIN;
365
  gpio_init.GPIO_Mode   = GPIO_Mode_OUT;
366
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
367
  gpio_init.GPIO_OType  = GPIO_OType_OD;
368
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
369
  GPIO_Init(SYS_WARMRST_N_GPIO, &gpio_init);
370

    
371
  /* initialize SYS_UART_DN and push it up (inactive) */
372
  GPIO_SetBits(SYS_UART_DN_GPIO, SYS_UART_DN_PIN);
373
  gpio_init.GPIO_Pin    = SYS_UART_DN_PIN;
374
  gpio_init.GPIO_Mode   = GPIO_Mode_OUT;
375
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
376
  gpio_init.GPIO_OType  = GPIO_OType_OD;
377
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
378
  GPIO_Init(SYS_UART_DN_GPIO, &gpio_init);
379

    
380
  /* initialize POWER_EN and pull it down (inactive) */
381
  GPIO_ResetBits(POWER_EN_GPIO, POWER_EN_PIN);
382
  gpio_init.GPIO_Pin    = POWER_EN_PIN;
383
  gpio_init.GPIO_Mode   = GPIO_Mode_OUT;
384
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
385
  gpio_init.GPIO_OType  = GPIO_OType_PP;
386
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
387
  GPIO_Init(POWER_EN_GPIO, &gpio_init);
388

    
389
  /* initialize SYS_REG_EN and pull it down (inactive) */
390
  GPIO_ResetBits(SYS_REG_EN_GPIO, SYS_REG_EN_PIN);
391
  gpio_init.GPIO_Pin    = SYS_REG_EN_PIN;
392
  gpio_init.GPIO_Mode   = GPIO_Mode_OUT;
393
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
394
  gpio_init.GPIO_OType  = GPIO_OType_PP;
395
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
396
  GPIO_Init(SYS_REG_EN_GPIO, &gpio_init);
397

    
398
  /* initialize CHARGE_EN1_N and CHARGE_EN2_N and push them up (inactive) */
399
  GPIO_SetBits(CHARGE_EN1_N_GPIO, CHARGE_EN1_N_PIN);
400
  GPIO_SetBits(CHARGE_EN2_N_GPIO, CHARGE_EN2_N_PIN);
401
  gpio_init.GPIO_Pin    = CHARGE_EN1_N_PIN;
402
  gpio_init.GPIO_Mode   = GPIO_Mode_OUT;
403
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
404
  gpio_init.GPIO_OType  = GPIO_OType_PP;
405
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
406
  GPIO_Init(CHARGE_EN1_N_GPIO, &gpio_init);
407
  gpio_init.GPIO_Pin    = CHARGE_EN2_N_PIN;
408
  GPIO_Init(CHARGE_EN2_N_GPIO, &gpio_init);
409

    
410
  /*
411
   * INPUTS
412
   */
413

    
414
  /* initialize PATH_DC */
415
  gpio_init.GPIO_Pin    = PATH_DC_PIN;
416
  gpio_init.GPIO_Mode   = GPIO_Mode_IN;
417
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
418
  gpio_init.GPIO_OType  = GPIO_OType_PP;
419
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
420
  GPIO_Init(PATH_DC_GPIO, &gpio_init);
421

    
422
  /* initialize TOUCH_INT_N */
423
  gpio_init.GPIO_Pin    = TOUCH_INT_N_PIN;
424
  gpio_init.GPIO_Mode   = GPIO_Mode_IN;
425
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
426
  gpio_init.GPIO_OType  = GPIO_OType_PP;
427
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
428
  GPIO_Init(TOUCH_INT_N_GPIO, &gpio_init);
429

    
430
  /* initialize VSYS_SENSE as analog input */
431
  gpio_init.GPIO_Pin    = VSYS_SENSE_PIN;
432
  gpio_init.GPIO_Mode   = GPIO_Mode_AN;
433
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
434
  gpio_init.GPIO_OType  = GPIO_OType_PP;
435
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
436
  GPIO_Init(VSYS_SENSE_GPIO, &gpio_init);
437

    
438
  /* initialize GPIOB4, since it is configured in alternate function mode on reset */
439
  gpio_init.GPIO_Pin    = CHARGE_STAT2A_PIN;
440
  gpio_init.GPIO_Mode   = GPIO_Mode_IN;
441
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
442
  gpio_init.GPIO_OType  = GPIO_OType_PP;
443
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
444
  GPIO_Init(CHARGE_STAT2A_GPIO, &gpio_init);
445

    
446
  return;
447
} /*** end of initGpio ***/
448

    
449
/*
450
 * Initialize all EXTI lines
451
 */
452
static void initExti(void) {
453
  /* configure EXTI lines */
454
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource0); // IR_INT1_N
455
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource0); // CHARGE_STAT1A
456
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource1); // GAUGE_BATLOW1
457
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource2); // GAUGE_BATGD1_N
458
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource3); // SYS_UART_DN
459
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource4); // CHARGE_STAT2A
460
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource4); // IR_INT2_N
461
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource5); // TOUCH_INT_N
462
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource6); // GAUGE_BATLOW2
463
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource7); // GAUGE_BATGD2_N
464
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource8); // PATH_DC
465
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource9); // SYS_SPI_DIR
466
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource12); // SYS_SYNC_N
467
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource13); // SYS_PD_N
468
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource14); // SYS_WARMRST_N
469
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource15); // SYS_UART_UP
470

    
471
  return;
472
} /*** end of initExti ***/
473

    
474
/*
475
 * Signals, which type of low-power mode the system shall enter after the shutdown sequence.
476
 */
477
ErrorStatus shutdownDisambiguationProcedure(const uint8_t type) {
478
  GPIO_SetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
479
  ErrorStatus ret_val = ERROR;
480

    
481
  switch (type) {
482
    case BL_SHUTDOWN_PRI_RSN_UNKNOWN:
483
    case BL_SHUTDOWN_PRI_RSN_HIBERNATE:
484
    case BL_SHUTDOWN_PRI_RSN_DEEPSLEEP:
485
    case BL_SHUTDOWN_PRI_RSN_TRANSPORT:
486
    {
487
      // broadcast a number of pulses, depending on the argument
488
      uint8_t pulse_counter = 0;
489
      for (pulse_counter = 0; pulse_counter < type; ++pulse_counter) {
490
        msleep(1);
491
        GPIO_ResetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
492
        msleep(1);
493
        GPIO_SetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
494
      }
495
      // wait for timeout
496
      msleep(10);
497
      ret_val = SUCCESS;
498
      break;
499
    }
500
    case BL_SHUTDOWN_PRI_RSN_RESTART:
501
    {
502
      // since there is no ambiguity for restart requests, no pulses are generated
503
      msleep(10);
504
      ret_val = SUCCESS;
505
      break;
506
    }
507
    default:
508
      ret_val = ERROR;
509
      break;
510
  }
511

    
512
  return ret_val;
513
} /*** end of shutdownDisambiguationProcedure ***/
514

    
515
/*
516
 * Final shutdown of the system to enter transportation mode.
517
 */
518
void shutdownToTransportation(void) {
519
  /* configure some criticpal GPIOs as input
520
   * This is required, because otherwise some hardware might be powered through these signals */
521
  configGpioForShutdown();
522

    
523
  /* power down the system */
524
  systemPowerDown();
525

    
526
  /* deactivate the WKUP pin */
527
  PWR_WakeUpPinCmd(DISABLE);
528

    
529
  /* deactivate any RTC related events */
530
  RTC_WakeUpCmd(DISABLE);
531
  RTC_TamperCmd(RTC_Tamper_1, DISABLE);
532
  RTC_TimeStampCmd(RTC_TimeStampEdge_Rising, DISABLE);
533
  RTC_TimeStampCmd(RTC_TimeStampEdge_Falling, DISABLE);
534
  RTC_ClearFlag(~0);
535

    
536
  /* disable the IWDG */
537
  IWDG_ReloadCounter();
538

    
539
  /* write some information to the backup register */
540
  blBackupRegister_t backup;
541
  backup.shutdown_pri_reason = BL_SHUTDOWN_PRI_RSN_TRANSPORT;
542
  backup.shutdown_sec_reason = BL_SHUTDOWN_SEC_RSN_UNKNOWN;
543
  backup.wakeup_pri_reason = BL_WAKEUP_PRI_RSN_UNKNOWN;
544
  backup.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_UNKNOWN;
545
  PWR_BackupAccessCmd(ENABLE);
546
  RTC_WriteBackupRegister(BL_RTC_BACKUP_REG, backup.raw);
547

    
548
  /* morse 'OK' via the LED to signal that shutdown was successful */
549
  blinkOK(1);
550

    
551
  /* enter standby mode */
552
  PWR_EnterSTANDBYMode();
553

    
554
  return;
555
} /*** end of shutdownToTransportation ***/
556

    
557
/*
558
 * Final shutdown of the system to enter deepsleep mode.
559
 */
560
void shutdownToDeepsleep(void) {
561
  /* configure some criticpal GPIOs as input
562
   * This is required, because otherwise some hardware might be powered through these signals */
563
  configGpioForShutdown();
564

    
565
  /* power down the system */
566
  systemPowerDown();
567

    
568
  /* activate the WKUP pin */
569
  PWR_WakeUpPinCmd(ENABLE);
570

    
571
  /*
572
   * Configuration of RTC and IWDG belongs to the OS.
573
   */
574

    
575
  /* write some information to the backup register */
576
  blBackupRegister_t backup;
577
  backup.shutdown_pri_reason = BL_SHUTDOWN_PRI_RSN_DEEPSLEEP;
578
  backup.shutdown_sec_reason = BL_SHUTDOWN_SEC_RSN_UNKNOWN;
579
  backup.wakeup_pri_reason = BL_WAKEUP_PRI_RSN_UNKNOWN;
580
  backup.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_UNKNOWN;
581
  PWR_BackupAccessCmd(ENABLE);
582
  RTC_WriteBackupRegister(BL_RTC_BACKUP_REG, backup.raw);
583

    
584
  /* morse 'OK' via the LED to signal that shutdown was successful */
585
  blinkOK(1);
586

    
587
  /* enter standby mode or restart the system in case a power plug is already present */
588
  if (GPIO_ReadInputDataBit(PATH_DC_GPIO, PATH_DC_PIN) != Bit_SET) {
589
    PWR_EnterSTANDBYMode();
590
  } else {
591
    NVIC_SystemReset();
592
  }
593

    
594
  return;
595
} /*** end of shutdownToDeepsleep ***/
596

    
597
/*
598
 * Final shutdown of the system to enter hibernate mode.
599
 */
600
void shutdownToHibernate(void) {
601
  /* configure some criticpal GPIOs as input
602
   * This is required, because otherwise some hardware might be powered through these signals */
603
  configGpioForShutdown();
604

    
605
  /* power down the system */
606
  systemPowerDown();
607

    
608
  /* write some information to the backup register */
609
  blBackupRegister_t backup;
610
  backup.shutdown_pri_reason = BL_SHUTDOWN_PRI_RSN_HIBERNATE;
611
  backup.shutdown_sec_reason = BL_SHUTDOWN_SEC_RSN_UNKNOWN;
612
  backup.wakeup_pri_reason = BL_WAKEUP_PRI_RSN_UNKNOWN;
613
  backup.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_UNKNOWN;
614
  PWR_BackupAccessCmd(ENABLE);
615
  RTC_WriteBackupRegister(BL_RTC_BACKUP_REG, backup.raw);
616

    
617
  /* morse 'OK' via the LED to signal that shutdown was successful */
618
  blinkOK(1);
619

    
620
  /* reset the MCU */
621
  NVIC_SystemReset();
622

    
623
  return;
624
} /*** end of shutdownToHibernate ***/
625

    
626
/*
627
 * Final shutdown of the system and restart.
628
 */
629
void shutdownAndRestart(void) {
630
  /* configure some criticpal GPIOs as input
631
   * This is required, because otherwise some hardware might be powered through these signals */
632
  configGpioForShutdown();
633

    
634
  /* power down the system */
635
  systemPowerDown();
636

    
637
  /* write some information to the backup register */
638
  blBackupRegister_t backup;
639
  backup.shutdown_pri_reason = BL_SHUTDOWN_PRI_RSN_RESTART;
640
  backup.shutdown_sec_reason = BL_SHUTDOWN_SEC_RSN_UNKNOWN;
641
  backup.wakeup_pri_reason = BL_WAKEUP_PRI_RSN_UNKNOWN;
642
  backup.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_UNKNOWN;
643
  PWR_BackupAccessCmd(ENABLE);
644
  RTC_WriteBackupRegister(BL_RTC_BACKUP_REG, backup.raw);
645

    
646
  /* morse 'OK' via the LED to signal that shutdown was successful */
647
  blinkOK(1);
648

    
649
  /* reset the MCU */
650
  NVIC_SystemReset();
651

    
652
  return;
653
} /*** end of shutdownAndRestart ***/
654

    
655
/*
656
 * Configures some GPIO pins as inputs for safety reasons.
657
 * Under certain circumstances, these pins might power hardware that is supposed to be shut down.
658
 */
659
void configGpioForShutdown(void) {
660
  /* setup the configuration */
661
  GPIO_InitTypeDef gpio_init;
662
  gpio_init.GPIO_Mode   = GPIO_Mode_IN;
663
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
664
  gpio_init.GPIO_OType  = GPIO_OType_PP;
665
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
666

    
667
  /* configure SYS_UART_TX */
668
  gpio_init.GPIO_Pin = SYS_UART_TX_PIN;
669
  GPIO_Init(SYS_UART_TX_GPIO, &gpio_init);
670

    
671
  /* configure all SYS_SPI signals */
672
  gpio_init.GPIO_Pin = SYS_SPI_SS0_N_PIN;
673
  GPIO_Init(SYS_SPI_SS0_N_GPIO, &gpio_init);
674
  gpio_init.GPIO_Pin = SYS_SPI_SCLK_PIN;
675
  GPIO_Init(SYS_SPI_SCLK_GPIO, &gpio_init);
676
  gpio_init.GPIO_Pin = SYS_SPI_MISO_PIN;
677
  GPIO_Init(SYS_SPI_MISO_GPIO, &gpio_init);
678
  gpio_init.GPIO_Pin = SYS_SPI_MOSI_PIN;
679
  GPIO_Init(SYS_SPI_MOSI_GPIO, &gpio_init);
680
  gpio_init.GPIO_Pin = SYS_SPI_SS1_N_PIN;
681
  GPIO_Init(SYS_SPI_SS1_N_GPIO, &gpio_init);
682
  gpio_init.GPIO_Pin = SYS_SPI_DIR_PIN;
683
  GPIO_Init(SYS_SPI_DIR_GPIO, &gpio_init);
684

    
685
  /* configure CAN_TX */
686
  gpio_init.GPIO_Pin = CAN_TX_PIN;
687
  GPIO_Init(CAN_TX_GPIO, &gpio_init);
688

    
689
  /* configure all Bluetooth signals */
690
  gpio_init.GPIO_Pin = BT_CTS_PIN;
691
  GPIO_Init(BT_CTS_GPIO, &gpio_init);
692
  gpio_init.GPIO_Pin = BT_RX_PIN;
693
  GPIO_Init(BT_RX_GPIO, &gpio_init);
694

    
695
  return;
696
} /*** end of configGpioForShutdown ***/
697

    
698
/*
699
 * Disables all regulated voltages and finally cuts power to the rest of the system.
700
 */
701
void systemPowerDown(void) {
702
  setLed(BLT_TRUE);
703

    
704
  /* make sure that all other modules are shut down */
705
  msleep(10);
706

    
707
  /* reset slave modules */
708
  GPIO_ResetBits(SYS_WARMRST_N_GPIO, SYS_WARMRST_N_PIN);
709

    
710
  /* disable voltage regulators */
711
  GPIO_ResetBits(SYS_REG_EN_GPIO, SYS_REG_EN_PIN);
712

    
713
  /* cut power */
714
  GPIO_ResetBits(POWER_EN_GPIO, POWER_EN_PIN);
715

    
716
  /* make sure, all capacitors are discharged */
717
  msleep(100);
718

    
719
  setLed(BLT_FALSE);
720

    
721
  return;
722
} /*** end of systemPowerDown ***/
723

    
724
/*
725
 * Cofigures the independent watchdog (IWDG) to fire after the specified time when it is enabled.
726
 * The argument is the requested time in milliseconds.
727
 * The time that was actually set for the IWDG is returned by the function (again in milliseconds).
728
 * In some cases the returned value might differ from the requested one, but if so, it will alwyas be smaller.
729
 * Although the IWDG provides higher resolutions than milliseconds, these are not supported by this function.
730
 */
731
uint16_t configIwdg(const uint16_t ms) {
732
  /* apply an upper bound to the ms argument */
733
  uint16_t ms_capped = (ms >= 0x8000) ? 0x7FFF : ms;
734

    
735
  /* detect the best fitting prescaler and compute the according reload value */
736
  uint8_t prescaler = 0;
737
  uint16_t reload_val = 0;
738
  if (ms_capped >= 0x4000) {
739
    prescaler = IWDG_Prescaler_256;
740
    reload_val = ms_capped >> 3;  // note: this corresponds to a floor function
741
    ms_capped = reload_val << 3;  // this applies the floor function to ms_capped
742
  } else if (ms_capped >= 0x2000) {
743
    prescaler = IWDG_Prescaler_128;
744
    reload_val = ms_capped >> 2;  // note: this corresponds to a floor function
745
    ms_capped = reload_val << 2;  // this applies the floor function to ms_capped
746
  } else if (ms_capped >= 0x1000) {
747
    ms_capped &= ~(0x0001);
748
    prescaler = IWDG_Prescaler_64;
749
    reload_val = ms_capped >> 1;  // note: this corresponds to a floor function
750
    ms_capped = reload_val << 1;  // this applies the floor function to ms_capped
751
  } else {
752
    prescaler = IWDG_Prescaler_32;
753
    reload_val = ms_capped;
754
  }
755

    
756
  /* configure the IWDG */
757
  if (reload_val > 0) {
758
    IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
759
    IWDG_SetPrescaler(prescaler);
760
    IWDG_SetReload(reload_val);
761
    IWDG_WriteAccessCmd(IWDG_WriteAccess_Disable);
762
  }
763

    
764
  return ms_capped;
765
} /*** end of configIWDG ***/
766

    
767
/*
768
 * System was reset via the NRST pin or the reason could not be detected.
769
 * In this case, everything is started up.
770
 * If an attempt for an OS update is detected, flashing mode is entered.
771
 * Otherwise, the system will boot the OS.
772
 */
773
ErrorStatus handleColdReset(void) {
774
  /* activate system power and wait some time to ensure stable voltages */
775
  setLed(BLT_TRUE);
776
  GPIO_SetBits(POWER_EN_GPIO, POWER_EN_PIN);
777
  msleep(10);
778
  GPIO_SetBits(SYS_REG_EN_GPIO, SYS_REG_EN_PIN);
779
  msleep(10);
780
  setLed(BLT_FALSE);
781

    
782
  /* drive SYS_WARMRST_N high (inactive) */
783
  GPIO_SetBits(SYS_WARMRST_N_GPIO, SYS_WARMRST_N_PIN);
784

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

    
789
  /* wait 1ms to make sure that all modules are running and started the bootloader */
790
  msleep(1);
791

    
792
  /* initialize the bootloader */
793
  BootInit();
794

    
795
  /* start the infinite program loop */
796
  uint32_t loopStartTime = 0;
797
  saTimerUpdate(&loopStartTime);
798
  uint32_t currentTime = loopStartTime;
799
  while (1)
800
  {
801
//    /* make the LED "double-blink" */
802
//    saTimerUpdate(&currentTime);
803
//    if (currentTime < loopStartTime + 50) {
804
//      setLed(BLT_TRUE);
805
//    } else if (currentTime < loopStartTime + 50+100) {
806
//      setLed(BLT_FALSE);
807
//    } else if (currentTime < loopStartTime + 50+100+50) {
808
//      setLed(BLT_TRUE);
809
//    } else if (currentTime < loopStartTime + 50+100+50+300) {
810
//      setLed(BLT_FALSE);
811
//    } else {
812
//      loopStartTime = currentTime;
813
//    }
814

    
815
    /* run the bootloader task */
816
    BootTask();
817

    
818
    /* check the SYS_PD_N signal */
819
    if (GPIO_ReadInputDataBit(SYS_PD_N_GPIO, SYS_PD_N_PIN) == Bit_RESET) {
820
      blCallbackHandleShutdownRequest();
821
      return SUCCESS;
822
    }
823
  }
824

    
825
  return ERROR;
826
} /*** end of handleColdReset ***/
827

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

    
848
      /* deactivate any RTC related events */
849
      RTC_WakeUpCmd(DISABLE);
850
      RTC_TamperCmd(RTC_Tamper_1, DISABLE);
851
      RTC_TimeStampCmd(RTC_TimeStampEdge_Rising, DISABLE);
852
      RTC_TimeStampCmd(RTC_TimeStampEdge_Falling, DISABLE);
853

    
854
      /* configure the IWDG to wake the system from standby mode */
855
      uint16_t iwdg_ms = 1;
856
      if (GPIO_ReadInputDataBit(PATH_DC_GPIO, PATH_DC_PIN) != Bit_SET) {
857
        /* if a power plug is detected, fire immediately (1ms), else fire after the defined hibernate time */
858
        iwdg_ms = HIBERNATE_TIME_MS;
859
      }
860
      configIwdg(iwdg_ms);
861
      IWDG_Enable();
862

    
863
      /* enter standby mode */
864
      PWR_EnterSTANDBYMode();
865

    
866
      return SUCCESS;
867
      break;
868
    }
869
    case BL_SHUTDOWN_PRI_RSN_RESTART:
870
    {
871
      return handleColdReset();
872
      break;
873
    }
874
    case BL_SHUTDOWN_PRI_RSN_DEEPSLEEP:
875
    {
876
      if (GPIO_ReadInputDataBit(PATH_DC_GPIO, PATH_DC_PIN) == Bit_SET) {
877
        return handlePathDcWakeup();
878
      } else {
879
        blCallbackShutdownDeepsleep();
880
      }
881
      break;
882
    }
883
    default:
884
      return ERROR;
885
  }
886
  return ERROR;
887
} /*** end of handleSoftwareReset ***/
888

    
889
/*
890
 * System was woken up via the WKUP pin and the SYS_UART_DN signal was found to be responsible.
891
 * In this case, the system starts as after a cold reset.
892
 * this function is identical to handleTouchWakeup().
893
 */
894
ErrorStatus handleUartDnWakeup(void) {
895
  return handleColdReset();
896
} /*** end of hanldeUartDnWakeup ***/
897

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

    
923
      return handleIwdgWakeup();
924
      break;
925
    case BL_SHUTDOWN_PRI_RSN_TRANSPORT:
926
      blCallbackShutdownTransportation();
927
      return SUCCESS;
928
      break;
929
    default:
930
      return ERROR;
931
      break;
932
  }
933

    
934
  return ERROR;
935
} /*** end of handlePathDcWakeup ***/
936

    
937
/*
938
 * System was woken up via the WKUP pin and the touch sensors were found to be responsible.
939
 * In this case the system starts as after an cold reset.
940
 * This function is identical to handleUartDnWakeup().
941
 */
942
ErrorStatus handleTouchWakeup(void) {
943
  return handleColdReset();
944
} /*** end of handleTouchWakeup ***/
945

    
946
/*
947
 * System was woken up via the IWDG.
948
 * In this case the ADC is configured and VSYS is measured once.
949
 * 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.
950
 * Otherwise, the system will configure the IWDG to wake the system again after five seconds and enter standby mode.
951
 */
952
ErrorStatus handleIwdgWakeup(void) {
953
  /* handle different situations, depending on the backup data */
954
  if ((backup_reg.shutdown_pri_reason == BL_SHUTDOWN_PRI_RSN_HIBERNATE) ||
955
      (backup_reg.shutdown_pri_reason == BL_SHUTDOWN_PRI_RSN_DEEPSLEEP)) {
956
    /* handle periodic wakeup in hibernate mode and in deepsleep mode when a power plug was detetced */
957

    
958
    /* if in hibernate mode, indicate the DiWheelDrive to enter hibernate mode as well, so it will activate the charging pins */
959
    if (backup_reg.shutdown_pri_reason == BL_SHUTDOWN_PRI_RSN_HIBERNATE) {
960
      indicateHibernate();
961
    }
962

    
963
    /* measure the current voltage of VSYS */
964
    AdcSingleMeasurement();
965

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

    
975
      /* re-enter power saving mode
976
       * If the system was shut down to deepsleep mode and the power plug was removed, re-enter deepsleep mode.
977
       * (This could be done earlier in this function, but since charging via the pins of the DeWheelDrive may be
978
       *  supported in the future, this is done after measuring VSYS)
979
       */
980
      if (backup_reg.shutdown_pri_reason == BL_SHUTDOWN_PRI_RSN_DEEPSLEEP &&
981
          GPIO_ReadInputDataBit(PATH_DC_GPIO, PATH_DC_PIN) == Bit_RESET) {
982
        blCallbackShutdownDeepsleep();
983
      } else {
984
        /* reconfigure the IWDG and power down for five seconds */
985
        configIwdg(HIBERNATE_TIME_MS);
986
        IWDG_Enable();
987

    
988
        /* enter standby mode */
989
        PWR_EnterSTANDBYMode();
990
      }
991

    
992
      return SUCCESS;
993
    } else {
994
      /* VSYS was found to be >= 9V */
995
      setLed(BLT_TRUE);
996

    
997
      /* charge the battieries */
998
      GPIO_ResetBits(CHARGE_EN1_N_GPIO, CHARGE_EN1_N_PIN);
999
      GPIO_ResetBits(CHARGE_EN2_N_GPIO, CHARGE_EN2_N_PIN);
1000

    
1001
      /* configure analog watchdoch to fire as soon as the voltage drops below 9V */
1002
      ADC_DeInit();
1003
      setupADC(ADC1, (uint16_t)(9.0f / 5.33f / 3.3f * (float)0x0FFF), 0x0FFF);
1004

    
1005
      EXTI_InitTypeDef exti;
1006
      /* configure UART_DN EXTI */
1007
      exti.EXTI_Line = EXTI_Line3;
1008
      exti.EXTI_Mode = EXTI_Mode_Interrupt;
1009
      exti.EXTI_Trigger = EXTI_Trigger_Falling;
1010
      exti.EXTI_LineCmd = ENABLE;
1011
      EXTI_Init(&exti);
1012

    
1013
      /* configure TOUCH_INT_N EXTI */
1014
      exti.EXTI_Line = EXTI_Line5;
1015
      exti.EXTI_Mode = EXTI_Mode_Interrupt;
1016
      exti.EXTI_Trigger = EXTI_Trigger_Falling;
1017
      exti.EXTI_LineCmd = ENABLE;
1018
      EXTI_Init(&exti);
1019

    
1020
      /* configure PATH_DC EXTI */
1021
      if (backup_reg.shutdown_pri_reason == BL_SHUTDOWN_PRI_RSN_DEEPSLEEP) {
1022
        exti.EXTI_Line = EXTI_Line8;
1023
        exti.EXTI_Mode = EXTI_Mode_Interrupt;
1024
        exti.EXTI_Trigger = EXTI_Trigger_Falling;
1025
        exti.EXTI_LineCmd = ENABLE;
1026
        EXTI_Init(&exti);
1027
      }
1028

    
1029
      /* configure the NVIC so ADC and EXTI will be handled */
1030
      NVIC_InitTypeDef nvic;
1031
      nvic.NVIC_IRQChannel = ADC_IRQn;
1032
      nvic.NVIC_IRQChannelPreemptionPriority = 6;
1033
      nvic.NVIC_IRQChannelSubPriority = 6;
1034
      nvic.NVIC_IRQChannelCmd = ENABLE;
1035
      NVIC_Init(&nvic);
1036
      nvic.NVIC_IRQChannel = EXTI3_IRQn;
1037
      nvic.NVIC_IRQChannelPreemptionPriority = 6;
1038
      nvic.NVIC_IRQChannelSubPriority = 6;
1039
      nvic.NVIC_IRQChannelCmd = ENABLE;
1040
      NVIC_Init(&nvic);
1041
      NVIC_EnableIRQ(EXTI3_IRQn);
1042
      nvic.NVIC_IRQChannel = EXTI9_5_IRQn;
1043
      nvic.NVIC_IRQChannelPreemptionPriority = 6;
1044
      nvic.NVIC_IRQChannelSubPriority = 6;
1045
      nvic.NVIC_IRQChannelCmd = ENABLE;
1046
      NVIC_Init(&nvic);
1047
      NVIC_EnableIRQ(EXTI9_5_IRQn);
1048

    
1049
      /* activate the ADC */
1050
      ADC_SoftwareStartConv(ADC1);
1051

    
1052
      /* sleep until something happens */
1053
      __WFI();
1054

    
1055
      /* disable the chargers */
1056
      GPIO_SetBits(CHARGE_EN1_N_GPIO, CHARGE_EN1_N_PIN);
1057
      GPIO_SetBits(CHARGE_EN2_N_GPIO, CHARGE_EN2_N_PIN);
1058
      setLed(BLT_FALSE);
1059

    
1060
      /* evaluate wakeup reason */
1061
      // note: since I (tschoepp) don't know the difference between 'pending' and 'active' IRQs, both flags are ORed.
1062
      uint8_t wkup_rsn = BL_WAKEUP_SEC_RSN_UNKNOWN;
1063
      if ((NVIC_GetActive(ADC_IRQn) != 0 || NVIC_GetPendingIRQ(ADC_IRQn) != 0) &&
1064
          ADC_GetITStatus(ADC1, ADC_IT_AWD) == SET &&
1065
          ADC_GetFlagStatus(ADC1, ADC_FLAG_AWD) == SET) {
1066
        wkup_rsn |= BL_WAKEUP_SEC_RSN_VSYSLOW;
1067
      }
1068
      if ((NVIC_GetActive(EXTI3_IRQn) != 0 || NVIC_GetPendingIRQ(EXTI3_IRQn) != 0) &&
1069
          EXTI_GetFlagStatus(EXTI_Line3) == SET) {
1070
        wkup_rsn |= BL_WAKEUP_SEC_RSN_UART;
1071
      }
1072
      if ((NVIC_GetActive(EXTI9_5_IRQn) != 0 || NVIC_GetPendingIRQ(EXTI9_5_IRQn) != 0) &&
1073
          EXTI_GetFlagStatus(EXTI_Line5) == SET) {
1074
        wkup_rsn |= BL_WAKEUP_SEC_RSN_TOUCH;
1075
      }
1076
      if ((NVIC_GetActive(EXTI9_5_IRQn) != 0 || NVIC_GetPendingIRQ(EXTI9_5_IRQn) != 0) &&
1077
          EXTI_GetFlagStatus(EXTI_Line8) == SET) {
1078
        wkup_rsn |= BL_WAKEUP_SEC_RSN_PWRPLUG;
1079
      }
1080

    
1081
      /* since only the first interrupt will be handles, clear any pending ones */
1082
      NVIC_DisableIRQ(ADC_IRQn);
1083
      NVIC_DisableIRQ(EXTI3_IRQn);
1084
      NVIC_DisableIRQ(EXTI9_5_IRQn);
1085
      NVIC_ClearPendingIRQ(ADC_IRQn);
1086
      NVIC_ClearPendingIRQ(EXTI3_IRQn);
1087
      NVIC_ClearPendingIRQ(EXTI9_5_IRQn);
1088

    
1089
      /* clear all pending EXTI events */
1090
      EXTI_DeInit();
1091
      EXTI_ClearFlag(EXTI_Line3);
1092
      EXTI_ClearFlag(EXTI_Line5);
1093
      EXTI_ClearFlag(EXTI_Line8);
1094

    
1095
      /* make sure the LED was visibly turned off */
1096
      msleep(100);
1097

    
1098
      /* depending on the wakup reason, handle accordingly */
1099
      if (wkup_rsn & BL_WAKEUP_SEC_RSN_TOUCH) {
1100
        /* the system was interrupted via the TOUCH_INT_N signal */
1101

    
1102
        /* act as if this was a normal touch wakeup */
1103
        backup_reg.wakeup_pri_reason = BL_WAKEUP_PRI_RSN_WKUP;
1104
        backup_reg.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_TOUCH;
1105
        RTC_WriteBackupRegister(BL_RTC_BACKUP_REG, backup_reg.raw);
1106
        return handleTouchWakeup();
1107
      } else if (wkup_rsn & BL_WAKEUP_SEC_RSN_UART) {
1108
        /* the system was interrupted via the SYS_UARTDN signal */
1109

    
1110
        /* act as if this was a normal UART wakeup */
1111
        backup_reg.wakeup_pri_reason = BL_WAKEUP_PRI_RSN_WKUP;
1112
        backup_reg.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_UART;
1113
        RTC_WriteBackupRegister(BL_RTC_BACKUP_REG, backup_reg.raw);
1114
        return handleUartDnWakeup();
1115
      } else if (wkup_rsn & BL_WAKEUP_SEC_RSN_VSYSLOW) {
1116
        /* VSYS has dropped below 9V */
1117

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

    
1153
  return ERROR;
1154
} /*** end of handleIwdgWakeup ***/
1155

    
1156
/*
1157
 * Indicates the DiWheelDrive module to enter hibernate mode at wakeup.
1158
 * This function should be called quite at the beginning of the according handleXXXReset/Wakeup() methods.
1159
 */
1160
static void indicateHibernate(void) {
1161
  /* signal the DiWheelDrive to enter hibernate mode as well, so it will activate the charging pins */
1162
  GPIO_ResetBits(SYS_UART_DN_GPIO, SYS_UART_DN_PIN);
1163
  msleep(10); // this must be that long, because the DiWheelDrive sleeps some time before evaluating any signals
1164
  GPIO_SetBits(SYS_UART_DN_GPIO, SYS_UART_DN_PIN);
1165

    
1166
  /* if the DiWheeDrive needs some time for setup it may pull down the signal */
1167
  waitForSignal(SYS_UART_DN_GPIO, SYS_UART_DN_PIN, Bit_SET);
1168

    
1169
  return;
1170
} /*** end of indicateHibernate ***/
1171

    
1172
/*
1173
 *Performs a one-shot measurement of the VSYS voltage.
1174
 */
1175
static void AdcSingleMeasurement(void) {
1176
  /* reset and initialize ADC for single-shot measurement */
1177
//    ADC_DeInit();
1178
  setupADC(ADC1, 0, 0);
1179

    
1180
  /* initialize the NVIC so ADC interrupts are handled */
1181
  NVIC_InitTypeDef nvic;
1182
  nvic.NVIC_IRQChannel = ADC_IRQn;
1183
  nvic.NVIC_IRQChannelPreemptionPriority = 6;
1184
  nvic.NVIC_IRQChannelSubPriority = 6;
1185
  nvic.NVIC_IRQChannelCmd = ENABLE;
1186
  NVIC_Init(&nvic);
1187

    
1188
  /* measure the voltage once */
1189
  setLed(BLT_TRUE);
1190
  ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
1191
  ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
1192
  NVIC_EnableIRQ(ADC_IRQn);
1193
  ADC_SoftwareStartConv(ADC1);
1194
  while (ADC_GetITStatus(ADC1, ADC_IT_EOC) != SET && ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) != SET) {
1195
    __WFI();
1196
  }
1197
  NVIC_DisableIRQ(ADC_IRQn);
1198
  ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
1199
  ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
1200
  NVIC_ClearPendingIRQ(ADC_IRQn);
1201
  setLed(BLT_FALSE);
1202

    
1203
  return;
1204
} /*** end of AdcSingleMeasurement ***/
1205

    
1206
/*
1207
 * Configures the ADC for measuring VSYS.
1208
 * ADCx is the ADC object to initialize.
1209
 * low_th and high_th are the threshold values for the analog watchdor (must be 12-bit!).
1210
 * If low_th >= high_th, the ADC is configured for single-shot measurements.
1211
 * Otherwise, the watchdog is configured with the corresponding thresholds.
1212
 */
1213
ADC_TypeDef* setupADC(ADC_TypeDef* adc, const uint16_t low_th, const uint16_t high_th) {
1214
  /* evaluate the arguments */
1215
  blt_bool awd_enable = BLT_FALSE;
1216
  if (low_th < high_th) {
1217
    awd_enable = BLT_TRUE;
1218
  }
1219

    
1220
  /* enable the clock */
1221
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
1222

    
1223
  /* enable the ADC (wakes it from low-power mode) */
1224
  ADC_Cmd(adc, ENABLE);
1225

    
1226
  /* initialize the common registers */
1227
  ADC_CommonInitTypeDef adc_cinit;
1228
  ADC_CommonStructInit(&adc_cinit);
1229
  adc_cinit.ADC_Prescaler = ADC_Prescaler_Div8; // clock as slow as possible
1230
  ADC_CommonInit(&adc_cinit);
1231

    
1232
  /* initialize the ADC */
1233
  ADC_InitTypeDef adc_init;
1234
  ADC_StructInit(&adc_init);
1235
  adc_init.ADC_ContinuousConvMode = (awd_enable == BLT_TRUE) ? ENABLE : DISABLE;
1236
  ADC_Init(adc, &adc_init);
1237

    
1238
  /* disable internal sensors */
1239
  ADC_TempSensorVrefintCmd(DISABLE);
1240
  ADC_VBATCmd(DISABLE);
1241

    
1242
  /* configure ADC channel and speed */
1243
  ADC_RegularChannelConfig(adc, ADC_Channel_9, 1, ADC_SampleTime_480Cycles);
1244
  ADC_EOCOnEachRegularChannelCmd(adc, (awd_enable == BLT_TRUE) ? DISABLE : ENABLE);
1245
  ADC_DiscModeCmd(adc, DISABLE);
1246

    
1247
  /* disable DMA */
1248
  ADC_DMACmd(adc, DISABLE);
1249

    
1250
  /* disable injected mode */
1251
  ADC_AutoInjectedConvCmd(adc, DISABLE);
1252
  ADC_InjectedDiscModeCmd(adc, DISABLE);
1253

    
1254
  /* configure the analog watchdog */
1255
  if (awd_enable == BLT_TRUE) {
1256
    ADC_AnalogWatchdogSingleChannelConfig(adc, ADC_Channel_9);
1257
    ADC_AnalogWatchdogThresholdsConfig(adc, high_th, low_th);
1258
    ADC_AnalogWatchdogCmd(adc, ADC_AnalogWatchdog_SingleRegEnable);
1259
  } else {
1260
    ADC_AnalogWatchdogCmd(adc, ADC_AnalogWatchdog_None);
1261
  }
1262

    
1263
  /* configure the interrupts to be generated by the ADC */
1264
  ADC_ITConfig(adc, ADC_IT_EOC, (awd_enable == BLT_TRUE) ? DISABLE : ENABLE);
1265
  ADC_ITConfig(adc, ADC_IT_AWD, (awd_enable == BLT_TRUE) ? ENABLE : DISABLE);
1266
  ADC_ITConfig(adc, ADC_IT_JEOC, DISABLE);
1267
  ADC_ITConfig(adc, ADC_IT_OVR, DISABLE);
1268

    
1269
  return adc;
1270
}
1271

    
1272
/*
1273
 * Callback function that handles the system shutdown and enters transportation mode.
1274
 * When called from a multithreaded environment, it must be ensured that no other thread will preempt this function.
1275
 * In transportation low-power mode the system can only be woken up by pulling down the NRST signal.
1276
 * Furthermore, the system can not be charged when in transportation mode.
1277
 */
1278
void blCallbackShutdownTransportation(void) {
1279
  /* make sure that the required clocks are activated */
1280
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE);
1281
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
1282

    
1283
  /* set/keep the SYS_SYNC and SYS_PD signals active */
1284
  GPIO_ResetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1285
  GPIO_ResetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
1286

    
1287
  /* initialized the standalone timer */
1288
  saTimerInit();
1289

    
1290
  setLed(BLT_TRUE);
1291

    
1292
  /* wait for all boards to be ready for shutdown */
1293
  GPIO_SetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1294
  if (GPIO_ReadOutputDataBit(SYS_REG_EN_GPIO, SYS_REG_EN_PIN) == Bit_SET) {
1295
    // this must skipped if the pullup voltage (VIO3.3) is not active
1296
    setLed(BLT_TRUE);
1297
    waitForSignal(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN, Bit_SET);
1298
    setLed(BLT_FALSE);
1299
  }
1300

    
1301
  /* execute disambiguation procedure and signal all modules to enter transportation mode */
1302
  if (shutdownDisambiguationProcedure(BL_SHUTDOWN_PRI_RSN_TRANSPORT) != SUCCESS) {
1303
    blinkSOS(1);
1304
    msleep(10);
1305
  }
1306

    
1307
  shutdownToTransportation();
1308

    
1309
  return;
1310
} /*** end of blCallbackTransportation ***/
1311

    
1312
/*
1313
 * Callback function that handles the system shutdown and enters deepsleep mode.
1314
 * When called from a multithreaded environment, it must be ensured that no other thread will preempt this function.
1315
 * 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.
1316
 * When a power plug is detected, the system will switch to hibernate mode, to provide charging capabilities (cf. handlePathDcWakeup()).
1317
 * As soon as the plug is removed again, however, the system will return to deppsleep mode (cf. handleIwdgWakeup()).
1318
 */
1319
void blCallbackShutdownDeepsleep(void) {
1320
  /* make sure that the required clocks are activated */
1321
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE);
1322
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
1323

    
1324
  /* set/keep the SYS_SYNC and SYS_PD signals active */
1325
  GPIO_ResetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1326
  GPIO_ResetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
1327

    
1328
  /* initialized the standalone timer */
1329
  saTimerInit();
1330

    
1331
  setLed(BLT_TRUE);
1332

    
1333
  /* wait for all boards to be ready for shutdown */
1334
  GPIO_SetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1335
  if (GPIO_ReadOutputDataBit(SYS_REG_EN_GPIO, SYS_REG_EN_PIN) == Bit_SET) {
1336
    // this must skipped if the pullup voltage (VIO3.3) is not active
1337
    setLed(BLT_TRUE);
1338
    waitForSignal(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN, Bit_SET);
1339
    setLed(BLT_FALSE);
1340
  }
1341

    
1342
  /* execute disambiguation procedure and signal all modules to enter deepsleep mode */
1343
  if (shutdownDisambiguationProcedure(BL_SHUTDOWN_PRI_RSN_DEEPSLEEP) != SUCCESS) {
1344
    blinkSOS(1);
1345
    msleep(10);
1346
  }
1347

    
1348
  shutdownToDeepsleep();
1349

    
1350
  return;
1351
} /*** end of blCallbackDeepsleep ***/
1352

    
1353
/*
1354
 * Callback function that handles the system shutdown and enters hibernate mode.
1355
 * When called from a multithreaded environment, it must be ensured that no other thread will preempt this function.
1356
 * Since this function actually just configures the system in a way, that it will enter hibernate mode after the next reset and rests it,
1357
 * see the handleSoftwareReset() function for more details about the hibernate low-power mode.
1358
 */
1359
void blCallbackShutdownHibernate(void) {
1360
  /* make sure that the required clocks are activated */
1361
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE);
1362
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
1363

    
1364
  /* set/keep the SYS_SYNC and SYS_PD signals active */
1365
  GPIO_ResetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1366
  GPIO_ResetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
1367

    
1368
  /* initialized the standalone timer */
1369
  saTimerInit();
1370

    
1371
  setLed(BLT_TRUE);
1372

    
1373
  /* wait for all boards to be ready for shutdown */
1374
  GPIO_SetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1375
  if (GPIO_ReadOutputDataBit(SYS_REG_EN_GPIO, SYS_REG_EN_PIN) == Bit_SET) {
1376
    // this must skipped if the pullup voltage (VIO3.3) is not active
1377
    setLed(BLT_TRUE);
1378
    waitForSignal(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN, Bit_SET);
1379
    setLed(BLT_FALSE);
1380
  }
1381

    
1382
  /* execute disambiguation procedure and signal all modules to enter hibernate mode */
1383
  if (shutdownDisambiguationProcedure(BL_SHUTDOWN_PRI_RSN_DEEPSLEEP) != SUCCESS) {
1384
    blinkSOS(1);
1385
    msleep(10);
1386
  }
1387

    
1388
  shutdownToHibernate();
1389

    
1390
  return;
1391
} /*** end of blCallbackShutdownHibernate ***/
1392

    
1393
/*
1394
 * Callback function that handles the system shutdown and initializes a restart.
1395
 * When called from a multithreaded environment, it must be ensured that no other thread will preempt this function.
1396
 * By configuration it is ensured, that the system will end up executing the handleSoftwareReset() function after reset.
1397
 */
1398
void blCallbackShutdownRestart(void) {
1399
  /* make sure that the required clocks are activated */
1400
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE);
1401
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
1402

    
1403
  /* set/keep the SYS_SYNC and SYS_PD signals active */
1404
  GPIO_ResetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1405
  GPIO_ResetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
1406

    
1407
  /* initialized the standalone timer */
1408
  saTimerInit();
1409

    
1410
  setLed(BLT_TRUE);
1411

    
1412
  /* deactivate SYS_PD_N and ensure that all modules had a chance to detect the falling edge */
1413
  msleep(1);
1414
  GPIO_SetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
1415
  msleep(1);
1416

    
1417
  /* wait for all boards to be ready for shutdown */
1418
  GPIO_SetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1419
  if (GPIO_ReadOutputDataBit(SYS_REG_EN_GPIO, SYS_REG_EN_PIN) == Bit_SET) {
1420
    // this must skipped if the pullup voltage (VIO3.3) is not active
1421
    setLed(BLT_TRUE);
1422
    waitForSignal(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN, Bit_SET);
1423
    setLed(BLT_FALSE);
1424
  }
1425

    
1426
  /* execute disambiguation procedure and signal all modules to restart normally */
1427
  if (shutdownDisambiguationProcedure(BL_SHUTDOWN_PRI_RSN_RESTART) != SUCCESS) {
1428
    blinkSOS(1);
1429
    msleep(10);
1430
  }
1431

    
1432
  /* restart the system */
1433
  shutdownAndRestart();
1434

    
1435
  return;
1436
} /*** end of blCallbackRestart ***/
1437

    
1438
/*
1439
 * Callback function that handles a system shutdown/restart request from another module.
1440
 * Depending on the result of the disambiguation procedure, the module will enter the according low-power mode or restart.
1441
 * When called from a multithreaded environment, it must be ensured that no other thread will preempt this function.
1442
 */
1443
void blCallbackHandleShutdownRequest(void) {
1444
  /* make sure that the required clocks are activated */
1445
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE);
1446
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
1447

    
1448
  /* set/keep the SYS_SYNC and SYS_PD signals active */
1449
  GPIO_ResetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1450
  GPIO_ResetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
1451

    
1452
  /* initialized the standalone timer */
1453
  saTimerInit();
1454

    
1455
  setLed(BLT_TRUE);
1456

    
1457
  /* deactivate SYS_PD_N and ensure that all modules had a chance to detect the falling edge */
1458
  msleep(1);
1459
  GPIO_SetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
1460
  msleep(1);
1461

    
1462
  /* wait for all boards to be ready for shutdown */
1463
  GPIO_SetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1464
  if (GPIO_ReadOutputDataBit(SYS_REG_EN_GPIO, SYS_REG_EN_PIN) == Bit_SET) {
1465
    // this must be skipped if the pullup voltage (VIO3.3) is not active
1466
    setLed(BLT_TRUE);
1467
    waitForSignal(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN, Bit_SET);
1468
    setLed(BLT_FALSE);
1469
  }
1470

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

    
1474
  /* disambiguation procedure (passive) */
1475
  uint32_t pulse_counter = 0;
1476
  while (waitForSignalTimeout(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN, Bit_RESET, 10)) {
1477
    waitForSignal(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN, Bit_SET);
1478
    ++pulse_counter;
1479
  }
1480

    
1481
  /* evaluate and hanlde disambiguation result */
1482
  if (shutdown_nrestart == BLT_TRUE) {
1483
    /* shutdown request */
1484

    
1485
    /* handle special cases */
1486
    if (pulse_counter == BL_SHUTDOWN_PRI_RSN_UNKNOWN) {
1487
      /* no pulse at all was received */
1488
      pulse_counter = BL_SHUTDOWN_PRI_RSN_DEFAULT;
1489
    } else if (pulse_counter != BL_SHUTDOWN_PRI_RSN_HIBERNATE &&
1490
               pulse_counter != BL_SHUTDOWN_PRI_RSN_DEEPSLEEP &&
1491
               pulse_counter != BL_SHUTDOWN_PRI_RSN_TRANSPORT) {
1492
      /* invalid number of pulses received */
1493
      blinkSOS(1);
1494
      pulse_counter = BL_SHUTDOWN_PRI_RSN_DEFAULT;
1495
    }
1496

    
1497
    switch (pulse_counter) {
1498
      case BL_SHUTDOWN_PRI_RSN_HIBERNATE:
1499
        shutdownToHibernate();
1500
        break;
1501
      case BL_SHUTDOWN_PRI_RSN_DEEPSLEEP:
1502
        shutdownToDeepsleep();
1503
        break;
1504
      case BL_SHUTDOWN_PRI_RSN_TRANSPORT:
1505
        shutdownToTransportation();
1506
        break;
1507
    }
1508
  } else {
1509
    /* restart request */
1510

    
1511
    /* there is no ambiguity for restart, so it is ignored */
1512
    shutdownAndRestart();
1513
  }
1514

    
1515
  /* if this code is reached, the system did neither shut down, nor restart.
1516
   * This must never be the case!
1517
   */
1518
  blinkSOSinf();
1519
  return;
1520
} /*** end of blCallbackHandleShutdownRequest ***/
1521

    
1522
/*********************************** end of main.c *************************************/