Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (57.492 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_Beta, BL_VERSION_MAJOR, BL_VERSION_MINOR, 4},
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 SWITCH_STATUS_N */
415
  gpio_init.GPIO_Pin    = SWITCH_STATUS_N_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(SWITCH_STATUS_N_GPIO, &gpio_init);
421

    
422
  /* initialize PATH_DC */
423
  gpio_init.GPIO_Pin    = PATH_DC_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(PATH_DC_GPIO, &gpio_init);
429

    
430
  /* initialize TOUCH_INT_N */
431
  gpio_init.GPIO_Pin    = TOUCH_INT_N_PIN;
432
  gpio_init.GPIO_Mode   = GPIO_Mode_IN;
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(TOUCH_INT_N_GPIO, &gpio_init);
437

    
438
  /* initialize VSYS_SENSE as analog input */
439
  gpio_init.GPIO_Pin    = VSYS_SENSE_PIN;
440
  gpio_init.GPIO_Mode   = GPIO_Mode_AN;
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(VSYS_SENSE_GPIO, &gpio_init);
445

    
446
  /* initialize GPIOB4, since it is configured in alternate function mode on reset */
447
  gpio_init.GPIO_Pin    = CHARGE_STAT2A_PIN;
448
  gpio_init.GPIO_Mode   = GPIO_Mode_IN;
449
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
450
  gpio_init.GPIO_OType  = GPIO_OType_PP;
451
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
452
  GPIO_Init(CHARGE_STAT2A_GPIO, &gpio_init);
453

    
454
  return;
455
} /*** end of initGpio ***/
456

    
457
/*
458
 * Initialize all EXTI lines
459
 */
460
static void initExti(void) {
461
  /* configure EXTI lines */
462
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource0); // IR_INT1_N
463
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource0); // CHARGE_STAT1A
464
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource1); // GAUGE_BATLOW1
465
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource2); // GAUGE_BATGD1_N
466
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource3); // SYS_UART_DN
467
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource4); // CHARGE_STAT2A
468
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource4); // IR_INT2_N
469
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource5); // TOUCH_INT_N
470
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource6); // GAUGE_BATLOW2
471
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource7); // GAUGE_BATGD2_N
472
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource8); // PATH_DC
473
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource9); // SYS_SPI_DIR
474
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource12); // SYS_SYNC_N
475
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource13); // SYS_PD_N
476
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource14); // SYS_WARMRST_N
477
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource15); // SYS_UART_UP
478

    
479
  return;
480
} /*** end of initExti ***/
481

    
482
/*
483
 * Signals, which type of low-power mode the system shall enter after the shutdown sequence.
484
 */
485
ErrorStatus shutdownDisambiguationProcedure(const uint8_t type) {
486
  GPIO_SetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
487
  ErrorStatus ret_val = ERROR;
488

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

    
520
  return ret_val;
521
} /*** end of shutdownDisambiguationProcedure ***/
522

    
523
/*
524
 * Final shutdown of the system to enter transportation mode.
525
 */
526
void shutdownToTransportation(void) {
527
  /* configure some criticpal GPIOs as input
528
   * This is required, because otherwise some hardware might be powered through these signals */
529
  configGpioForShutdown();
530

    
531
  /* power down the system */
532
  systemPowerDown();
533

    
534
  /* deactivate the WKUP pin */
535
  PWR_WakeUpPinCmd(DISABLE);
536

    
537
  /* deactivate any RTC related events */
538
  RTC_WakeUpCmd(DISABLE);
539
  RTC_TamperCmd(RTC_Tamper_1, DISABLE);
540
  RTC_TimeStampCmd(RTC_TimeStampEdge_Rising, DISABLE);
541
  RTC_TimeStampCmd(RTC_TimeStampEdge_Falling, DISABLE);
542
  RTC_ClearFlag(~0);
543

    
544
  /* disable the IWDG */
545
  IWDG_ReloadCounter();
546

    
547
  /* write some information to the backup register */
548
  blBackupRegister_t backup;
549
  backup.shutdown_pri_reason = BL_SHUTDOWN_PRI_RSN_TRANSPORT;
550
  backup.shutdown_sec_reason = BL_SHUTDOWN_SEC_RSN_UNKNOWN;
551
  backup.wakeup_pri_reason = BL_WAKEUP_PRI_RSN_UNKNOWN;
552
  backup.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_UNKNOWN;
553
  PWR_BackupAccessCmd(ENABLE);
554
  RTC_WriteBackupRegister(BL_RTC_BACKUP_REG, backup.raw);
555

    
556
  /* morse 'OK' via the LED to signal that shutdown was successful */
557
  blinkOK(1);
558

    
559
  /* enter standby mode */
560
  PWR_EnterSTANDBYMode();
561

    
562
  return;
563
} /*** end of shutdownToTransportation ***/
564

    
565
/*
566
 * Final shutdown of the system to enter deepsleep mode.
567
 */
