Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (60.885 KB)

1 69661903 Thomas Schöpping
/************************************************************************************//**
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 470d0567 Thomas Schöpping
* 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 69661903 Thomas Schöpping
* proprietary components. The exception text is included at the bottom of the license
29
* file <license.html>.
30 470d0567 Thomas Schöpping
*
31 69661903 Thomas Schöpping
* \endinternal
32
****************************************************************************************/
33
34
/****************************************************************************************
35
* Include files
36
****************************************************************************************/
37
#include "boot.h"                                /* bootloader generic header          */
38
#include "stm32f4xx.h"                           /* STM32 registers                    */
39
#include "stm32f4xx_conf.h"                      /* STM32 peripheral drivers           */
40
#include "com.h"
41
#include "ARMCM4_STM32/types.h"
42 470d0567 Thomas Schöpping
#include "AMiRo/amiroblt.h"
43 69661903 Thomas Schöpping
#include "AMiRo/helper.h"
44
45
/****************************************************************************************
46
* Defines
47
****************************************************************************************/
48
#define WKUP_GPIO               GPIOA
49
#define WKUP_PIN                GPIO_Pin_0
50
#define SYS_UART_TX_GPIO        GPIOA
51
#define SYS_UART_TX_PIN         GPIO_Pin_2
52
#define SYS_UART_RX_GPIO        GPIOA
53
#define SYS_UART_RX_PIN         GPIO_Pin_3
54
#define SYS_SPI_SS0_N_GPIO      GPIOA
55
#define SYS_SPI_SS0_N_PIN       GPIO_Pin_4
56
#define SYS_SPI_SCLK_GPIO       GPIOA
57
#define SYS_SPI_SCLK_PIN        GPIO_Pin_5
58
#define SYS_SPI_MISO_GPIO       GPIOA
59
#define SYS_SPI_MISO_PIN        GPIO_Pin_6
60
#define SYS_SPI_MOSI_GPIO       GPIOA
61
#define SYS_SPI_MOSI_PIN        GPIO_Pin_7
62
#define SYS_REG_EN_GPIO         GPIOA
63
#define SYS_REG_EN_PIN          GPIO_Pin_8
64
#define PROG_RX_GPIO            GPIOA
65
#define PROG_RX_PIN             GPIO_Pin_9
66
#define PROG_TX_GPIO            GPIOA
67
#define PROG_TX_PIN             GPIO_Pin_10
68
#define CAN_RX_GPIO             GPIOA
69
#define CAN_RX_PIN              GPIO_Pin_11
70
#define CAN_TX_GPIO             GPIOA
71
#define CAN_TX_PIN              GPIO_Pin_12
72
#define SWDIO_GPIO              GPIOA
73
#define SWDIO_PIN               GPIO_Pin_13
74
#define SWCLK_GPIO              GPIOA
75
#define SWCLK_PIN               GPIO_Pin_14
76
#define SYS_SPI_SS1_N_GPIO      GPIOA
77
#define SYS_SPI_SS1_N_PIN       GPIO_Pin_15
78
79
#define IR_INT1_N_GPIO          GPIOB
80
#define IR_INT1_N_PIN           GPIO_Pin_0
81
#define VSYS_SENSE_GPIO         GPIOB
82
#define VSYS_SENSE_PIN          GPIO_Pin_1
83
#define POWER_EN_GPIO           GPIOB
84
#define POWER_EN_PIN            GPIO_Pin_2
85
#define SYS_UART_DN_GPIO        GPIOB
86
#define SYS_UART_DN_PIN         GPIO_Pin_3
87
#define CHARGE_STAT2A_GPIO      GPIOB
88
#define CHARGE_STAT2A_PIN       GPIO_Pin_4
89
#define BUZZER_GPIO             GPIOB
90
#define BUZZER_PIN              GPIO_Pin_5
91
#define GAUGE_BATLOW2_GPIO      GPIOB
92
#define GAUGE_BATLOW2_PIN       GPIO_Pin_6
93
#define GAUGE_BATGD2_N_GPIO     GPIOB
94
#define GAUGE_BATGD2_N_PIN      GPIO_Pin_7
95
#define GAUGE_SCL2_GPIO         GPIOB
96
#define GAUGE_SCL2_PIN          GPIO_Pin_8
97
#define GAUGE_SDA2_GPIO         GPIOB
98
#define GAUGE_SDA2_PIN          GPIO_Pin_9
99
#define GAUGE_SCL1_GPIO         GPIOB
100
#define GAUGE_SCL1_PIN          GPIO_Pin_10
101
#define GAUGE_SDA1_GPIO         GPIOB
102
#define GAUGE_SDA1_PIN          GPIO_Pin_11
103
#define LED_GPIO                GPIOB
104
#define LED_PIN                 GPIO_Pin_12
105
#define BT_RTS_GPIO             GPIOB
106
#define BT_RTS_PIN              GPIO_Pin_13
107
#define BT_CTS_GPIO             GPIOB
108
#define BT_CTS_PIN              GPIO_Pin_14
109
#define SYS_UART_UP_GPIO        GPIOB
110
#define SYS_UART_UP_PIN         GPIO_Pin_15
111
112
#define CHARGE_STAT1A_GPIO      GPIOC
113
#define CHARGE_STAT1A_PIN       GPIO_Pin_0
114
#define GAUGE_BATLOW1_GPIO      GPIOC
115
#define GAUGE_BATLOW1_PIN       GPIO_Pin_1
116
#define GAUGE_BATGD1_N_GPIO     GPIOC
117
#define GAUGE_BATGD1_N_PIN      GPIO_Pin_2
118
#define CHARGE_EN1_N_GPIO       GPIOC
119
#define CHARGE_EN1_N_PIN        GPIO_Pin_3
120
#define IR_INT2_N_GPIO          GPIOC
121
#define IR_INT2_N_PIN           GPIO_Pin_4
122
#define TOUCH_INT_N_GPIO        GPIOC
123
#define TOUCH_INT_N_PIN         GPIO_Pin_5
124
#define SYS_DONE_GPIO           GPIOC
125
#define SYS_DONE_PIN            GPIO_Pin_6
126
#define SYS_PROG_N_GPIO         GPIOC
127
#define SYS_PROG_N_PIN          GPIO_Pin_7
128
#define PATH_DC_GPIO            GPIOC
129
#define PATH_DC_PIN             GPIO_Pin_8
130
#define SYS_SPI_DIR_GPIO        GPIOC
131
#define SYS_SPI_DIR_PIN         GPIO_Pin_9
132
#define BT_RX_GPIO              GPIOC
133
#define BT_RX_PIN               GPIO_Pin_10
134
#define BT_TX_GPIO              GPIOC
135
#define BT_TX_PIN               GPIO_Pin_11
136
#define SYS_SYNC_N_GPIO         GPIOC
137
#define SYS_SYNC_N_PIN          GPIO_Pin_12
138
#define SYS_PD_N_GPIO           GPIOC
139
#define SYS_PD_N_PIN            GPIO_Pin_13
140
#define SYS_WARMRST_N_GPIO      GPIOC
141
#define SYS_WARMRST_N_PIN       GPIO_Pin_14
142
#define BT_RST_GPIO             GPIOC
143
#define BT_RST_PIN              GPIO_Pin_15
144
145
#define OSC_IN_GPIO             GPIOD
146
#define OSC_IN_PIN              GPIO_Pin_0
147
#define OSC_OUT_GPIO            GPIOD
148
#define OSC_OUT_PIN             GPIO_Pin_1
149
#define CHARGE_EN2_N_GPIO       GPIOD
150
#define CHARGE_EN2_N_PIN        GPIO_Pin_2
151
152
#define HIBERNATE_TIME_MS       5000
153
154
/****************************************************************************************
155
* Function prototypes and static variables
156
****************************************************************************************/
157
static void Init(void);
158
159
static void initGpio();
160
static void initExti();
161
void configGpioForShutdown();
162
void systemPowerDown();
163
164
ErrorStatus handleColdReset();
165
ErrorStatus handleSoftwareReset();
166
ErrorStatus handleUartDnWakeup();
167
ErrorStatus handlePathDcWakeup();
168
ErrorStatus handleTouchWakeup();
169
ErrorStatus handleIwdgWakeup();
170
171
static void indicateHibernate();
172
static void AdcSingleMeasurement();
173
174
ADC_TypeDef* setupADC(ADC_TypeDef* adc, const uint16_t low_th, const uint16_t high_th);
175
uint16_t configIwdg(const uint16_t ms);
176
177
ErrorStatus shutdownDisambiguationProcedure(const uint8_t type);
178
void shutdownToTransportation();
179
void shutdownToDeepsleep();
180
void shutdownToHibernate();
181
void shutdownAndRestart();
182
183 470d0567 Thomas Schöpping
volatile blBackupRegister_t backup_reg;
184 69661903 Thomas Schöpping
185
/****************************************************************************************
186
* Callback configuration
187
****************************************************************************************/
188
void blCallbackShutdownTransportation(void);
189
void blCallbackShutdownDeepsleep(void);
190
void blCallbackShutdownHibernate(void);
191
void blCallbackShutdownRestart(void);
192
void blCallbackHandleShutdownRequest(void);
193
194 470d0567 Thomas Schöpping
const blCallbackTable_t cbtable __attribute__ ((section ("_callback_table"))) = {
195 69661903 Thomas Schöpping
  .magicNumber = BL_MAGIC_NUMBER,
196 f4758731 Thomas Schöpping
  .vBootloader = {BL_VERSION_ID_AMiRoBLT_Release, BL_VERSION_MAJOR, BL_VERSION_MINOR, 1},
197 470d0567 Thomas Schöpping
  .vSSSP = {BL_VERSION_ID_SSSP, SSSP_VERSION_MAJOR, SSSP_VERSION_MINOR, 0},
198
  .vCompiler = {BL_VERSION_ID_GCC, __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__},  // currently only GCC is supported
199 69661903 Thomas Schöpping
  .cbShutdownHibernate = blCallbackShutdownHibernate,
200
  .cbShutdownDeepsleep = blCallbackShutdownDeepsleep,
201
  .cbShutdownTransportation = blCallbackShutdownTransportation,
202
  .cbShutdownRestart = blCallbackShutdownRestart,
203
  .cbHandleShutdownRequest = blCallbackHandleShutdownRequest,
204
  .cb5 = (void*)0,
205
  .cb6 = (void*)0,
206
  .cb7 = (void*)0,
207
  .cb8 = (void*)0,
208
  .cb9 = (void*)0,
209
  .cb10 = (void*)0,
210
  .cb11 = (void*)0
211
};
212
213
/************************************************************************************//**
214 470d0567 Thomas Schöpping
** \brief     This is the entry point for the bootloader application and is called
215 69661903 Thomas Schöpping
**            by the reset interrupt vector after the C-startup routines executed.
216
** \return    none.
217
**
218
****************************************************************************************/
219
void main(void)
220
{
221
  /* initialize the microcontroller */
222
  Init();
223
224
  /* activate some required clocks */
225
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE);
226
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
227
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
228
229
  /* initialize GPIOs and EXTI lines */
