Statistics
| Branch: | Tag: | Revision:

amiro-blt / Target / Modules / PowerManagement_1-2 / Boot / main.c @ a270d48f

History | View | Annotate | Download (56.971 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, 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
  /* 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

    
313
} /*** end of Init ***/
314

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

    
321
  /*
322
   * OUTPUTS
323
   */
324

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

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

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

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

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

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

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

    
388
  /* initialize CHARGE_EN1_N and CHARGE_EN2_N and push them up (inactive) */
389
  GPIO_SetBits(CHARGE_EN1_N_GPIO, CHARGE_EN1_N_PIN);
390
  GPIO_SetBits(CHARGE_EN2_N_GPIO, CHARGE_EN2_N_PIN);
391
  gpio_init.GPIO_Pin    = CHARGE_EN1_N_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(CHARGE_EN1_N_GPIO, &gpio_init);
397
  gpio_init.GPIO_Pin    = CHARGE_EN2_N_PIN;
398
  GPIO_Init(CHARGE_EN2_N_GPIO, &gpio_init);
399

    
400
  /*
401
   * INPUTS
402
   */
403

    
404
  /* initialize SWITCH_STATUS_N */
405
  gpio_init.GPIO_Pin    = SWITCH_STATUS_N_PIN;
406
  gpio_init.GPIO_Mode   = GPIO_Mode_IN;
407
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
408
  gpio_init.GPIO_OType  = GPIO_OType_PP;
409
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
410
  GPIO_Init(SWITCH_STATUS_N_GPIO, &gpio_init);
411

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

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

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

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

    
444
  return;
445
} /*** end of initGpio ***/
446

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

    
469
  return;
470
} /*** end of initExti ***/
471

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

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

    
510
  return ret_val;
511
} /*** end of shutdownDisambiguationProcedure ***/
512

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

    
521
  /* power down the system */
522
  systemPowerDown();
523

    
524
  /* deactivate the WKUP pin */
525
  PWR_WakeUpPinCmd(DISABLE);
526

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

    
534
  /* disable the IWDG */
535
  IWDG_ReloadCounter();
536

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

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

    
549
  /* enter standby mode */
550
  PWR_EnterSTANDBYMode();
551

    
552
  return;
553
} /*** end of shutdownToTransportation ***/
554

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

    
563
  /* power down the system */
564
  systemPowerDown();
565

    
566
  /* activate the WKUP pin */
567
  PWR_WakeUpPinCmd(ENABLE);
568

    
569
  /*
570
   * Configuration of RTC and IWDG belongs to the OS.
571
   */
572

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

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

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

    
592
  return;
593
} /*** end of shutdownToDeepsleep ***/
594

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

    
603
  /* power down the system */
604
  systemPowerDown();
605

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

    
615
  /* morse 'OK' via the LED to signal that shutodnw was successful */
616
  blinkOK(1);
617

    
618
  /* reset the MCU */
619
  NVIC_SystemReset();
620

    
621
  return;
622
} /*** end of shutdownToHibernate ***/
623

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

    
632
  /* power down the system */
633
  systemPowerDown();
634

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

    
644
  /* morse 'OK' via the LED to signal that shutodnw was successful */
645
  blinkOK(1);
646

    
647
  /* reset the MCU */
648
  NVIC_SystemReset();
649

    
650
  return;
651
} /*** end of shutdownAndRestart ***/
652

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

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

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

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

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

    
693
  return;
694
} /*** end of configGpioForShutdown ***/
695

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

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

    
705
  /* reset slave modules */
706
  GPIO_ResetBits(SYS_WARMRST_N_GPIO, SYS_WARMRST_N_PIN);
707

    
708
  /* disable voltage regulators */
709
  GPIO_ResetBits(SYS_REG_EN_GPIO, SYS_REG_EN_PIN);
710

    
711
  /* cut power */
712
  GPIO_ResetBits(POWER_EN_GPIO, POWER_EN_PIN);