568
void shutdownToDeepsleep(void) {
569
  /* configure some criticpal GPIOs as input
570
   * This is required, because otherwise some hardware might be powered through these signals */
571
  configGpioForShutdown();
572

    
573
  /* power down the system */
574
  systemPowerDown();
575

    
576
  /* activate the WKUP pin */
577
  PWR_WakeUpPinCmd(ENABLE);
578

    
579
  /*
580
   * Configuration of RTC and IWDG belongs to the OS.
581
   */
582

    
583
  /* write some information to the backup register */
584
  blBackupRegister_t backup;
585
  backup.shutdown_pri_reason = BL_SHUTDOWN_PRI_RSN_DEEPSLEEP;
586
  backup.shutdown_sec_reason = BL_SHUTDOWN_SEC_RSN_UNKNOWN;
587
  backup.wakeup_pri_reason = BL_WAKEUP_PRI_RSN_UNKNOWN;
588
  backup.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_UNKNOWN;
589
  PWR_BackupAccessCmd(ENABLE);
590
  RTC_WriteBackupRegister(BL_RTC_BACKUP_REG, backup.raw);
591

    
592
  /* morse 'OK' via the LED to signal that shutdown was successful */
593
  blinkOK(1);
594

    
595
  /* enter standby mode or restart the system in case a power plug is already present */
596
  if (GPIO_ReadInputDataBit(PATH_DC_GPIO, PATH_DC_PIN) != Bit_SET) {
597
    PWR_EnterSTANDBYMode();
598
  } else {
599
    NVIC_SystemReset();
600
  }
601

    
602
  return;
603
} /*** end of shutdownToDeepsleep ***/
604

    
605
/*
606
 * Final shutdown of the system to enter hibernate mode.
607
 */
608
void shutdownToHibernate(void) {
609
  /* configure some criticpal GPIOs as input
610
   * This is required, because otherwise some hardware might be powered through these signals */
611
  configGpioForShutdown();
612

    
613
  /* power down the system */
614
  systemPowerDown();
615

    
616
  /* write some information to the backup register */
617
  blBackupRegister_t backup;
618
  backup.shutdown_pri_reason = BL_SHUTDOWN_PRI_RSN_HIBERNATE;
619
  backup.shutdown_sec_reason = BL_SHUTDOWN_SEC_RSN_UNKNOWN;
620
  backup.wakeup_pri_reason = BL_WAKEUP_PRI_RSN_UNKNOWN;
621
  backup.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_UNKNOWN;
622
  PWR_BackupAccessCmd(ENABLE);
623
  RTC_WriteBackupRegister(BL_RTC_BACKUP_REG, backup.raw);
624

    
625
  /* morse 'OK' via the LED to signal that shutdown was successful */
626
  blinkOK(1);
627

    
628
  /* reset the MCU */
629
  NVIC_SystemReset();
630

    
631
  return;
632
} /*** end of shutdownToHibernate ***/
633

    
634
/*
635
 * Final shutdown of the system and restart.
636
 */
637
void shutdownAndRestart(void) {
638
  /* configure some criticpal GPIOs as input
639
   * This is required, because otherwise some hardware might be powered through these signals */
640
  configGpioForShutdown();
641

    
642
  /* power down the system */
643
  systemPowerDown();
644

    
645
  /* write some information to the backup register */
646
  blBackupRegister_t backup;
647
  backup.shutdown_pri_reason = BL_SHUTDOWN_PRI_RSN_RESTART;
648
  backup.shutdown_sec_reason = BL_SHUTDOWN_SEC_RSN_UNKNOWN;
649
  backup.wakeup_pri_reason = BL_WAKEUP_PRI_RSN_UNKNOWN;
650
  backup.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_UNKNOWN;
651
  PWR_BackupAccessCmd(ENABLE);
652
  RTC_WriteBackupRegister(BL_RTC_BACKUP_REG, backup.raw);
653

    
654
  /* morse 'OK' via the LED to signal that shutdown was successful */
655
  blinkOK(1);
656

    
657
  /* reset the MCU */
658
  NVIC_SystemReset();
659

    
660
  return;
661
} /*** end of shutdownAndRestart ***/
662

    
663
/*
664
 * Configures some GPIO pins as inputs for safety reasons.
665
 * Under certain circumstances, these pins might power hardware that is supposed to be shut down.
666
 */