230
  initGpio();
231
  setLed(BLT_TRUE);
232
  initExti();
233
234
  /* initialize the timer */
235
  TimerInit(); // do not use saTimerInit() in order to initialize the static variable.
236
237
  /* read the backup register */
238
  backup_reg.raw = RTC_ReadBackupRegister(BL_RTC_BACKUP_REG);
239
240
  /* detect the primary reason for this wakeup/restart */
241
  backup_reg.wakeup_pri_reason =
242
      ((RCC_GetFlagStatus(RCC_FLAG_LPWRRST) == SET) ? BL_WAKEUP_PRI_RSN_LPWRRST : 0) |
243
      ((RCC_GetFlagStatus(RCC_FLAG_WWDGRST) == SET) ? BL_WAKEUP_PRI_RSN_WWDGRST : 0) |
244
      ((RCC_GetFlagStatus(RCC_FLAG_IWDGRST) == SET) ? BL_WAKEUP_PRI_RSN_IWDGRST : 0) |
245
      ((RCC_GetFlagStatus(RCC_FLAG_SFTRST) == SET) ? BL_WAKEUP_PRI_RSN_SFTRST : 0)   |
246
      ((RCC_GetFlagStatus(RCC_FLAG_PORRST) == SET) ? BL_WAKEUP_PRI_RSN_PORRST : 0)   |
247
      ((RCC_GetFlagStatus(RCC_FLAG_PINRST) == SET) ? BL_WAKEUP_PRI_RSN_PINRST : 0)   |
248
      ((RCC_GetFlagStatus(RCC_FLAG_BORRST) == SET) ? BL_WAKEUP_PRI_RSN_BORRST : 0)   |
249
      ((PWR_GetFlagStatus(PWR_FLAG_WU) == SET) ? BL_WAKEUP_PRI_RSN_WKUP : 0);
250
251
  /* when woken from standby mode, detect the secondary reason for this wakeup/reset */
252
  if ( (backup_reg.wakeup_pri_reason & BL_WAKEUP_PRI_RSN_WKUP) && (PWR_GetFlagStatus(PWR_FLAG_SB) == SET) ) {
253
    if (GPIO_ReadInputDataBit(SYS_UART_DN_GPIO, SYS_UART_DN_PIN) == Bit_RESET) {
254
      backup_reg.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_UART;
255
    } else if (GPIO_ReadInputDataBit(PATH_DC_GPIO, PATH_DC_PIN) == Bit_SET) {
256
      backup_reg.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_PWRPLUG;
257
    } else {
258
      backup_reg.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_TOUCH;
259
    }
260
  } else {
261
    backup_reg.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_UNKNOWN;
262
  }
263
264
  /* store the information about this wakeup/restart in the backup register */
265
  PWR_BackupAccessCmd(ENABLE);
266
  RTC_WriteBackupRegister(BL_RTC_BACKUP_REG, backup_reg.raw);
267
268
  /* clear the flags */
269
  RCC_ClearFlag();
270
  PWR_ClearFlag(PWR_FLAG_WU);
271
272
  setLed(BLT_FALSE);
273
274
  /* handle different wakeup/reset reasons */
275
  ErrorStatus status = ERROR;
276
  if (backup_reg.wakeup_pri_reason & BL_WAKEUP_PRI_RSN_SFTRST) {
277
    /* system was reset by software */
278
    status = handleSoftwareReset();
279
  } else if (backup_reg.wakeup_pri_reason & BL_WAKEUP_PRI_RSN_WKUP) {
280
    /* system was woken via WKUP pin */
281
    /* differeciate between thre wakeup types */
282
    switch (backup_reg.wakeup_sec_reason) {
283
      case BL_WAKEUP_SEC_RSN_UART:
284
        status = handleUartDnWakeup();
285
        break;
286
      case BL_WAKEUP_SEC_RSN_PWRPLUG:
287
        status = handlePathDcWakeup();
288
        break;
289
      case BL_WAKEUP_SEC_RSN_TOUCH:
290
        status = handleTouchWakeup();
291
        break;
292
      default:
293
        status = ERROR;
294
        break;
295
    }
296
  } else if (backup_reg.wakeup_pri_reason & BL_WAKEUP_PRI_RSN_IWDGRST) {
297
    /* system was woken by IWDG */
298
    status = handleIwdgWakeup();
299
  } else if (backup_reg.wakeup_pri_reason == BL_WAKEUP_PRI_RSN_PINRST) {
300
    /* system was reset via NRST pin */
301
    status = handleColdReset();
302
  } else {
303
    /* system was woken/reset for an unexpected reason.
304
     * In this case the LED blinks "SOS" (... --- ...) and the system resets.
305
     */
306
    blinkSOS(1);
307
    status = ERROR;
308
    backup_reg.shutdown_pri_reason = BL_SHUTDOWN_PRI_RSN_RESTART;
309
    backup_reg.shutdown_sec_reason = BL_SHUTDOWN_SEC_RSN_UNKNOWN;
310
    RTC_WriteBackupRegister(BL_RTC_BACKUP_REG, backup_reg.raw);
311
    NVIC_SystemReset();
312
  }
313
314
  /* if something went wrong, signal this failure */
315
  if (status != SUCCESS) {
316
    blinkSOSinf();
317
  }
318
319
  return;
320
} /*** end of main ***/
321
322
323
/************************************************************************************//**
324 470d0567 Thomas Schöpping
** \brief     Initializes the microcontroller.
325 69661903 Thomas Schöpping
** \return    none.
326
**
327
****************************************************************************************/
328
static void Init(void)
329
{
330
#if (BOOT_COM_UART_ENABLE > 0 || BOOT_GATE_UART_ENABLE > 0)
331
  GPIO_InitTypeDef  GPIO_InitStructure;
332
#elif (BOOT_FILE_SYS_ENABLE > 0)
333
  GPIO_InitTypeDef  GPIO_InitStructure;
334
  USART_InitTypeDef USART_InitStructure;
335
#elif (BOOT_COM_CAN_ENABLE > 0 || BOOT_GATE_CAN_ENABLE > 0)
336
  GPIO_InitTypeDef  GPIO_InitStructure;
337 470d0567 Thomas Schöpping
#endif
338 69661903 Thomas Schöpping
339
  /* initialize the system and its clocks */
340
  SystemInit();
341
#if (BOOT_COM_UART_ENABLE > 0 || BOOT_GATE_UART_ENABLE > 0)
342
  /* enable UART peripheral clock */
343
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
344
  /* enable GPIO peripheral clock for transmitter and receiver pins */
345
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
346
  /* connect the pin to the peripherals alternate function */
347
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
348
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
349
  /* configure USART Tx as alternate function  */
350
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
351
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
352
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
353
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
354
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
355
  GPIO_Init(GPIOA, &GPIO_InitStructure);
356
  /* configure USART Rx as alternate function */
357
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
358
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
359
  GPIO_Init(GPIOA, &GPIO_InitStructure);
360
#endif
361
362
#if (BOOT_COM_BLUETOOTH_UART_ENABLE > 0)
363
  /* enable UART peripheral clock */
364
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
365
366
  /* enable GPIO peripheral clock for transmitter and receiver pins */
367
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
368
  /* connect the pin to the peripherals alternate function */
369
  GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_USART3);
370
  GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_USART3);
371
  /* configure USART Tx as alternate function  */
372
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
373
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
374
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
375
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
376
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
377
  GPIO_Init(GPIOC, &GPIO_InitStructure);
378
  /* configure USART Rx as alternate function */
379
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
380
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
381
  GPIO_Init(GPIOC, &GPIO_InitStructure);
382
383
  /* Configure Bluetooth reset pin */
384
  GPIO_InitTypeDef  gpio_init;
385
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
386
  gpio_init.GPIO_Pin   = BT_RST_PIN;
387
  gpio_init.GPIO_OType = GPIO_OType_OD;
388
  gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL;
389
  gpio_init.GPIO_Mode = GPIO_Mode_OUT;
390
  gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
391
  GPIO_Init(BT_RST_GPIO, &gpio_init);
392
  /* Reset Bluetooth reset pin */
393
  GPIO_ResetBits(BT_RST_GPIO, BT_RST_PIN);
394
#endif
395
396
397
#if (BOOT_COM_CAN_ENABLE > 0 || BOOT_GATE_CAN_ENABLE > 0)
398
  /* enable clocks for CAN transmitter and receiver pins */