713

    
714
  /* make sure, all capacitors are discharged */
715
  msleep(100);
716

    
717
  setLed(BLT_FALSE);
718

    
719
  return;
720
} /*** end of systemPowerDown ***/
721

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

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

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

    
762
  return ms_capped;
763
} /*** end of configIWDG ***/
764

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

    
780
  /* drive SYS_WARMRST_N high (inactive) */
781
  GPIO_SetBits(SYS_WARMRST_N_GPIO, SYS_WARMRST_N_PIN);
782

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

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

    
790
  /* initialize the bootloader */
791
  BootInit();
792

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

    
813
    /* run the bootloader task */
814
    BootTask();
815

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

    
823
  return ERROR;
824
} /*** end of handleColdReset ***/
825

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

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

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

    
861
      /* enter standby mode */
862
      PWR_EnterSTANDBYMode();
863

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

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

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

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

    
932
  return ERROR;
933
} /*** end of handlePathDcWakeup ***/
934

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

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

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

    
961
    /* measure the current voltage of VSYS */
962
    AdcSingleMeasurement();
963

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

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

    
986
        /* enter standby mode */
987
        PWR_EnterSTANDBYMode();
988
      }
989

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

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

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

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

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

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

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

    
1047
      /* activate the ADC */
1048
      ADC_SoftwareStartConv(ADC1);
1049

    
1050
      /* sleep until something happens */
1051
      __WFI();
1052

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

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

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

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

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

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

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

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

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

    
1151
  return ERROR;
1152
} /*** end of handleIwdgWakeup ***/
1153

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

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

    
1167
  return;
1168
} /*** end of indicateHibernate ***/
1169

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

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

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

    
1201
  return;
1202
} /*** end of AdcSingleMeasurement ***/
1203

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

    
1218
  /* enable the clock */
1219
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
1220

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

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

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

    
1236
  /* disable internal sensors */
1237
  ADC_TempSensorVrefintCmd(DISABLE);
1238
  ADC_VBATCmd(DISABLE);
1239

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

    
1245
  /* disable DMA */
1246
  ADC_DMACmd(adc, DISABLE);
1247

    
1248
  /* disable injected mode */
1249
  ADC_AutoInjectedConvCmd(adc, DISABLE);
1250
  ADC_InjectedDiscModeCmd(adc, DISABLE);
1251

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

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

    
1267
  return adc;
1268
}
1269

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

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

    
1285
  /* initialized the standalone timer */
1286
  saTimerInit();
1287

    
1288
  setLed(BLT_TRUE);
1289

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

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

    
1305
  shutdownToTransportation();
1306

    
1307
  return;
1308
} /*** end of bLCallbackTransportation ***/
1309

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

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

    
1326
  /* initialized the standalone timer */
1327
  saTimerInit();
1328

    
1329
  setLed(BLT_TRUE);
1330

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

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

    
1346
  shutdownToDeepsleep();
1347

    
1348
  return;
1349
} /*** end of bLCallbackDeepsleep ***/
1350

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

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

    
1366
  /* initialized the standalone timer */
1367
  saTimerInit();
1368

    
1369
  setLed(BLT_TRUE);
1370

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

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

    
1386
  shutdownToHibernate();
1387

    
1388
  return;
1389
} /*** end of bLCallbackShutdownHibernate ***/
1390

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

    
1401
  /* set/keep the SYS_SYNC and SYS_PD signal active */
1402
  GPIO_ResetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1403
  GPIO_ResetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
1404

    
1405
  /* initialized the standalone timer */
1406
  saTimerInit();
1407

    
1408
  setLed(BLT_TRUE);
1409

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

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

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

    
1430
  /* restart the system */
1431
  shutdownAndRestart();
1432

    
1433
  return;
1434
} /*** end of bLCallbackRestart ***/
1435

    
1436

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

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

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

    
1454
  setLed(BLT_TRUE);
1455

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

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

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

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

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

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

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

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

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

    
1521

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