667
void configGpioForShutdown(void) {
668
  /* setup the configuration */
669
  GPIO_InitTypeDef gpio_init;
670
  gpio_init.GPIO_Mode   = GPIO_Mode_IN;
671
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
672
  gpio_init.GPIO_OType  = GPIO_OType_PP;
673
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
674

    
675
  /* configure SYS_UART_TX */
676
  gpio_init.GPIO_Pin = SYS_UART_TX_PIN;
677
  GPIO_Init(SYS_UART_TX_GPIO, &gpio_init);
678

    
679
  /* configure all SYS_SPI signals */
680
  gpio_init.GPIO_Pin = SYS_SPI_SS0_N_PIN;
681
  GPIO_Init(SYS_SPI_SS0_N_GPIO, &gpio_init);
682
  gpio_init.GPIO_Pin = SYS_SPI_SCLK_PIN;
683
  GPIO_Init(SYS_SPI_SCLK_GPIO, &gpio_init);
684
  gpio_init.GPIO_Pin = SYS_SPI_MISO_PIN;
685
  GPIO_Init(SYS_SPI_MISO_GPIO, &gpio_init);
686
  gpio_init.GPIO_Pin = SYS_SPI_MOSI_PIN;
687
  GPIO_Init(SYS_SPI_MOSI_GPIO, &gpio_init);
688
  gpio_init.GPIO_Pin = SYS_SPI_SS1_N_PIN;
689
  GPIO_Init(SYS_SPI_SS1_N_GPIO, &gpio_init);
690
  gpio_init.GPIO_Pin = SYS_SPI_DIR_PIN;
691
  GPIO_Init(SYS_SPI_DIR_GPIO, &gpio_init);
692

    
693
  /* configure CAN_TX */
694
  gpio_init.GPIO_Pin = CAN_TX_PIN;
695
  GPIO_Init(CAN_TX_GPIO, &gpio_init);
696

    
697
  /* configure all Bluetooth signals */
698
  gpio_init.GPIO_Pin = BT_CTS_PIN;
699
  GPIO_Init(BT_CTS_GPIO, &gpio_init);
700
  gpio_init.GPIO_Pin = BT_RX_PIN;
701
  GPIO_Init(BT_RX_GPIO, &gpio_init);
702

    
703
  return;
704
} /*** end of configGpioForShutdown ***/
705

    
706
/*
707
 * Disables all regulated voltages and finally cuts power to the rest of the system.
708
 */
709
void systemPowerDown(void) {
710
  setLed(BLT_TRUE);
711

    
712
  /* make sure that all other modules are shut down */
713
  msleep(10);
714

    
715
  /* reset slave modules */
716
  GPIO_ResetBits(SYS_WARMRST_N_GPIO, SYS_WARMRST_N_PIN);
717

    
718
  /* disable voltage regulators */
719
  GPIO_ResetBits(SYS_REG_EN_GPIO, SYS_REG_EN_PIN);
720

    
721
  /* cut power */
722
  GPIO_ResetBits(POWER_EN_GPIO, POWER_EN_PIN);
723

    
724
  /* make sure, all capacitors are discharged */
725
  msleep(100);
726

    
727
  setLed(BLT_FALSE);
728

    
729
  return;
730
} /*** end of systemPowerDown ***/
731

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

    
743
  /* detect the best fitting prescaler and compute the according reload value */
744
  uint8_t prescaler = 0;
745
  uint16_t reload_val = 0;
746
  if (ms_capped >= 0x4000) {
747
    prescaler = IWDG_Prescaler_256;
748
    reload_val = ms_capped >> 3;  // note: this corresponds to a floor function
749
    ms_capped = reload_val << 3;  // this applies the floor function to ms_capped
750
  } else if (ms_capped >= 0x2000) {
751
    prescaler = IWDG_Prescaler_128;
752
    reload_val = ms_capped >> 2;  // note: this corresponds to a floor function
753
    ms_capped = reload_val << 2;  // this applies the floor function to ms_capped
754
  } else if (ms_capped >= 0x1000) {
755
    ms_capped &= ~(0x0001);
756
    prescaler = IWDG_Prescaler_64;
757
    reload_val = ms_capped >> 1;  // note: this corresponds to a floor function
758
    ms_capped = reload_val << 1;  // this applies the floor function to ms_capped
759
  } else {
760
    prescaler = IWDG_Prescaler_32;
761
    reload_val = ms_capped;
762
  }
763

    
764
  /* configure the IWDG */
765
  if (reload_val > 0) {
766
    IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
767
    IWDG_SetPrescaler(prescaler);
768
    IWDG_SetReload(reload_val);
769
    IWDG_WriteAccessCmd(IWDG_WriteAccess_Disable);
770
  }
771

    
772
  return ms_capped;
773
} /*** end of configIWDG ***/
774

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

    
790
  /* drive SYS_WARMRST_N high (inactive) */
791
  GPIO_SetBits(SYS_WARMRST_N_GPIO, SYS_WARMRST_N_PIN);
792

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

    
797
  /* wait 1ms to make sure that all modules are running and started the bootloader */
798
  msleep(1);
799

    
800
  /* initialize the bootloader */
801
  BootInit();
802

    
803
  /* start the infinite program loop */
804
  uint32_t loopStartTime = 0;
805
  saTimerUpdate(&loopStartTime);
806
  uint32_t currentTime = loopStartTime;
807
  while (1)