399
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
400
  /* select alternate function for the CAN pins */
401
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource11, GPIO_AF_CAN1);
402 470d0567 Thomas Schöpping
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource12, GPIO_AF_CAN1);
403 69661903 Thomas Schöpping
  /* configure CAN RX and TX pins */
404
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
405
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
406
  GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
407
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
408
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
409
  GPIO_Init(GPIOA, &GPIO_InitStructure);
410
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
411
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
412
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
413
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
414
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
415
  GPIO_Init(GPIOA, &GPIO_InitStructure);
416 470d0567 Thomas Schöpping
#endif
417 69661903 Thomas Schöpping
418
} /*** end of Init ***/
419
420
/*
421
 * Initializes all GPIO used by the bootloader
422
 */
423
static void initGpio() {
424
  GPIO_InitTypeDef gpio_init;
425
426
  /*
427
   * OUTPUTS
428
   */
429
430
  /* initialize LED and push it up (inactive) */
431
  GPIO_SetBits(LED_GPIO, LED_PIN);
432
  gpio_init.GPIO_Pin    = LED_PIN;
433
  gpio_init.GPIO_Mode   = GPIO_Mode_OUT;
434
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
435
  gpio_init.GPIO_OType  = GPIO_OType_PP;
436
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
437
  GPIO_Init(LED_GPIO, &gpio_init);
438
439
  /* initialize SYS_PD_N and push it up (inactive) */
440
  GPIO_SetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
441
  gpio_init.GPIO_Pin    = SYS_PD_N_PIN;
442
  gpio_init.GPIO_Mode   = GPIO_Mode_OUT;
443
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
444
  gpio_init.GPIO_OType  = GPIO_OType_OD;
445
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
446
  GPIO_Init(SYS_PD_N_GPIO, &gpio_init);
447
448
  /* initialize SYS_SYNC_N and pull it down (active) */
449
  GPIO_ResetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
450
  gpio_init.GPIO_Pin    = SYS_SYNC_N_PIN;
451
  gpio_init.GPIO_Mode   = GPIO_Mode_OUT;
452
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
453
  gpio_init.GPIO_OType  = GPIO_OType_OD;
454
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
455
  GPIO_Init(SYS_SYNC_N_GPIO, &gpio_init);
456
457
  /* initialize SYS_WARMRST_N and pull it down (active) */
458
  GPIO_ResetBits(SYS_WARMRST_N_GPIO, SYS_WARMRST_N_PIN);
459
  gpio_init.GPIO_Pin    = SYS_WARMRST_N_PIN;
460
  gpio_init.GPIO_Mode   = GPIO_Mode_OUT;
461
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
462
  gpio_init.GPIO_OType  = GPIO_OType_OD;
463
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
464
  GPIO_Init(SYS_WARMRST_N_GPIO, &gpio_init);
465
466
  /* initialize SYS_UART_DN and push it up (inactive) */
467
  GPIO_SetBits(SYS_UART_DN_GPIO, SYS_UART_DN_PIN);
468
  gpio_init.GPIO_Pin    = SYS_UART_DN_PIN;
469
  gpio_init.GPIO_Mode   = GPIO_Mode_OUT;
470
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
471
  gpio_init.GPIO_OType  = GPIO_OType_OD;
472
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
473
  GPIO_Init(SYS_UART_DN_GPIO, &gpio_init);
474
475
  /* initialize POWER_EN and pull it down (inactive) */
476
  GPIO_ResetBits(POWER_EN_GPIO, POWER_EN_PIN);
477
  gpio_init.GPIO_Pin    = POWER_EN_PIN;
478
  gpio_init.GPIO_Mode   = GPIO_Mode_OUT;
479
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
480
  gpio_init.GPIO_OType  = GPIO_OType_PP;
481
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
482
  GPIO_Init(POWER_EN_GPIO, &gpio_init);
483
484
  /* initialize SYS_REG_EN and pull it down (inactive) */
485
  GPIO_ResetBits(SYS_REG_EN_GPIO, SYS_REG_EN_PIN);
486
  gpio_init.GPIO_Pin    = SYS_REG_EN_PIN;
487
  gpio_init.GPIO_Mode   = GPIO_Mode_OUT;
488
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
489
  gpio_init.GPIO_OType  = GPIO_OType_PP;
490
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
491
  GPIO_Init(SYS_REG_EN_GPIO, &gpio_init);
492
493
  /* initialize CHARGE_EN1_N and CHARGE_EN2_N and push them up (inactive) */
494
  GPIO_SetBits(CHARGE_EN1_N_GPIO, CHARGE_EN1_N_PIN);
495
  GPIO_SetBits(CHARGE_EN2_N_GPIO, CHARGE_EN2_N_PIN);
496
  gpio_init.GPIO_Pin    = CHARGE_EN1_N_PIN;
497
  gpio_init.GPIO_Mode   = GPIO_Mode_OUT;
498
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
499
  gpio_init.GPIO_OType  = GPIO_OType_PP;
500
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
501
  GPIO_Init(CHARGE_EN1_N_GPIO, &gpio_init);
502
  gpio_init.GPIO_Pin    = CHARGE_EN2_N_PIN;
503
  GPIO_Init(CHARGE_EN2_N_GPIO, &gpio_init);
504
505
  /*
506
   * INPUTS
507
   */
508
509
  /* initialize PATH_DC */
510
  gpio_init.GPIO_Pin    = PATH_DC_PIN;
511
  gpio_init.GPIO_Mode   = GPIO_Mode_IN;
512
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
513
  gpio_init.GPIO_OType  = GPIO_OType_PP;
514
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
515
  GPIO_Init(PATH_DC_GPIO, &gpio_init);
516
517
  /* initialize TOUCH_INT_N */
518
  gpio_init.GPIO_Pin    = TOUCH_INT_N_PIN;
519
  gpio_init.GPIO_Mode   = GPIO_Mode_IN;
520
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
521
  gpio_init.GPIO_OType  = GPIO_OType_PP;
522
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
523
  GPIO_Init(TOUCH_INT_N_GPIO, &gpio_init);
524
525
  /* initialize VSYS_SENSE as analog input */
526
  gpio_init.GPIO_Pin    = VSYS_SENSE_PIN;
527
  gpio_init.GPIO_Mode   = GPIO_Mode_AN;
528
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
529
  gpio_init.GPIO_OType  = GPIO_OType_PP;
530
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
531
  GPIO_Init(VSYS_SENSE_GPIO, &gpio_init);
532
533
  /* initialize GPIOB4, since it is configured in alternate function mode on reset */
534
  gpio_init.GPIO_Pin    = CHARGE_STAT2A_PIN;
535
  gpio_init.GPIO_Mode   = GPIO_Mode_IN;
536
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
537
  gpio_init.GPIO_OType  = GPIO_OType_PP;
538
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
539
  GPIO_Init(CHARGE_STAT2A_GPIO, &gpio_init);
540
541
  return;
542
} /*** end of initGpio ***/
543
544
/*
545
 * Initialize all EXTI lines
546
 */
547
static void initExti() {
548
  /* configure EXTI lines */
549
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource0); // IR_INT1_N
550
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource0); // CHARGE_STAT1A
551
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource1); // GAUGE_BATLOW1
552
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource2); // GAUGE_BATGD1_N
553
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource3); // SYS_UART_DN
554
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource4); // CHARGE_STAT2A
555
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource4); // IR_INT2_N
556
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource5); // TOUCH_INT_N
557
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource6); // GAUGE_BATLOW2
558
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource7); // GAUGE_BATGD2_N
559
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource8); // PATH_DC
560
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource9); // SYS_SPI_DIR
561
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource12); // SYS_SYNC_N
562
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource13); // SYS_PD_N
563
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource14); // SYS_WARMRST_N
564
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource15); // SYS_UART_UP
565
566
  return;
567
} /*** end of initExti ***/
568
569
/*
570
 * Signals, which type of low-power mode the system shall enter after the shutdown sequence.
571
 */
572
ErrorStatus shutdownDisambiguationProcedure(const uint8_t type) {
573
  GPIO_SetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
574
  ErrorStatus ret_val = ERROR;
575
576
  switch (type) {
577
    case BL_SHUTDOWN_PRI_RSN_UNKNOWN:
578
    case BL_SHUTDOWN_PRI_RSN_HIBERNATE:
579
    case BL_SHUTDOWN_PRI_RSN_DEEPSLEEP:
580
    case BL_SHUTDOWN_PRI_RSN_TRANSPORT:
581
    {
582
      // broadcast a number of pulses, depending on the argument
583
      uint8_t pulse_counter = 0;
584
      for (pulse_counter = 0; pulse_counter < type; ++pulse_counter) {
585
        msleep(1);
586
        GPIO_ResetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
587
        msleep(1);
588
        GPIO_SetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
589
      }
590
      // wait for timeout
591
      msleep(10);
592
      ret_val = SUCCESS;
593
      break;
594
    }
595
    case BL_SHUTDOWN_PRI_RSN_RESTART:
596
    {
597
      // since there is no ambiguity for restart requests, no pulses are generated
598
      msleep(10);
599
      ret_val = SUCCESS;
600
      break;
601
    }
602
    default:
603
      ret_val = ERROR;
604
      break;
605
  }
606
607
  return ret_val;
608
} /*** end of shutdownDisambiguationProcedure ***/
609
610
/*
611
 * Final shutdown of the system to enter transportation mode.
612
 */
613
void shutdownToTransportation() {
614
  /* configure some criticpal GPIOs as input
615
   * This is required, because otherwise some hardware might be powered through these signals */
616
  configGpioForShutdown();
617
618
  /* power down the system */
619
  systemPowerDown();
620
621
  /* deactivate the WKUP pin */
622
  PWR_WakeUpPinCmd(DISABLE);
623
624
  /* deactivate any RTC related events */
625
  RTC_WakeUpCmd(DISABLE);
626
  RTC_TamperCmd(RTC_Tamper_1, DISABLE);
627
  RTC_TimeStampCmd(RTC_TimeStampEdge_Rising, DISABLE);
628
  RTC_TimeStampCmd(RTC_TimeStampEdge_Falling, DISABLE);
629
  RTC_ClearFlag(~0);
630
631
  /* disable the IWDG */
632
  IWDG_ReloadCounter();
633
634
  /* write some information to the backup register */
635 470d0567 Thomas Schöpping
  blBackupRegister_t backup;
636 69661903 Thomas Schöpping
  backup.shutdown_pri_reason = BL_SHUTDOWN_PRI_RSN_TRANSPORT;
637
  backup.shutdown_sec_reason = BL_SHUTDOWN_SEC_RSN_UNKNOWN;
638
  backup.wakeup_pri_reason = BL_WAKEUP_PRI_RSN_UNKNOWN;
639
  backup.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_UNKNOWN;
640
  PWR_BackupAccessCmd(ENABLE);
641
  RTC_WriteBackupRegister(BL_RTC_BACKUP_REG, backup.raw);
642
643
  /* morse 'OK' via the LED to signal that shutdown was successful */
644
  blinkOK(1);
645
646
  /* enter standby mode */
647
  PWR_EnterSTANDBYMode();
648
649
  return;
650
} /*** end of shutdownToTransportation ***/
651
652
/*
653
 * Final shutdown of the system to enter deepseleep mode.
654
 */
655
void shutdownToDeepsleep() {
656
  /* configure some criticpal GPIOs as input
657
   * This is required, because otherwise some hardware might be powered through these signals */
658
  configGpioForShutdown();
659
660
  /* power down the system */
661
  systemPowerDown();
662
663
  /* activate the WKUP pin */
664
  PWR_WakeUpPinCmd(ENABLE);
665
666
  /*
667
   * Configuration of RTC and IWDG belongs to the OS.
668
   */
669
670
  /* write some information to the backup register */
671 470d0567 Thomas Schöpping
  blBackupRegister_t backup;
672 69661903 Thomas Schöpping
  backup.shutdown_pri_reason = BL_SHUTDOWN_PRI_RSN_DEEPSLEEP;
673
  backup.shutdown_sec_reason = BL_SHUTDOWN_SEC_RSN_UNKNOWN;
674
  backup.wakeup_pri_reason = BL_WAKEUP_PRI_RSN_UNKNOWN;
675
  backup.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_UNKNOWN;
676
  PWR_BackupAccessCmd(ENABLE);
677
  RTC_WriteBackupRegister(BL_RTC_BACKUP_REG, backup.raw);
678
679
  /* morse 'OK' via the LED to signal that shutdown was successful */
680
  blinkOK(1);
681
682
  /* enter standby mode or restart the system in case a power plug is already present */
683
  if (GPIO_ReadInputDataBit(PATH_DC_GPIO, PATH_DC_PIN) != Bit_SET) {
684
    PWR_EnterSTANDBYMode();
685
  } else {
686
    NVIC_SystemReset();
687
  }
688
689
  return;
690
} /*** end of shutdownToDeepsleep ***/
691
692
/*
693
 * Final shutdown of the system to enter hibernate mode.
694
 */
695
void shutdownToHibernate() {
696
  /* configure some criticpal GPIOs as input
697
   * This is required, because otherwise some hardware might be powered through these signals */
698
  configGpioForShutdown();
699
700
  /* power down the system */
701
  systemPowerDown();
702
703
  /* write some information to the backup register */
704 470d0567 Thomas Schöpping
  blBackupRegister_t backup;
705 69661903 Thomas Schöpping
  backup.shutdown_pri_reason = BL_SHUTDOWN_PRI_RSN_HIBERNATE;
706
  backup.shutdown_sec_reason = BL_SHUTDOWN_SEC_RSN_UNKNOWN;
707
  backup.wakeup_pri_reason = BL_WAKEUP_PRI_RSN_UNKNOWN;
708
  backup.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_UNKNOWN;
709
  PWR_BackupAccessCmd(ENABLE);
710
  RTC_WriteBackupRegister(BL_RTC_BACKUP_REG, backup.raw);
711
712
  /* morse 'OK' via the LED to signal that shutodnw was successful */
713
  blinkOK(1);
714
715
  /* reset the MCU */
716
  NVIC_SystemReset();
717
718
  return;
719
} /*** end of shutdownToHibernate ***/
720
721
/*
722
 * Final shutdown of the system and restart.
723
 */
724
void shutdownAndRestart() {
725
  /* configure some criticpal GPIOs as input
726
   * This is required, because otherwise some hardware might be powered through these signals */
727
  configGpioForShutdown();
728
729
  /* power down the system */
730
  systemPowerDown();
731
732
  /* write some information to the backup register */
733 470d0567 Thomas Schöpping
  blBackupRegister_t backup;
734 69661903 Thomas Schöpping
  backup.shutdown_pri_reason = BL_SHUTDOWN_PRI_RSN_RESTART;
735
  backup.shutdown_sec_reason = BL_SHUTDOWN_SEC_RSN_UNKNOWN;
736
  backup.wakeup_pri_reason = BL_WAKEUP_PRI_RSN_UNKNOWN;
737
  backup.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_UNKNOWN;
738
  PWR_BackupAccessCmd(ENABLE);
739
  RTC_WriteBackupRegister(BL_RTC_BACKUP_REG, backup.raw);
740
741
  /* morse 'OK' via the LED to signal that shutodnw was successful */
742
  blinkOK(1);
743
744
  /* reset the MCU */
745
  NVIC_SystemReset();
746
747
  return;
748
} /*** end of shutdownAndRestart ***/
749
750
/*
751
 * Configures some GPIO pins as inputs for safety reasons.
752
 * Under certain circumstances, these pins might power hardware that is supposed to be shut down.
753
 */
754
void configGpioForShutdown() {
755
  /* setup the configuration */
756
  GPIO_InitTypeDef  gpio_init;
757
  gpio_init.GPIO_Mode   = GPIO_Mode_IN;
758
  gpio_init.GPIO_Speed  = GPIO_Speed_50MHz;
759
  gpio_init.GPIO_OType  = GPIO_OType_PP;
760
  gpio_init.GPIO_PuPd   = GPIO_PuPd_NOPULL;
761
762
  /* configure SYS_UART_TX */
763
  gpio_init.GPIO_Pin = SYS_UART_TX_PIN;
764
  GPIO_Init(SYS_UART_TX_GPIO, &gpio_init);
765
766
  /* configure all SYS_SPI signals */
767
  gpio_init.GPIO_Pin = SYS_SPI_SS0_N_PIN;
768
  GPIO_Init(SYS_SPI_SS0_N_GPIO, &gpio_init);
769
  gpio_init.GPIO_Pin = SYS_SPI_SCLK_PIN;
770
  GPIO_Init(SYS_SPI_SCLK_GPIO, &gpio_init);
771
  gpio_init.GPIO_Pin = SYS_SPI_MISO_PIN;
772
  GPIO_Init(SYS_SPI_MISO_GPIO, &gpio_init);
773
  gpio_init.GPIO_Pin = SYS_SPI_MOSI_PIN;
774
  GPIO_Init(SYS_SPI_MOSI_GPIO, &gpio_init);
775
  gpio_init.GPIO_Pin = SYS_SPI_SS1_N_PIN;
776
  GPIO_Init(SYS_SPI_SS1_N_GPIO, &gpio_init);
777
  gpio_init.GPIO_Pin = SYS_SPI_DIR_PIN;
778
  GPIO_Init(SYS_SPI_DIR_GPIO, &gpio_init);
779
780
  /* configure CAN_TX */
781
  gpio_init.GPIO_Pin = CAN_TX_PIN;
782
  GPIO_Init(CAN_TX_GPIO, &gpio_init);
783
784
  /* configure all Bluetooth signals */
785
  gpio_init.GPIO_Pin = BT_CTS_PIN;
786
  GPIO_Init(BT_CTS_GPIO, &gpio_init);
787
  gpio_init.GPIO_Pin = BT_RX_PIN;
788
  GPIO_Init(BT_RX_GPIO, &gpio_init);
789
790
  return;
791
} /*** end of configGpioForShutdown ***/
792
793
/*
794
 * Disables all regulated voltages and finally cuts power to the rest of the system.
795
 */
796
void systemPowerDown() {
797
  setLed(BLT_TRUE);
798
799
  /* make sure that all other modules are shut down */
800
  msleep(10);
801
802
  /* reset slave modules */
803
  GPIO_ResetBits(SYS_WARMRST_N_GPIO, SYS_WARMRST_N_PIN);
804
805
  /* disable voltage regulators */
806
  GPIO_ResetBits(SYS_REG_EN_GPIO, SYS_REG_EN_PIN);
807
808
  /* cut power */
809
  GPIO_ResetBits(POWER_EN_GPIO, POWER_EN_PIN);
810
811
  /* make sure, all capacitors are discharged */
812
  msleep(100);
813
814
  setLed(BLT_FALSE);
815
816
  return;
817
} /*** end of systemPowerDown ***/
818
819
/*
820
 * Cofigures the independent watchdog (IWDG) to fire after the specified time when it is enabled.
821
 * The argument is the requested time in milliseconds.
822
 * The time that was actually set for the IWDG is returned by the function (again in milliseconds).
823
 * In some cases the returned value might differ from the requested one, but if so, it will alwyas be smaller.
824
 * Although the IWDG provides higher resolutions than milliseconds, these are not supported by this function.
825
 */