808
  {
809
//    /* make the LED "double-blink" */
810
//    saTimerUpdate(&currentTime);
811
//    if (currentTime < loopStartTime + 50) {
812
//      setLed(BLT_TRUE);
813
//    } else if (currentTime < loopStartTime + 50+100) {
814
//      setLed(BLT_FALSE);
815
//    } else if (currentTime < loopStartTime + 50+100+50) {
816
//      setLed(BLT_TRUE);
817
//    } else if (currentTime < loopStartTime + 50+100+50+300) {
818
//      setLed(BLT_FALSE);
819
//    } else {
820
//      loopStartTime = currentTime;
821
//    }
822

    
823
    /* run the bootloader task */
824
    BootTask();
825

    
826
    /* check the SYS_PD_N signal */
827
    if (GPIO_ReadInputDataBit(SYS_PD_N_GPIO, SYS_PD_N_PIN) == Bit_RESET) {
828
      blCallbackHandleShutdownRequest();
829
      return SUCCESS;
830
    }
831
  }
832

    
833
  return ERROR;
834
} /*** end of handleColdReset ***/
835

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

    
856
      /* deactivate any RTC related events */
857
      RTC_WakeUpCmd(DISABLE);
858
      RTC_TamperCmd(RTC_Tamper_1, DISABLE);
859
      RTC_TimeStampCmd(RTC_TimeStampEdge_Rising, DISABLE);
860
      RTC_TimeStampCmd(RTC_TimeStampEdge_Falling, DISABLE);
861

    
862
      /* configure the IWDG to wake the system from standby mode */
863
      uint16_t iwdg_ms = 1;
864
      if (GPIO_ReadInputDataBit(PATH_DC_GPIO, PATH_DC_PIN) != Bit_SET) {
865
        /* if a power plug is detected, fire immediately (1ms), else fire after the defined hibernate time */
866
        iwdg_ms = HIBERNATE_TIME_MS;
867
      }
868
      configIwdg(iwdg_ms);
869
      IWDG_Enable();
870

    
871
      /* enter standby mode */
872
      PWR_EnterSTANDBYMode();
873

    
874
      return SUCCESS;
875
      break;
876
    }
877
    case BL_SHUTDOWN_PRI_RSN_RESTART:
878
    {
879
      return handleColdReset();
880
      break;
881
    }
882
    case BL_SHUTDOWN_PRI_RSN_DEEPSLEEP:
883
    {
884
      if (GPIO_ReadInputDataBit(PATH_DC_GPIO, PATH_DC_PIN) == Bit_SET) {
885
        return handlePathDcWakeup();
886
      } else {
887
        blCallbackShutdownDeepsleep();
888
      }
889
      break;
890
    }
891
    default:
892
      return ERROR;
893
  }
894
  return ERROR;
895
} /*** end of handleSoftwareReset ***/
896

    
897
/*
898
 * System was woken up via the WKUP pin and the SYS_UART_DN signal was found to be responsible.
899
 * In this case, the system starts as after a cold reset.
900
 * this function is identical to handleTouchWakeup().
901
 */
902
ErrorStatus handleUartDnWakeup(void) {
903
  return handleColdReset();
904
} /*** end of hanldeUartDnWakeup ***/
905

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

    
931
      return handleIwdgWakeup();
932
      break;
933
    case BL_SHUTDOWN_PRI_RSN_TRANSPORT:
934
      blCallbackShutdownTransportation();
935
      return SUCCESS;
936
      break;
937
    default:
938
      return ERROR;
939
      break;
940
  }
941

    
942
  return ERROR;
943
} /*** end of handlePathDcWakeup ***/
944

    
945
/*
946
 * System was woken up via the WKUP pin and the touch sensors were found to be responsible.
947
 * In this case the system starts as after an cold reset.
948
 * This function is identical to handleUartDnWakeup().
949
 */
950
ErrorStatus handleTouchWakeup(void) {
951
  return handleColdReset();
952
} /*** end of handleTouchWakeup ***/
953

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

    
966
    /* if in hibernate mode, indicate the DiWheelDrive to enter hibernate mode as well, so it will activate the charging pins */
967
    if (backup_reg.shutdown_pri_reason == BL_SHUTDOWN_PRI_RSN_HIBERNATE) {
968
      indicateHibernate();
969
    }
970

    
971
    /* measure the current voltage of VSYS */
972
    AdcSingleMeasurement();
973

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

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

    
996
        /* enter standby mode */
997
        PWR_EnterSTANDBYMode();
998
      }
999

    
1000
      return SUCCESS;
1001
    } else {
1002
      /* VSYS was found to be >= 9V */
1003
      setLed(BLT_TRUE);
1004

    
1005
      /* charge the battieries */
1006
      GPIO_ResetBits(CHARGE_EN1_N_GPIO, CHARGE_EN1_N_PIN);
1007
      GPIO_ResetBits(CHARGE_EN2_N_GPIO, CHARGE_EN2_N_PIN);
1008

    
1009
      /* configure analog watchdoch to fire as soon as the voltage drops below 9V */
1010
      ADC_DeInit();
1011
      setupADC(ADC1, (uint16_t)(9.0f / 5.33f / 3.3f * (float)0x0FFF), 0x0FFF);
1012

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

    
1021
      /* configure TOUCH_INT_N EXTI */
1022
      exti.EXTI_Line = EXTI_Line5;
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
      /* configure PATH_DC EXTI */
1029
      if (backup_reg.shutdown_pri_reason == BL_SHUTDOWN_PRI_RSN_DEEPSLEEP) {
1030
        exti.EXTI_Line = EXTI_Line8;
1031
        exti.EXTI_Mode = EXTI_Mode_Interrupt;
1032
        exti.EXTI_Trigger = EXTI_Trigger_Falling;
1033
        exti.EXTI_LineCmd = ENABLE;
1034
        EXTI_Init(&exti);
1035
      }
1036

    
1037
      /* configure the NVIC so ADC and EXTI will be handled */
1038
      NVIC_InitTypeDef nvic;
1039
      nvic.NVIC_IRQChannel = ADC_IRQn;
1040
      nvic.NVIC_IRQChannelPreemptionPriority = 6;
1041
      nvic.NVIC_IRQChannelSubPriority = 6;
1042
      nvic.NVIC_IRQChannelCmd = ENABLE;
1043
      NVIC_Init(&nvic);
1044
      nvic.NVIC_IRQChannel = EXTI3_IRQn;
1045
      nvic.NVIC_IRQChannelPreemptionPriority = 6;
1046
      nvic.NVIC_IRQChannelSubPriority = 6;
1047
      nvic.NVIC_IRQChannelCmd = ENABLE;
1048
      NVIC_Init(&nvic);
1049
      NVIC_EnableIRQ(EXTI3_IRQn);
1050
      nvic.NVIC_IRQChannel = EXTI9_5_IRQn;
1051
      nvic.NVIC_IRQChannelPreemptionPriority = 6;
1052
      nvic.NVIC_IRQChannelSubPriority = 6;
1053
      nvic.NVIC_IRQChannelCmd = ENABLE;
1054
      NVIC_Init(&nvic);
1055
      NVIC_EnableIRQ(EXTI9_5_IRQn);
1056

    
1057
      /* activate the ADC */
1058
      ADC_SoftwareStartConv(ADC1);
1059

    
1060
      /* sleep until something happens */
1061
      __WFI();
1062

    
1063
      /* disable the chargers */
1064
      GPIO_SetBits(CHARGE_EN1_N_GPIO, CHARGE_EN1_N_PIN);
1065
      GPIO_SetBits(CHARGE_EN2_N_GPIO, CHARGE_EN2_N_PIN);
1066
      setLed(BLT_FALSE);
1067

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

    
1089
      /* since only the first interrupt will be handles, clear any pending ones */
1090
      NVIC_DisableIRQ(ADC_IRQn);
1091
      NVIC_DisableIRQ(EXTI3_IRQn);
1092
      NVIC_DisableIRQ(EXTI9_5_IRQn);
1093
      NVIC_ClearPendingIRQ(ADC_IRQn);
1094
      NVIC_ClearPendingIRQ(EXTI3_IRQn);
1095
      NVIC_ClearPendingIRQ(EXTI9_5_IRQn);
1096

    
1097
      /* clear all pending EXTI events */
1098
      EXTI_DeInit();
1099
      EXTI_ClearFlag(EXTI_Line3);
1100
      EXTI_ClearFlag(EXTI_Line5);
1101
      EXTI_ClearFlag(EXTI_Line8);
1102

    
1103
      /* make sure the LED was visibly turned off */
1104
      msleep(100);
1105

    
1106
      /* depending on the wakup reason, handle accordingly */
1107
      if (wkup_rsn & BL_WAKEUP_SEC_RSN_TOUCH) {
1108
        /* the system was interrupted via the TOUCH_INT_N signal */
1109

    
1110
        /* act as if this was a normal touch wakeup */
1111
        backup_reg.wakeup_pri_reason = BL_WAKEUP_PRI_RSN_WKUP;
1112
        backup_reg.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_TOUCH;
1113
        RTC_WriteBackupRegister(BL_RTC_BACKUP_REG, backup_reg.raw);
1114
        return handleTouchWakeup();
1115
      } else if (wkup_rsn & BL_WAKEUP_SEC_RSN_UART) {
1116
        /* the system was interrupted via the SYS_UARTDN signal */
1117

    
1118
        /* act as if this was a normal UART wakeup */
1119
        backup_reg.wakeup_pri_reason = BL_WAKEUP_PRI_RSN_WKUP;
1120
        backup_reg.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_UART;
1121
        RTC_WriteBackupRegister(BL_RTC_BACKUP_REG, backup_reg.raw);
1122
        return handleUartDnWakeup();
1123
      } else if (wkup_rsn & BL_WAKEUP_SEC_RSN_VSYSLOW) {
1124
        /* VSYS has dropped below 9V */
1125

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

    
1161
  return ERROR;
1162
} /*** end of handleIwdgWakeup ***/
1163

    
1164
/*
1165
 * Indicates the DiWheelDrive module to enter hibernate mode at wakeup.
1166
 * This function should be called quite at the beginning of the according handleXXXReset/Wakeup() methods.
1167
 */
1168
static void indicateHibernate(void) {
1169
  /* signal the DiWheelDrive to enter hibernate mode as well, so it will activate the charging pins */
1170
  GPIO_ResetBits(SYS_UART_DN_GPIO, SYS_UART_DN_PIN);
1171
  msleep(10); // this must be that long, because the DiWheelDrive sleeps some time before evaluating any signals
1172
  GPIO_SetBits(SYS_UART_DN_GPIO, SYS_UART_DN_PIN);
1173

    
1174
  /* if the DiWheeDrive needs some time for setup it may pull down the signal */
1175
  waitForSignal(SYS_UART_DN_GPIO, SYS_UART_DN_PIN, Bit_SET);
1176

    
1177
  return;
1178
} /*** end of indicateHibernate ***/
1179

    
1180
/*
1181
 *Performs a one-shot measurement of the VSYS voltage.
1182
 */
1183
static void AdcSingleMeasurement(void) {
1184
  /* reset and initialize ADC for single-shot measurement */
1185
//    ADC_DeInit();
1186
  setupADC(ADC1, 0, 0);
1187

    
1188
  /* initialize the NVIC so ADC interrupts are handled */
1189
  NVIC_InitTypeDef nvic;
1190
  nvic.NVIC_IRQChannel = ADC_IRQn;
1191
  nvic.NVIC_IRQChannelPreemptionPriority = 6;
1192
  nvic.NVIC_IRQChannelSubPriority = 6;
1193
  nvic.NVIC_IRQChannelCmd = ENABLE;
1194
  NVIC_Init(&nvic);
1195

    
1196
  /* measure the voltage once */
1197
  setLed(BLT_TRUE);
1198
  ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
1199
  ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
1200
  NVIC_EnableIRQ(ADC_IRQn);
1201
  ADC_SoftwareStartConv(ADC1);
1202
  while (ADC_GetITStatus(ADC1, ADC_IT_EOC) != SET && ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) != SET) {
1203
    __WFI();
1204
  }
1205
  NVIC_DisableIRQ(ADC_IRQn);
1206
  ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
1207
  ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
1208
  NVIC_ClearPendingIRQ(ADC_IRQn);
1209
  setLed(BLT_FALSE);
1210

    
1211
  return;
1212
} /*** end of AdcSingleMeasurement ***/
1213

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

    
1228
  /* enable the clock */