826
uint16_t configIwdg(const uint16_t ms) {
827
  /* apply an upper bound to the ms argument */
828
  uint16_t ms_capped = (ms >= 0x8000) ? 0x7FFF : ms;
829
830
  /* detect the best fitting prescaler and compute the according reload value */
831
  uint8_t prescaler = 0;
832
  uint16_t reload_val = 0;
833
  if (ms_capped >= 0x4000) {
834
    prescaler = IWDG_Prescaler_256;
835
    reload_val = ms_capped >> 3;  // note: this corresponds to a floor function
836
    ms_capped = reload_val << 3;  // this applies the floor function to ms_capped
837
  } else if (ms_capped >= 0x2000) {
838
    prescaler = IWDG_Prescaler_128;
839
    reload_val = ms_capped >> 2;  // note: this corresponds to a floor function
840
    ms_capped = reload_val << 2;  // this applies the floor function to ms_capped
841
  } else if (ms_capped >= 0x1000) {
842
    ms_capped &= ~(0x0001);
843
    prescaler = IWDG_Prescaler_64;
844
    reload_val = ms_capped >> 1;  // note: this corresponds to a floor function
845
    ms_capped = reload_val << 1;  // this applies the floor function to ms_capped
846
  } else {
847
    prescaler = IWDG_Prescaler_32;
848
    reload_val = ms_capped;
849
  }
850
851
  /* configure the IWDG */
852
  if (reload_val > 0) {
853
    IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
854
    IWDG_SetPrescaler(prescaler);
855
    IWDG_SetReload(reload_val);
856
    IWDG_WriteAccessCmd(IWDG_WriteAccess_Disable);
857
  }
858
859
  return ms_capped;
860
} /*** end of configIWDG ***/
861
862
/*
863
 * System was reset via the NRST pin or the reason could not be detected.
864
 * In this case, everything is started up.
865
 * If an attempt for an OS update is detected, flashing mode is entered.
866
 * Otherwise, the system will boot the OS.
867
 */
868
ErrorStatus handleColdReset() {
869
  /* activate system power and wait some time to ensure stable voltages */
870
  setLed(BLT_TRUE);
871
  GPIO_SetBits(POWER_EN_GPIO, POWER_EN_PIN);
872
  msleep(10);
873
  GPIO_SetBits(SYS_REG_EN_GPIO, SYS_REG_EN_PIN);
874
  msleep(10);
875
  setLed(BLT_FALSE);
876
877
  /* drive SYS_WARMRST_N high (inactive) */
878
  GPIO_SetBits(SYS_WARMRST_N_GPIO, SYS_WARMRST_N_PIN);
879
880
  /* enable CAN clock
881
   * Note that CAN1 shares reception filters with CAN1 so for CAN2 the CAN1 peripheral also needs to be enabled. */
882
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN2 | RCC_APB1Periph_CAN1, ENABLE);
883
884
  /* wait 1ms to make sure that all modules are running and started the bootloader */
885
  msleep(1);
886
887
  /* initialize the bootloader */
888
  BootInit();
889
890
  /* start the infinite program loop */
891
  uint32_t loopStartTime = 0;
892
  saTimerUpdate(&loopStartTime);
893
  uint32_t currentTime = loopStartTime;
894
  while (1)
895
  {
896
    /* make the LED "double-blink" */
897
    saTimerUpdate(&currentTime);
898
    if (currentTime < loopStartTime + 50) {
899
      setLed(BLT_TRUE);
900
    } else if (currentTime < loopStartTime + 50+100) {
901
      setLed(BLT_FALSE);
902
    } else if (currentTime < loopStartTime + 50+100+50) {
903
      setLed(BLT_TRUE);
904
    } else if (currentTime < loopStartTime + 50+100+50+300) {
905
      setLed(BLT_FALSE);
906
    } else {
907
      loopStartTime = currentTime;
908
    }
909
910
    /* run the bootloader task */
911
    BootTask();
912
913
    /* check the SYS_PD_N signal */
914
    if (GPIO_ReadInputDataBit(SYS_PD_N_GPIO, SYS_PD_N_PIN) == Bit_RESET) {
915
      blCallbackHandleShutdownRequest();
916
      return SUCCESS;
917
    }
918
  }
919
920
  return ERROR;
921
} /*** end of handleColdReset ***/
922
923
/*
924
 * System was reset by software.
925
 * Depending on the argument, which was read from the 1st backup register (see main function) the effect of this function differs.
926
 * There are three cases that can occur:
927
 * - The system was reset to enter hibernate mode.
928
 *   In this case the system will enter a medium power saving mode (hibernate mode), but can be charged via the charging pins.
929
 *   The system can be woken up in the same way as in deepsleep mode (cf. blCallbackShutdownDeepsleep() function).
930
 * - The system was reset to reboot.
931
 *   In this case the system will restart in the same way as after a cold reset.
932
 * - The reason is unknown.
933
 *   This case will cause an error.
934
 */
935
ErrorStatus handleSoftwareReset() {
936
  /* action depends on original shutdown reason */
937
  switch (backup_reg.shutdown_pri_reason) {
938
    case BL_SHUTDOWN_PRI_RSN_HIBERNATE:
939
    {
940
      /* activate the WKUP pin */
941
      PWR_WakeUpPinCmd(ENABLE);
942
943
      /* deactivate any RTC related events */
944
      RTC_WakeUpCmd(DISABLE);
945
      RTC_TamperCmd(RTC_Tamper_1, DISABLE);
946
      RTC_TimeStampCmd(RTC_TimeStampEdge_Rising, DISABLE);
947
      RTC_TimeStampCmd(RTC_TimeStampEdge_Falling, DISABLE);
948
949
      /* configure the IWDG to wake the system from standby mode */
950
      uint16_t iwdg_ms = 1;
951
      if (GPIO_ReadInputDataBit(PATH_DC_GPIO, PATH_DC_PIN) != Bit_SET) {
952
        /* if a power plug is detected, fire immediately (1ms), else fire after the defined hibernate time */
953
        iwdg_ms = HIBERNATE_TIME_MS;
954
      }
955
      configIwdg(iwdg_ms);
956
      IWDG_Enable();
957
958
      /* enter standby mode */
959
      PWR_EnterSTANDBYMode();
960
961
      return SUCCESS;
962
      break;
963
    }
964
    case BL_SHUTDOWN_PRI_RSN_RESTART:
965
    {
966
      return handleColdReset();
967
      break;
968
    }
969
    case BL_SHUTDOWN_PRI_RSN_DEEPSLEEP:
970
    {
971
      if (GPIO_ReadInputDataBit(PATH_DC_GPIO, PATH_DC_PIN) == Bit_SET) {
972
        return handlePathDcWakeup();
973
      } else {
974
        blCallbackShutdownDeepsleep();
975
      }
976
      break;
977
    }
978
    default:
979
      return ERROR;
980
  }
981
  return ERROR;
982
} /*** end of handleSoftwareReset ***/
983
984
/*
985
 * System was woken up via the WKUP pin and the SYS_UART_DN signal was found to be responsible.
986
 * In this case, the system starts as after a cold reset.
987
 * this function is identical to handleTouchWakeup().
988
 */
989
ErrorStatus handleUartDnWakeup() {
990
  return handleColdReset();
991
} /*** end of hanldeUartDnWakeup ***/
992
993
/*
994
 * System was woken up via the WKUP pin and the PATH_DC signal was found to be responsible.
995
 * If the system was woken from deepsleep mode, it will enter hibernate mode to enable charging as long as the power plug is present.
996
 * In any other case, the system will just enter the previous low-power mode again.
997
 */
998
ErrorStatus handlePathDcWakeup() {
999
  /* reenter the previous low-power mode */
1000
  switch (backup_reg.shutdown_pri_reason) {
1001
    case BL_SHUTDOWN_PRI_RSN_HIBERNATE:
1002
      blCallbackShutdownHibernate();
1003
      return SUCCESS;
1004
      break;
1005
    case BL_SHUTDOWN_PRI_RSN_DEEPSLEEP:
1006
      /* visualize that the power plug was detected
1007
       * This is helpful for feedback, and required for the follwing reason:
1008
       * When the power plug is detected, it takes some additional time for the ADC to detect a high voltage.
1009
       * If the ADC detects a low voltage at the first attempt, the system will enter hibernate mode.
1010
       * Thus, the ADC will measure the voltage again after several seconds and charging will start.
1011
       * However, this behaviour does not meet the user expection.
1012
       * Hence, the voltage has some to adapt at this point
1013
       */
1014
      setLed(BLT_TRUE);
1015
      msleep(500);
1016
      setLed(BLT_FALSE);
1017
1018
      return handleIwdgWakeup();
1019
      break;
1020
    case BL_SHUTDOWN_PRI_RSN_TRANSPORT:
1021
      blCallbackShutdownTransportation();
1022
      return SUCCESS;
1023
      break;
1024
    default:
1025
      return ERROR;
1026
      break;
1027
  }
1028
1029
  return ERROR;
1030
} /*** end of handlePathDcWakeup ***/
1031
1032
/*
1033
 * System was woken up via the WKUP pin and the touch sensors were found to be responsible.
1034
 * In this case the system starts as after an cold reset.
1035
 * This function is identical to handleUartDnWakeup().
1036
 */
1037
ErrorStatus handleTouchWakeup() {
1038
  return handleColdReset();
1039
} /*** end of handleTouchWakeup ***/
1040
1041
/*
1042
 * System was woken up via the IWDG.
1043
 * In this case the ADC is configured and VSYS is measured once.
1044
 * 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.
1045
 * Otherwise, the system will configure the IWDG to wake the system again after five seconds and enter standby mode.
1046
 */
1047
ErrorStatus handleIwdgWakeup() {
1048
  /* handle different situations, depending on the backup data */
1049
  if ((backup_reg.shutdown_pri_reason == BL_SHUTDOWN_PRI_RSN_HIBERNATE) ||
1050
      (backup_reg.shutdown_pri_reason == BL_SHUTDOWN_PRI_RSN_DEEPSLEEP)) {
1051
    /* handle periodic wakeup in hibernate mode and in deepsleep mode when a power plug was detetced */
1052
1053
    /* if in hibernate mode, indicate the DiWheelDrive to enter hibernate mode as well, so it will activate the charging pins */
1054
    if (backup_reg.shutdown_pri_reason == BL_SHUTDOWN_PRI_RSN_HIBERNATE) {
1055
      indicateHibernate();
1056
    }
1057
1058
    /* measure the current voltage of VSYS */
1059
    AdcSingleMeasurement();
1060
1061
    /* evaluate the value
1062
     * The ADC value represents the analog voltage between Vref- (= GND = 0.0V) and Vref+ (= VDD = 3.3V) as 12-bit value.
1063
     * Hence, the value read from the register is first scaled to [0V .. 3.3V].
1064
     * Then, an additional factor 5.33 is applied to account the downscaling on the board.
1065
     * Actually, the factor should be 5.0, but due to too large resistors it was corrected to 5.33.
1066
     */
1067
    if ( (((float)(ADC_GetConversionValue(ADC1)) / (float)(0x0FFF)) * 3.3f * 5.33f) < 9.0f ) {
1068
      /* VSYS was found to be < 9V */
1069
1070
      /* re-enter power saving mode
1071
       * If the system was shut down to deepsleep mode and the power plug was removed, re-enter deepsleep mode.
1072
       * (This could be done earlier in this function, but since charging via the pins of the DeWheelDrive may be
1073
       *  supported in the future, this is done after measuring VSYS)
1074
       */
1075
      if (backup_reg.shutdown_pri_reason == BL_SHUTDOWN_PRI_RSN_DEEPSLEEP &&
1076
          GPIO_ReadInputDataBit(PATH_DC_GPIO, PATH_DC_PIN) == Bit_RESET) {
1077
        blCallbackShutdownDeepsleep();
1078
      } else {
1079
        /* reconfigure the IWDG and power down for five seconds */
1080
        configIwdg(HIBERNATE_TIME_MS);
1081
        IWDG_Enable();
1082
1083
        /* enter standby mode */
1084
        PWR_EnterSTANDBYMode();
1085
      }
1086
1087
      return SUCCESS;
1088
    } else {
1089
      /* VSYS was found to be >= 9V */
1090
      setLed(BLT_TRUE);
1091
1092
      /* charge the battieries */
1093
      GPIO_ResetBits(CHARGE_EN1_N_GPIO, CHARGE_EN1_N_PIN);
1094
      GPIO_ResetBits(CHARGE_EN2_N_GPIO, CHARGE_EN2_N_PIN);
1095
1096
      /* configure analog watchdoch to fire as soon as the voltage drops below 9V */
1097
      ADC_DeInit();
1098
      setupADC(ADC1, (uint16_t)(9.0f / 5.33f / 3.3f * (float)0x0FFF), 0x0FFF);
1099
1100
      EXTI_InitTypeDef exti;
1101
      /* configure UART_DN EXTI */
1102
      exti.EXTI_Line = EXTI_Line3;
1103
      exti.EXTI_Mode = EXTI_Mode_Interrupt;
1104
      exti.EXTI_Trigger = EXTI_Trigger_Falling;
1105
      exti.EXTI_LineCmd = ENABLE;
1106
      EXTI_Init(&exti);
1107
1108
      /* configure TOUCH_INT_N EXTI */
1109
      exti.EXTI_Line = EXTI_Line5;
1110
      exti.EXTI_Mode = EXTI_Mode_Interrupt;
1111
      exti.EXTI_Trigger = EXTI_Trigger_Falling;
1112
      exti.EXTI_LineCmd = ENABLE;
1113
      EXTI_Init(&exti);
1114
1115
      /* configure PATH_DC EXTI */
1116
      if (backup_reg.shutdown_pri_reason == BL_SHUTDOWN_PRI_RSN_DEEPSLEEP) {
1117
        exti.EXTI_Line = EXTI_Line8;
1118
        exti.EXTI_Mode = EXTI_Mode_Interrupt;
1119
        exti.EXTI_Trigger = EXTI_Trigger_Falling;
1120
        exti.EXTI_LineCmd = ENABLE;
1121
        EXTI_Init(&exti);
1122
      }
1123
1124
      /* configure the NVIC so ADC and EXTI will be handled */
1125
      NVIC_InitTypeDef nvic;
1126
      nvic.NVIC_IRQChannel = ADC_IRQn;
1127
      nvic.NVIC_IRQChannelPreemptionPriority = 6;
1128
      nvic.NVIC_IRQChannelSubPriority = 6;
1129
      nvic.NVIC_IRQChannelCmd = ENABLE;
1130
      NVIC_Init(&nvic);
1131
      nvic.NVIC_IRQChannel = EXTI3_IRQn;
1132
      nvic.NVIC_IRQChannelPreemptionPriority = 6;
1133
      nvic.NVIC_IRQChannelSubPriority = 6;
1134
      nvic.NVIC_IRQChannelCmd = ENABLE;
1135
      NVIC_Init(&nvic);
1136
      NVIC_EnableIRQ(EXTI3_IRQn);
1137
      nvic.NVIC_IRQChannel = EXTI9_5_IRQn;
1138
      nvic.NVIC_IRQChannelPreemptionPriority = 6;
1139
      nvic.NVIC_IRQChannelSubPriority = 6;
1140
      nvic.NVIC_IRQChannelCmd = ENABLE;
1141
      NVIC_Init(&nvic);
1142
      NVIC_EnableIRQ(EXTI9_5_IRQn);
1143
1144
      /* activate the ADC */
1145
      ADC_SoftwareStartConv(ADC1);
1146
1147
      /* sleep until something happens */
1148
      __WFI();
1149
1150
      /* disable the chargers */
1151
      GPIO_SetBits(CHARGE_EN1_N_GPIO, CHARGE_EN1_N_PIN);
1152
      GPIO_SetBits(CHARGE_EN2_N_GPIO, CHARGE_EN2_N_PIN);
1153
      setLed(BLT_FALSE);
1154
1155
      /* evaluate wakeup reason */
1156
      // note: since I (tschoepp) don't know the difference between 'pending' and 'active' IRQs, both flags are ORed.
1157
      uint8_t wkup_rsn = BL_WAKEUP_SEC_RSN_UNKNOWN;
1158
      if ((NVIC_GetActive(ADC_IRQn) != 0 || NVIC_GetPendingIRQ(ADC_IRQn) != 0) &&
1159
          ADC_GetITStatus(ADC1, ADC_IT_AWD) == SET &&
1160
          ADC_GetFlagStatus(ADC1, ADC_FLAG_AWD) == SET) {
1161
        wkup_rsn |= BL_WAKEUP_SEC_RSN_VSYSLOW;
1162
      }
1163
      if ((NVIC_GetActive(EXTI3_IRQn) != 0 || NVIC_GetPendingIRQ(EXTI3_IRQn) != 0) &&
1164
          EXTI_GetFlagStatus(EXTI_Line3) == SET) {
1165
        wkup_rsn |= BL_WAKEUP_SEC_RSN_UART;
1166
      }
1167
      if ((NVIC_GetActive(EXTI9_5_IRQn) != 0 || NVIC_GetPendingIRQ(EXTI9_5_IRQn) != 0) &&
1168
          EXTI_GetFlagStatus(EXTI_Line5) == SET) {
1169
        wkup_rsn |= BL_WAKEUP_SEC_RSN_TOUCH;
1170
      }
1171
      if ((NVIC_GetActive(EXTI9_5_IRQn) != 0 || NVIC_GetPendingIRQ(EXTI9_5_IRQn) != 0) &&
1172
          EXTI_GetFlagStatus(EXTI_Line8) == SET) {
1173
        wkup_rsn |= BL_WAKEUP_SEC_RSN_PWRPLUG;
1174
      }
1175
1176
      /* since only the first interrupt will be handles, clear any pending ones */
1177
      NVIC_DisableIRQ(ADC_IRQn);
1178
      NVIC_DisableIRQ(EXTI3_IRQn);
1179
      NVIC_DisableIRQ(EXTI9_5_IRQn);
1180
      NVIC_ClearPendingIRQ(ADC_IRQn);
1181
      NVIC_ClearPendingIRQ(EXTI3_IRQn);
1182
      NVIC_ClearPendingIRQ(EXTI9_5_IRQn);
1183
1184
      /* clear all pending EXTI events */
1185
      EXTI_DeInit();
1186
      EXTI_ClearFlag(EXTI_Line3);
1187
      EXTI_ClearFlag(EXTI_Line5);
1188
      EXTI_ClearFlag(EXTI_Line8);
1189
1190
      /* make sure the LED was visibly turned off */
1191
      msleep(100);
1192
1193
      /* depending on the wakup reason, handle accordingly */
1194
      if (wkup_rsn & BL_WAKEUP_SEC_RSN_TOUCH) {
1195
        /* the system was interrupted via the TOUCH_INT_N signal */
1196
1197
        /* act as if this was a normal touch wakeup */
1198
        backup_reg.wakeup_pri_reason = BL_WAKEUP_PRI_RSN_WKUP;
1199
        backup_reg.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_TOUCH;
1200
        RTC_WriteBackupRegister(BL_RTC_BACKUP_REG, backup_reg.raw);
1201
        return handleTouchWakeup();
1202
      } else if (wkup_rsn & BL_WAKEUP_SEC_RSN_UART) {
1203
        /* the system was interrupted via the SYS_UARTDN signal */
1204
1205
        /* act as if this was a normal UART wakeup */
1206
        backup_reg.wakeup_pri_reason = BL_WAKEUP_PRI_RSN_WKUP;
1207
        backup_reg.wakeup_sec_reason = BL_WAKEUP_SEC_RSN_UART;
1208
        RTC_WriteBackupRegister(BL_RTC_BACKUP_REG, backup_reg.raw);
1209
        return handleUartDnWakeup();
1210
      } else if (wkup_rsn & BL_WAKEUP_SEC_RSN_VSYSLOW) {
1211
        /* VSYS has dropped below 9V */
1212
1213
        /* depending on the original reason for shutdown, act differenty */
1214
        switch (backup_reg.shutdown_pri_reason) {
1215
          case BL_SHUTDOWN_PRI_RSN_HIBERNATE:
1216
          {
1217
            blCallbackShutdownHibernate();
1218
            return SUCCESS;
1219
          }
1220
          case BL_SHUTDOWN_PRI_RSN_DEEPSLEEP:
1221
          {
1222
            NVIC_SystemReset();
1223
            return SUCCESS;
1224
          }
1225
          default:
1226
            return ERROR;
1227
        }
1228
      } else if (wkup_rsn & BL_WAKEUP_SEC_RSN_PWRPLUG) {
1229
        /* system was interrupted because the power plug was removed
1230
         * note: when a power cord is plugged in, this will not trigger an interrupt because the NVIC is configured for a falling edge only */
1231
        if (backup_reg.shutdown_pri_reason == BL_SHUTDOWN_PRI_RSN_DEEPSLEEP) {
1232
          blCallbackShutdownDeepsleep();
1233
          return SUCCESS;
1234
        } else {
1235
          /* this state is undefined, because the PATH_DC inerrupt is only configured when the primary shutdown reason was to enter deepsleep mode */
1236
          return ERROR;
1237
        }
1238
      } else {
1239
        /* the system was interrupted for an unknown reason */
1240
        return ERROR;
1241
      }
1242
    } // end of ADC evaluation
1243
  } else {
1244
    /* since it is unknown why the IWDG was configured, act as after a cold reset */
1245
    return handleColdReset();
1246
  }
1247
1248
  return ERROR;
1249
} /*** end of handleIwdgWakeup ***/
1250
1251
/*
1252
 * Indicates the DiWheelDrive module to enter hibernate mode at wakeup.
1253
 * This function should be called quite at the beginning of the according handleXXXReset/Wakeup() methods.
1254
 */