1229
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
1230

    
1231
  /* enable the ADC (wakes it from low-power mode) */
1232
  ADC_Cmd(adc, ENABLE);
1233

    
1234
  /* initialize the common registers */
1235
  ADC_CommonInitTypeDef adc_cinit;
1236
  ADC_CommonStructInit(&adc_cinit);
1237
  adc_cinit.ADC_Prescaler = ADC_Prescaler_Div8; // clock as slow as possible
1238
  ADC_CommonInit(&adc_cinit);
1239

    
1240
  /* initialize the ADC */
1241
  ADC_InitTypeDef adc_init;
1242
  ADC_StructInit(&adc_init);
1243
  adc_init.ADC_ContinuousConvMode = (awd_enable == BLT_TRUE) ? ENABLE : DISABLE;
1244
  ADC_Init(adc, &adc_init);
1245

    
1246
  /* disable internal sensors */
1247
  ADC_TempSensorVrefintCmd(DISABLE);
1248
  ADC_VBATCmd(DISABLE);
1249

    
1250
  /* configure ADC channel and speed */
1251
  ADC_RegularChannelConfig(adc, ADC_Channel_9, 1, ADC_SampleTime_480Cycles);
1252
  ADC_EOCOnEachRegularChannelCmd(adc, (awd_enable == BLT_TRUE) ? DISABLE : ENABLE);
1253
  ADC_DiscModeCmd(adc, DISABLE);
1254

    
1255
  /* disable DMA */
1256
  ADC_DMACmd(adc, DISABLE);
1257

    
1258
  /* disable injected mode */
1259
  ADC_AutoInjectedConvCmd(adc, DISABLE);
1260
  ADC_InjectedDiscModeCmd(adc, DISABLE);
1261

    
1262
  /* configure the analog watchdog */
1263
  if (awd_enable == BLT_TRUE) {
1264
    ADC_AnalogWatchdogSingleChannelConfig(adc, ADC_Channel_9);
1265
    ADC_AnalogWatchdogThresholdsConfig(adc, high_th, low_th);
1266
    ADC_AnalogWatchdogCmd(adc, ADC_AnalogWatchdog_SingleRegEnable);
1267
  } else {
1268
    ADC_AnalogWatchdogCmd(adc, ADC_AnalogWatchdog_None);
1269
  }