1255
static void indicateHibernate() {
1256
  /* signal the DiWheelDrive to enter hibernate mode as well, so it will activate the charging pins */
1257
  GPIO_ResetBits(SYS_UART_DN_GPIO, SYS_UART_DN_PIN);
1258
  msleep(10); // this must be that long, because the DiWheelDrive sleeps some time before evaluating any signals
1259
  GPIO_SetBits(SYS_UART_DN_GPIO, SYS_UART_DN_PIN);
1260
1261
  /* if the DiWheeDrive needs some time for setup it may pull down the signal */
1262
  waitForSignal(SYS_UART_DN_GPIO, SYS_UART_DN_PIN, Bit_SET);
1263
1264
  return;
1265
} /*** end of indicateHibernate ***/
1266
1267
/*
1268
 *Performs a one-shot measurement of the VSYS voltage.
1269
 */
1270
static void AdcSingleMeasurement() {
1271
  /* reset and initialize ADC for single-shot measurement */
1272
//    ADC_DeInit();
1273
  setupADC(ADC1, 0, 0);
1274
1275
  /* initialize the NVIC so ADC interrupts are handled */
1276
  NVIC_InitTypeDef nvic;
1277
  nvic.NVIC_IRQChannel = ADC_IRQn;
1278
  nvic.NVIC_IRQChannelPreemptionPriority = 6;
1279
  nvic.NVIC_IRQChannelSubPriority = 6;
1280
  nvic.NVIC_IRQChannelCmd = ENABLE;
1281
  NVIC_Init(&nvic);
1282
1283
  /* measure the voltage once */
1284
  setLed(BLT_TRUE);
1285
  ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
1286
  ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
1287
  NVIC_EnableIRQ(ADC_IRQn);
1288
  ADC_SoftwareStartConv(ADC1);
1289
  while (ADC_GetITStatus(ADC1, ADC_IT_EOC) != SET && ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) != SET) {
1290
    __WFI();
1291
  }
1292
  NVIC_DisableIRQ(ADC_IRQn);
1293
  ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
1294
  ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
1295
  NVIC_ClearPendingIRQ(ADC_IRQn);
1296
  setLed(BLT_FALSE);
1297
1298
  return;
1299
} /*** end of AdcSingleMeasurement ***/
1300
1301
/*
1302
 * Configures the ADC for measuring VSYS.
1303
 * ADCx is the ADC object to initialize.
1304
 * low_th and high_th are the threshold values for the analog watchdor (must be 12-bit!).
1305
 * If low_th >= high_th, the ADC is configured for single-shot measurements.
1306
 * Otherwise, the watchdog is configured with the corresponding thresholds.
1307
 */
1308
ADC_TypeDef* setupADC(ADC_TypeDef* adc, const uint16_t low_th, const uint16_t high_th) {
1309
  /* evaluate the arguments */
1310
  blt_bool awd_enable = BLT_FALSE;
1311
  if (low_th < high_th) {
1312
    awd_enable = BLT_TRUE;
1313
  }
1314
1315
  /* enable the clock */
1316
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
1317
1318
  /* enable the ADC (wakes it from low-power mode) */
1319
  ADC_Cmd(adc, ENABLE);
1320
1321
  /* initialize the common registers */
1322
  ADC_CommonInitTypeDef adc_cinit;
1323
  ADC_CommonStructInit(&adc_cinit);
1324
  adc_cinit.ADC_Prescaler = ADC_Prescaler_Div8; // clock as slow as possible
1325
  ADC_CommonInit(&adc_cinit);
1326
1327
  /* initialize the ADC */
1328
  ADC_InitTypeDef adc_init;
1329
  ADC_StructInit(&adc_init);
1330
  adc_init.ADC_ContinuousConvMode = (awd_enable == BLT_TRUE) ? ENABLE : DISABLE;
1331
  ADC_Init(adc, &adc_init);
1332
1333
  /* disable internal sensors */
1334
  ADC_TempSensorVrefintCmd(DISABLE);
1335
  ADC_VBATCmd(DISABLE);
1336
1337
  /* configure ADC channel and speed */
1338
  ADC_RegularChannelConfig(adc, ADC_Channel_9, 1, ADC_SampleTime_480Cycles);
1339
  ADC_EOCOnEachRegularChannelCmd(adc, (awd_enable == BLT_TRUE) ? DISABLE : ENABLE);
1340
  ADC_DiscModeCmd(adc, DISABLE);
1341
1342
  /* disable DMA */
1343
  ADC_DMACmd(adc, DISABLE);
1344
1345
  /* disable injected mode */
1346
  ADC_AutoInjectedConvCmd(adc, DISABLE);
1347
  ADC_InjectedDiscModeCmd(adc, DISABLE);
1348
1349
  /* configure the analog watchdog */
1350
  if (awd_enable == BLT_TRUE) {
1351
    ADC_AnalogWatchdogSingleChannelConfig(adc, ADC_Channel_9);
1352
    ADC_AnalogWatchdogThresholdsConfig(adc, high_th, low_th);
1353
    ADC_AnalogWatchdogCmd(adc, ADC_AnalogWatchdog_SingleRegEnable);
1354
  } else {
1355
    ADC_AnalogWatchdogCmd(adc, ADC_AnalogWatchdog_None);
1356
  }
1357
1358
  /* configure the interrupts to be generated by the ADC */
1359
  ADC_ITConfig(adc, ADC_IT_EOC, (awd_enable == BLT_TRUE) ? DISABLE : ENABLE);
1360
  ADC_ITConfig(adc, ADC_IT_AWD, (awd_enable == BLT_TRUE) ? ENABLE : DISABLE);
1361
  ADC_ITConfig(adc, ADC_IT_JEOC, DISABLE);
1362
  ADC_ITConfig(adc, ADC_IT_OVR, DISABLE);
1363
1364
  return adc;
1365
}
1366
1367
/*
1368
 * Callback function that handles the system shutdown and enters transportation mode.
1369
 * When called from a multithreaded environment, it must be ensured that no other thread will preempt this function.
1370
 * In transportation low-power mode the system can only be woken up by pulling down the NRST signal.
1371
 * Furthermore, the system can not be charged when in transportation mode.
1372
 */
1373
void blCallbackShutdownTransportation(void) {
1374
  /* make sure that the required clocks are activated */
1375
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE);
1376
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
1377
1378
  /* set/keep the SYS_SYNC and SYS_PD signals active */
1379
  GPIO_ResetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1380
  GPIO_ResetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
1381
1382
  /* initialized the standalone timer */
1383
  saTimerInit();
1384
1385
  setLed(BLT_TRUE);
1386
1387
  /* wait for all boards to be ready for shutdown */
1388
  GPIO_SetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1389
  if (GPIO_ReadOutputDataBit(SYS_REG_EN_GPIO, SYS_REG_EN_PIN) == Bit_SET) {
1390
    // this must skipped if the pullup voltage (VIO3.3) is not active
1391
    setLed(BLT_TRUE);
1392
    waitForSignal(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN, Bit_SET);
1393
    setLed(BLT_FALSE);
1394
  }
1395
1396
  /* execute disambiguation procedure and signal all modules to enter transportation mode */
1397
  if (shutdownDisambiguationProcedure(BL_SHUTDOWN_PRI_RSN_TRANSPORT) != SUCCESS) {
1398
    blinkSOS(1);
1399
    msleep(10);
1400
  }
1401
1402
  shutdownToTransportation();
1403
1404
  return;
1405
} /*** end of bLCallbackTransportation ***/
1406
1407
/*
1408
 * Callback function that handles the system shutdown and enters deepsleep mode.
1409
 * When called from a multithreaded environment, it must be ensured that no other thread will preempt this function.
1410
 * 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.
1411
 * When a power plug is detected, the system will switch to hibernate mode, to provide charging capabilities (cf. handlePathDcWakeup()).
1412
 * As soon as the plug is removed again, however, the system will return to deppsleep mode (cf. handleIwdgWakeup()).
1413
 */
1414
void blCallbackShutdownDeepsleep(void) {
1415
  /* make sure that the required clocks are activated */
1416
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE);
1417
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
1418
1419
  /* set/keep the SYS_SYNC and SYS_PD signals active */
1420
  GPIO_ResetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1421
  GPIO_ResetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
1422
1423
  /* initialized the standalone timer */
1424
  saTimerInit();
1425
1426
  setLed(BLT_TRUE);
1427
1428
  /* wait for all boards to be ready for shutdown */
1429
  GPIO_SetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1430
  if (GPIO_ReadOutputDataBit(SYS_REG_EN_GPIO, SYS_REG_EN_PIN) == Bit_SET) {
1431
    // this must skipped if the pullup voltage (VIO3.3) is not active
1432
    setLed(BLT_TRUE);
1433
    waitForSignal(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN, Bit_SET);
1434
    setLed(BLT_FALSE);
1435
  }
1436
1437
  /* execute disambiguation procedure and signal all modules to enter deepsleep mode */
1438
  if (shutdownDisambiguationProcedure(BL_SHUTDOWN_PRI_RSN_DEEPSLEEP) != SUCCESS) {
1439
    blinkSOS(1);
1440
    msleep(10);
1441
  }
1442
1443
  shutdownToDeepsleep();
1444
1445
  return;
1446
} /*** end of bLCallbackDeepsleep ***/
1447
1448
/*
1449
 * Callback function that handles the system shutdown and enters hibernate mode.
1450
 * When called from a multithreaded environment, it must be ensured that no other thread will preempt this function.
1451
 * Since this function actually just configures the system in a way, that it will enter hibernate mode after the next reset and rests it,
1452
 * see the handleSoftwareReset() function for more details about the hibernate low-power mode.
1453
 */
1454
void blCallbackShutdownHibernate(void) {
1455
  /* make sure that the required clocks are activated */
1456
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE);
1457
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
1458
1459
  /* set/keep the SYS_SYNC and SYS_PD signals active */
1460
  GPIO_ResetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1461
  GPIO_ResetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
1462
1463
  /* initialized the standalone timer */
1464
  saTimerInit();
1465
1466
  setLed(BLT_TRUE);
1467
1468
  /* wait for all boards to be ready for shutdown */
1469
  GPIO_SetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1470
  if (GPIO_ReadOutputDataBit(SYS_REG_EN_GPIO, SYS_REG_EN_PIN) == Bit_SET) {
1471
    // this must skipped if the pullup voltage (VIO3.3) is not active
1472
    setLed(BLT_TRUE);
1473
    waitForSignal(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN, Bit_SET);
1474
    setLed(BLT_FALSE);
1475
  }
1476
1477
  /* execute disambiguation procedure and signal all modules to enter hibernate mode */
1478
  if (shutdownDisambiguationProcedure(BL_SHUTDOWN_PRI_RSN_DEEPSLEEP) != SUCCESS) {
1479
    blinkSOS(1);
1480
    msleep(10);
1481
  }
1482
1483
  shutdownToHibernate();
1484
1485
  return;
1486
} /*** end of bLCallbackShutdownHibernate ***/
1487
1488
/*
1489
 * Callback function that handles the system shutdown and initializes a restart.
1490
 * When called from a multithreaded environment, it must be ensured that no other thread will preempt this function.
1491
 * By configuration it is ensured, that the system will end up executing the handleSoftwareReset() function after reset.
1492
 */
1493
void blCallbackShutdownRestart(void) {
1494
  /* make sure that the required clocks are activated */
1495
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE);
1496
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
1497
1498
  /* set/keep the SYS_SYNC and SYS_PD signal active */
1499
  GPIO_ResetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1500
  GPIO_ResetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
1501
1502
  /* initialized the standalone timer */
1503
  saTimerInit();
1504
1505
  setLed(BLT_TRUE);
1506
1507
  /* deactivate SYS_PD_N and ensure that all modules had a chance to detect the falling edge */
1508
  msleep(1);
1509
  GPIO_SetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
1510
  msleep(1);
1511
1512
  /* wait for all boards to be ready for shutdown */
1513
  GPIO_SetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1514
  if (GPIO_ReadOutputDataBit(SYS_REG_EN_GPIO, SYS_REG_EN_PIN) == Bit_SET) {
1515
    // this must skipped if the pullup voltage (VIO3.3) is not active
1516
    setLed(BLT_TRUE);
1517
    waitForSignal(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN, Bit_SET);
1518
    setLed(BLT_FALSE);
1519
  }
1520
1521
  /* execute disambiguation procedure and signal all modules to restart normally */
1522
  if (shutdownDisambiguationProcedure(BL_SHUTDOWN_PRI_RSN_RESTART) != SUCCESS) {
1523
    blinkSOS(1);
1524
    msleep(10);
1525
  }
1526
1527
  /* restart the system */
1528
  shutdownAndRestart();
1529
1530
  return;
1531
} /*** end of bLCallbackRestart ***/
1532
1533
1534
/*
1535
 * Callback function that handles a system shutdown/restart request from another module.
1536
 * Depending on the result of the disambiguation procedure, the module will enter the according low-power mode or restart.
1537
 * When called from a multithreaded environment, it must be ensured that no other thread will preempt this function.
1538
 */
1539
void blCallbackHandleShutdownRequest(void) {
1540
  /* make sure that the required clocks are activated */
1541
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE);
1542
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
1543
1544
  /* set/keep the SYS_SYNC and SYS_PD signal active */
1545
  GPIO_ResetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1546
  GPIO_ResetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
1547
1548
  /* initialized the standalone timer */
1549
  saTimerInit();
1550
1551
  setLed(BLT_TRUE);
1552
1553
  /* deactivate SYS_PD_N and ensure that all modules had a chance to detect the falling edge */
1554
  msleep(1);
1555
  GPIO_SetBits(SYS_PD_N_GPIO, SYS_PD_N_PIN);
1556
  msleep(1);
1557
1558
  /* wait for all boards to be ready for shutdown */
1559
  GPIO_SetBits(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN);
1560
  if (GPIO_ReadOutputDataBit(SYS_REG_EN_GPIO, SYS_REG_EN_PIN) == Bit_SET) {
1561
    // this must skipped if the pullup voltage (VIO3.3) is not active
1562
    setLed(BLT_TRUE);
1563
    waitForSignal(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN, Bit_SET);
1564
    setLed(BLT_FALSE);
1565
  }
1566
1567
  /* check ths SYS_PD_N signal, whether the system shall shutdown or restart */
1568
  blt_bool shutdown_nrestart = (GPIO_ReadInputDataBit(SYS_PD_N_GPIO, SYS_PD_N_PIN) == Bit_RESET) ? BLT_TRUE : BLT_FALSE;
1569
1570
  /* disambiguation procedure (passive) */
1571
  uint32_t pulse_counter = 0;
1572
  while (waitForSignalTimeout(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN, Bit_RESET, 10)) {
1573
    waitForSignal(SYS_SYNC_N_GPIO, SYS_SYNC_N_PIN, Bit_SET);
1574
    ++pulse_counter;
1575
  }
1576
1577
  /* evaluate and hanlde disambiguation result */
1578
  if (shutdown_nrestart == BLT_TRUE) {
1579
    /* shutdown request */
1580
1581
    /* handle special cases */
1582
    if (pulse_counter == BL_SHUTDOWN_PRI_RSN_UNKNOWN) {
1583
      /* no pulse at all was received */
1584
      pulse_counter = BL_SHUTDOWN_PRI_RSN_DEFAULT;
1585
    } else if (pulse_counter != BL_SHUTDOWN_PRI_RSN_HIBERNATE &&
1586
               pulse_counter != BL_SHUTDOWN_PRI_RSN_DEEPSLEEP &&
1587
               pulse_counter != BL_SHUTDOWN_PRI_RSN_TRANSPORT) {
1588
      /* invalid number of pulses received */
1589
      blinkSOS(1);
1590
      pulse_counter = BL_SHUTDOWN_PRI_RSN_DEFAULT;
1591
    }
1592
1593
    switch (pulse_counter) {
1594
      case BL_SHUTDOWN_PRI_RSN_HIBERNATE:
1595
        shutdownToHibernate();
1596
        break;
1597
      case BL_SHUTDOWN_PRI_RSN_DEEPSLEEP:
1598
        shutdownToDeepsleep();
1599
        break;
1600
      case BL_SHUTDOWN_PRI_RSN_TRANSPORT:
1601
        shutdownToTransportation();
1602
        break;
1603
    }
1604
  } else {
1605
    /* restart request */
1606
1607
    /* there is no ambiguity for restart, so it is ignored */
1608
    shutdownAndRestart();
1609
  }
1610
1611
  /* if this code is reached, the system did neither shut down, nor restart.
1612
   * This must never be the case!
1613
   */
1614
  blinkSOSinf();
1615
  return;
1616
} /*** end of blCallbackHandleShutdownRequest ***/
1617
1618
1619
/*********************************** end of main.c *************************************/