1270

    
1271
  /* configure the interrupts to be generated by the ADC */
1272
  ADC_ITConfig(adc, ADC_IT_EOC, (awd_enable == BLT_TRUE) ? DISABLE : ENABLE);
1273
  ADC_ITConfig(adc, ADC_IT_AWD, (awd_enable == BLT_TRUE) ? ENABLE : DISABLE);
1274
  ADC_ITConfig(adc, ADC_IT_JEOC, DISABLE);
1275
  ADC_ITConfig(adc, ADC_IT_OVR, DISABLE);
1276

    
1277
  return adc;
1278
}
1279

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

    
1291
  /* set/keep the SYS_SYNC and SYS_PD signals active */
1292
  GPIO_ResetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1293
  GPIO_ResetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
1294

    
1295
  /* initialized the standalone timer */
1296
  saTimerInit();
1297

    
1298
  setLed(BLT_TRUE);
1299

    
1300
  /* wait for all boards to be ready for shutdown */
1301
  GPIO_SetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1302
  if (GPIO_ReadOutputDataBit(SYS_REG_EN_GPIO, SYS_REG_EN_PIN) == Bit_SET) {
1303
    // this must skipped if the pullup voltage (VIO3.3) is not active
1304
    setLed(BLT_TRUE);
1305
    waitForSignal(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN, Bit_SET);
1306
    setLed(BLT_FALSE);
1307
  }
1308

    
1309
  /* execute disambiguation procedure and signal all modules to enter transportation mode */
1310
  if (shutdownDisambiguationProcedure(BL_SHUTDOWN_PRI_RSN_TRANSPORT) != SUCCESS) {
1311
    blinkSOS(1);
1312
    msleep(10);
1313
  }
1314

    
1315
  shutdownToTransportation();
1316

    
1317
  return;
1318
} /*** end of blCallbackTransportation ***/
1319

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

    
1332
  /* set/keep the SYS_SYNC and SYS_PD signals active */
1333
  GPIO_ResetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1334
  GPIO_ResetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
1335

    
1336
  /* initialized the standalone timer */
1337
  saTimerInit();
1338

    
1339
  setLed(BLT_TRUE);
1340

    
1341
  /* wait for all boards to be ready for shutdown */
1342
  GPIO_SetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1343
  if (GPIO_ReadOutputDataBit(SYS_REG_EN_GPIO, SYS_REG_EN_PIN) == Bit_SET) {
1344
    // this must skipped if the pullup voltage (VIO3.3) is not active
1345
    setLed(BLT_TRUE);
1346
    waitForSignal(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN, Bit_SET);
1347
    setLed(BLT_FALSE);
1348
  }
1349

    
1350
  /* execute disambiguation procedure and signal all modules to enter deepsleep mode */
1351
  if (shutdownDisambiguationProcedure(BL_SHUTDOWN_PRI_RSN_DEEPSLEEP) != SUCCESS) {
1352
    blinkSOS(1);
1353
    msleep(10);
1354
  }
1355

    
1356
  shutdownToDeepsleep();
1357

    
1358
  return;
1359
} /*** end of blCallbackDeepsleep ***/
1360

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

    
1372
  /* set/keep the SYS_SYNC and SYS_PD signals active */
1373
  GPIO_ResetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1374
  GPIO_ResetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
1375

    
1376
  /* initialized the standalone timer */
1377
  saTimerInit();
1378

    
1379
  setLed(BLT_TRUE);
1380

    
1381
  /* wait for all boards to be ready for shutdown */
1382
  GPIO_SetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1383
  if (GPIO_ReadOutputDataBit(SYS_REG_EN_GPIO, SYS_REG_EN_PIN) == Bit_SET) {
1384
    // this must skipped if the pullup voltage (VIO3.3) is not active
1385
    setLed(BLT_TRUE);
1386
    waitForSignal(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN, Bit_SET);
1387
    setLed(BLT_FALSE);
1388
  }
1389

    
1390
  /* execute disambiguation procedure and signal all modules to enter hibernate mode */
1391
  if (shutdownDisambiguationProcedure(BL_SHUTDOWN_PRI_RSN_DEEPSLEEP) != SUCCESS) {
1392
    blinkSOS(1);
1393
    msleep(10);
1394
  }
1395

    
1396
  shutdownToHibernate();
1397

    
1398
  return;
1399
} /*** end of blCallbackShutdownHibernate ***/
1400

    
1401
/*
1402
 * Callback function that handles the system shutdown and initializes a restart.
1403
 * When called from a multithreaded environment, it must be ensured that no other thread will preempt this function.
1404
 * By configuration it is ensured, that the system will end up executing the handleSoftwareReset() function after reset.
1405
 */
1406
void blCallbackShutdownRestart(void) {
1407
  /* make sure that the required clocks are activated */
1408
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE);
1409
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
1410

    
1411
  /* set/keep the SYS_SYNC and SYS_PD signals active */
1412
  GPIO_ResetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1413
  GPIO_ResetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
1414

    
1415
  /* initialized the standalone timer */
1416
  saTimerInit();
1417

    
1418
  setLed(BLT_TRUE);
1419

    
1420
  /* deactivate SYS_PD_N and ensure that all modules had a chance to detect the falling edge */
1421
  msleep(1);
1422
  GPIO_SetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
1423
  msleep(1);
1424

    
1425
  /* wait for all boards to be ready for shutdown */
1426
  GPIO_SetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1427
  if (GPIO_ReadOutputDataBit(SYS_REG_EN_GPIO, SYS_REG_EN_PIN) == Bit_SET) {
1428
    // this must skipped if the pullup voltage (VIO3.3) is not active
1429
    setLed(BLT_TRUE);
1430
    waitForSignal(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN, Bit_SET);
1431
    setLed(BLT_FALSE);
1432
  }
1433

    
1434
  /* execute disambiguation procedure and signal all modules to restart normally */
1435
  if (shutdownDisambiguationProcedure(BL_SHUTDOWN_PRI_RSN_RESTART) != SUCCESS) {
1436
    blinkSOS(1);
1437
    msleep(10);
1438
  }
1439

    
1440
  /* restart the system */
1441
  shutdownAndRestart();
1442

    
1443
  return;
1444
} /*** end of blCallbackRestart ***/
1445

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

    
1456
  /* set/keep the SYS_SYNC and SYS_PD signals active */
1457
  GPIO_ResetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1458
  GPIO_ResetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
1459

    
1460
  /* initialized the standalone timer */
1461
  saTimerInit();
1462

    
1463
  setLed(BLT_TRUE);
1464

    
1465
  /* deactivate SYS_PD_N and ensure that all modules had a chance to detect the falling edge */
1466
  msleep(1);
1467
  GPIO_SetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
1468
  msleep(1);
1469

    
1470
  /* wait for all boards to be ready for shutdown */
1471
  GPIO_SetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1472
  if (GPIO_ReadOutputDataBit(SYS_REG_EN_GPIO, SYS_REG_EN_PIN) == Bit_SET) {
1473
    // this must be skipped if the pullup voltage (VIO3.3) is not active
1474
    setLed(BLT_TRUE);
1475
    waitForSignal(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN, Bit_SET);
1476
    setLed(BLT_FALSE);
1477
  }
1478

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

    
1482
  /* disambiguation procedure (passive) */
1483
  uint32_t pulse_counter = 0;
1484
  while (waitForSignalTimeout(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN, Bit_RESET, 10)) {
1485
    waitForSignal(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN, Bit_SET);
1486
    ++pulse_counter;
1487
  }
1488

    
1489
  /* evaluate and hanlde disambiguation result */
1490
  if (shutdown_nrestart == BLT_TRUE) {
1491
    /* shutdown request */
1492

    
1493
    /* handle special cases */
1494
    if (pulse_counter == BL_SHUTDOWN_PRI_RSN_UNKNOWN) {
1495
      /* no pulse at all was received */
1496
      pulse_counter = BL_SHUTDOWN_PRI_RSN_DEFAULT;
1497
    } else if (pulse_counter != BL_SHUTDOWN_PRI_RSN_HIBERNATE &&
1498
               pulse_counter != BL_SHUTDOWN_PRI_RSN_DEEPSLEEP &&
1499
               pulse_counter != BL_SHUTDOWN_PRI_RSN_TRANSPORT) {
1500
      /* invalid number of pulses received */
1501
      blinkSOS(1);
1502
      pulse_counter = BL_SHUTDOWN_PRI_RSN_DEFAULT;
1503
    }
1504

    
1505
    switch (pulse_counter) {
1506
      case BL_SHUTDOWN_PRI_RSN_HIBERNATE:
1507
        shutdownToHibernate();
1508
        break;
1509
      case BL_SHUTDOWN_PRI_RSN_DEEPSLEEP:
1510
        shutdownToDeepsleep();
1511
        break;
1512
      case BL_SHUTDOWN_PRI_RSN_TRANSPORT:
1513
        shutdownToTransportation();
1514
        break;
1515
    }
1516
  } else {
1517
    /* restart request */
1518

    
1519
    /* there is no ambiguity for restart, so it is ignored */
1520
    shutdownAndRestart();
1521
  }
1522

    
1523
  /* if this code is reached, the system did neither shut down, nor restart.
1524
   * This must never be the case!
1525
   */
1526
  blinkSOSinf();
1527
  return;
1528
} /*** end of blCallbackHandleShutdownRequest ***/
1529

    
1530
/*********************************** end of main.c *************************************/