1 /**
2 *********************************************************************************
3 *
4 * @file utils.c
5 * @brief This file contains the Utilities functions/types for the driver.
6 *
7 * @version V1.1
8 * @date 13 Apr 2021
9 * @author AE Team
10 * @note
11 * Change Logs:
12 * Date Author Notes
13 * 07 Nov 2019 AE Team The first version
14 * 13 Apr 2021 AE Team Add API: sys_config()
15 *
16 * Copyright (C) Shanghai Eastsoft Microelectronics Co. Ltd. All rights reserved.
17 *
18 * SPDX-License-Identifier: Apache-2.0
19 *
20 * Licensed under the Apache License, Version 2.0 (the License); you may
21 * not use this file except in compliance with the License.
22 * You may obtain a copy of the License at
23 *
24 * www.apache.org/licenses/LICENSE-2.0
25 *
26 * Unless required by applicable law or agreed to in writing, software
27 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
28 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29 * See the License for the specific language governing permissions and
30 * limitations under the License.
31 **********************************************************************************
32 */
33
34 #include <string.h>
35 #include "ald_conf.h"
36
37 /** @defgroup ES32FXXX_ALD EASTSOFT ES32F3xx ALD
38 * @brief Shanghai Eastsoft Microelectronics Cortex-M Chip Abstraction Layer Driver(ALD)
39 * @{
40 */
41
42 /** @defgroup UTILS Utils
43 * @brief Utils module driver
44 * @{
45 */
46
47 /** @defgroup ALD_Private_Constants Private Constants
48 * @brief ALD Private Constants
49 * @{
50 */
51
52 /**
53 * @brief ALD version number
54 */
55 #define __ALD_VERSION_MAIN (0x01) /**< [31:24] main version */
56 #define __ALD_VERSION_SUB1 (0x00) /**< [23:16] sub1 version */
57 #define __ALD_VERSION_SUB2 (0x00) /**< [15:8] sub2 version */
58 #define __ALD_VERSION_RC (0x00) /**< [7:0] release candidate */
59 #define __ALD_VERSION ((__ALD_VERSION_MAIN << 24) | \
60 (__ALD_VERSION_SUB1 << 16) | \
61 (__ALD_VERSION_SUB2 << 8 ) | \
62 (__ALD_VERSION_RC))
63 /**
64 * @}
65 */
66
67 /** @defgroup ALD_Private_Variables Private Variables
68 * @{
69 */
70 /** @brief lib_tick: Increase by one millisecond
71 */
72 static __IO uint32_t lib_tick;
73 uint32_t __systick_interval = SYSTICK_INTERVAL_1MS;
74 /**
75 * @}
76 */
77
78
79 /** @defgroup ALD_Public_Functions Public Functions
80 * @{
81 */
82
83 /** @defgroup ALD_Public_Functions_Group1 Initialization Function
84 * @brief Initialization functions
85 *
86 * @verbatim
87 ===============================================================================
88 ##### Initialization functions #####
89 ===============================================================================
90 [..] This section provides functions allowing to:
91 (+) Initializes interface, the NVIC allocation and initial clock
92 configuration. It initializes the source of time base also when timeout
93 is needed and the backup domain when enabled.
94 (+) Configure The time base source to have 1ms time base with a dedicated
95 Tick interrupt priority.
96 (++) Systick timer is used by default as source of time base, but user
97 can eventually implement his proper time base source (a general purpose
98 timer for example or other time source), keeping in mind that Time base
99 duration should be kept 1ms.
100 (++) Time base configuration function (ald_tick_init()) is called automatically
101 at the beginning of the program after reset by ald_cmu_init() or at
102 any time when clock is configured.
103 (++) Source of time base is configured to generate interrupts at regular
104 time intervals. Care must be taken if ald_delay_ms() is called from a
105 peripheral ISR process, the Tick interrupt line must have higher priority
106 (numerically lower) than the peripheral interrupt. Otherwise the caller
107 ISR process will be blocked.
108 (++) functions affecting time base configurations are declared as __weak
109 to make override possible in case of other implementations in user file.
110 (+) Configure the interval of Systick interrupt.
111
112 @endverbatim
113 * @{
114 */
115
116 /**
117 * @brief This function Configures time base source, NVIC and DMA.
118 * @note This function is called at the beginning of program after reset and before
119 * the clock configuration.
120 * @note The time base configuration is based on MSI clock when exiting from Reset.
121 * Once done, time base tick start incrementing.
122 * In the default implementation, Systick is used as source of time base.
123 * The tick variable is incremented each 1ms in its ISR.
124 * @retval None
125 */
ald_cmu_init(void)126 void ald_cmu_init(void)
127 {
128 NVIC_SetPriorityGrouping(NVIC_PRIORITY_GROUP_2);
129 ald_cmu_clock_config_default();
130 ald_tick_init(TICK_INT_PRIORITY);
131 #ifdef ALD_DMA
132 ald_cmu_perh_clock_config(CMU_PERH_DMA, ENABLE);
133 ald_dma_init(DMA0);
134 #endif
135 return;
136 }
137
138 /**
139 * @brief This function configures the source of the time base.
140 * The time source is configured to have 1ms time base with a dedicated
141 * Tick interrupt priority.
142 * @note In the default implementation, SysTick timer is the source of time base.
143 * It is used to generate interrupts at regular time intervals.
144 * Care must be taken if ald_delay_ms() is called from a peripheral ISR process,
145 * The SysTick interrupt must have higher priority (numerically lower)
146 * than the peripheral interrupt. Otherwise the caller ISR process will be blocked.
147 * The function is declared as __weak to be overwritten in case of other
148 * implementation in user file.
149 * @param prio: Tick interrupt priority.
150 * @retval None
151 */
ald_tick_init(uint32_t prio)152 __weak void ald_tick_init(uint32_t prio)
153 {
154 /* Configure the SysTick IRQ */
155 SysTick_Config(ald_cmu_get_sys_clock() / SYSTICK_INTERVAL_1MS);
156 NVIC_SetPriority(SysTick_IRQn, prio);
157
158 return;
159 }
160
161 /**
162 * @brief Selects the interval of systick interrupt.
163 * @param value: The value of interval:
164 * @arg @ref SYSTICK_INTERVAL_1MS 1 millisecond
165 * @arg @ref SYSTICK_INTERVAL_10MS 10 milliseconds
166 * @arg @ref SYSTICK_INTERVAL_100MS 100 milliseconds
167 * @arg @ref SYSTICK_INTERVAL_1000MS 1 second
168 * @retval None
169 */
ald_systick_interval_select(systick_interval_t value)170 void ald_systick_interval_select(systick_interval_t value)
171 {
172 assert_param(IS_SYSTICK_INTERVAL(value));
173
174 if (value == 0) return;
175
176 SysTick_Config(ald_cmu_get_sys_clock() / value);
177 __systick_interval = value;
178
179 if (TICK_INT_PRIORITY != 15)
180 NVIC_SetPriority(SysTick_IRQn, TICK_INT_PRIORITY);
181
182 return;
183 }
184 /**
185 * @}
186 */
187
188 /** @defgroup ALD_Public_Functions_Group2 Control functions
189 * @brief Control functions
190 *
191 * @verbatim
192 ===============================================================================
193 ##### Control functions #####
194 ===============================================================================
195 [..] This section provides functions allowing to:
196 (+) Provide a tick value in millisecond
197 (+) Provide a blocking delay in millisecond
198 (+) Suspend the time base source interrupt
199 (+) Resume the time base source interrupt
200 (+) Get the ALD version
201 (+) Waiting for flag
202 (+) Configure the interrupt
203 (+) Provide system tick value
204 (+) Initialize core timestamp
205 (+) Get core timestamp
206 (+) Get CPU ID
207 (+) Get UID
208 (+) Get CHIPID
209
210 @endverbatim
211 * @{
212 */
213
214 /**
215 * @brief This function invoked by Systick ISR.
216 * @note This function is declared as __weak to be overwritten in case of
217 * other implementations in user file.
218 * @retval None
219 */
ald_systick_irq_cbk(void)220 __weak void ald_systick_irq_cbk(void)
221 {
222 /* do nothing */
223 return;
224 }
225
226 /**
227 * @brief This function is called to increment a global variable "lib_tick"
228 * used as application time base.
229 * @note In the default implementation, this variable is incremented each 1ms
230 * in Systick ISR.
231 * @note This function is declared as __weak to be overwritten in case of other
232 * implementations in user file.
233 * @retval None
234 */
ald_inc_tick(void)235 __weak void ald_inc_tick(void)
236 {
237 ++lib_tick;
238 ald_systick_irq_cbk();
239 }
240
241 /**
242 * @brief Provides a tick value in millisecond.
243 * @note This function is declared as __weak to be overwritten in case of other
244 * implementations in user file.
245 * @retval tick value
246 */
ald_get_tick(void)247 __weak uint32_t ald_get_tick(void)
248 {
249 return lib_tick;
250 }
251
252 /**
253 * @brief This function provides accurate delay (in microseconds) based
254 * on variable incremented.
255 * @note In the default implementation, SysTick timer is the source of time base.
256 * It is used to generate interrupts at regular time intervals where lib_tick
257 * is incremented.
258 * @note This function is declared as __weak to be overwritten in case of other
259 * implementations in user file.
260 * @param delay: specifies the delay time length, in microseconds(us).
261 * @retval None
262 */
ald_delay_us(__IO uint32_t delay)263 __weak void ald_delay_us(__IO uint32_t delay)
264 {
265 uint32_t start, now, delta, reload, us_tick;
266 start = SysTick->VAL;
267 reload = SysTick->LOAD;
268 us_tick = ald_cmu_get_sys_clock() / 1000000UL;
269 do
270 {
271 now = SysTick->VAL;
272 delta = (start > now) ? (start - now) : (reload + start - now);
273 }
274 while (delta < (us_tick * delay));
275 }
276
277 /**
278 * @brief This function provides accurate delay (in milliseconds) based
279 * on variable incremented.
280 * @note In the default implementation, SysTick timer is the source of time base.
281 * It is used to generate interrupts at regular time intervals where lib_tick
282 * is incremented.
283 * @note This function is declared as __weak to be overwritten in case of other
284 * implementations in user file.
285 * @param delay: specifies the delay time length, in milliseconds.
286 * @retval None
287 */
ald_delay_ms(__IO uint32_t delay)288 __weak void ald_delay_ms(__IO uint32_t delay)
289 {
290 uint32_t tick, __delay;
291
292 switch (__systick_interval) {
293 case SYSTICK_INTERVAL_1MS:
294 __delay = delay;
295 break;
296
297 case SYSTICK_INTERVAL_10MS:
298 __delay = delay / 10;
299 break;
300
301 case SYSTICK_INTERVAL_100MS:
302 __delay = delay / 100;
303 break;
304
305 case SYSTICK_INTERVAL_1000MS:
306 __delay = delay / 1000;
307 break;
308
309 default:
310 __delay = delay;
311 break;
312 }
313
314 tick = ald_get_tick();
315 __delay = __delay == 0 ? 1 : __delay;
316
317 while ((ald_get_tick() - tick) < __delay)
318 ;
319 }
320
321 /**
322 * @brief Suspend Tick increment.
323 * @note In the default implementation, SysTick timer is the source of time base.
324 * It is used to generate interrupts at regular time intervals.
325 * Once ald_suspend_tick() is called, the the SysTick interrupt
326 * will be disabled and so Tick increment is suspended.
327 * @note This function is declared as __weak to be overwritten
328 * in case of other implementations in user file.
329 * @retval None
330 */
ald_suspend_tick(void)331 __weak void ald_suspend_tick(void)
332 {
333 CLEAR_BIT(SysTick->CTRL, SysTick_CTRL_TICKINT_Msk);
334 }
335
336 /**
337 * @brief Resume Tick increment.
338 * @note In the default implementation, SysTick timer is the source of
339 * time base. It is used to generate interrupts at regular time
340 * intervals. Once ald_resume_tick() is called, the the SysTick
341 * interrupt will be enabled and so Tick increment is resumed.
342 * @note This function is declared as __weak to be overwritten
343 * in case of other implementations in user file.
344 * @retval None
345 */
ald_resume_tick(void)346 __weak void ald_resume_tick(void)
347 {
348 SET_BIT(SysTick->CTRL, SysTick_CTRL_TICKINT_Msk);
349 }
350
351 /**
352 * @brief This method returns the ALD revision
353 * @retval version: 0xXYZR (8bits for each decimal, R for RC)
354 */
ald_get_ald_version(void)355 uint32_t ald_get_ald_version(void)
356 {
357 return __ALD_VERSION;
358 }
359
360 /**
361 * @brief Configure the flash wait period.
362 * @param cycle: The period.
363 * @retval None
364 */
ald_flash_wait_config(uint8_t cycle)365 void ald_flash_wait_config(uint8_t cycle)
366 {
367 uint32_t tmp;
368
369 tmp = MSC->MEMWAIT;
370 MODIFY_REG(tmp, MSC_MEMWAIT_FLASH_W_MSK, (0x30U | cycle) << MSC_MEMWAIT_FLASH_W_POSS);
371 MSC->MEMWAIT = tmp;
372
373 return;
374 }
375
376 /**
377 * @brief Waiting the specified bit in the register change to SET/RESET.
378 * @param reg: The register address.
379 * @param bit: The specified bit.
380 * @param status: The status for waiting.
381 * @param timeout: Timeout duration.
382 * @retval Status, see @ref ald_status_t.
383 */
ald_wait_flag(uint32_t * reg,uint32_t bit,flag_status_t status,uint32_t timeout)384 ald_status_t ald_wait_flag(uint32_t *reg, uint32_t bit, flag_status_t status, uint32_t timeout)
385 {
386 uint32_t tick = ald_get_tick();
387
388 assert_param(timeout > 0);
389
390 if (status == SET) {
391 while (!(IS_BIT_SET(*reg, bit))) {
392 if (((ald_get_tick()) - tick) > timeout)
393 return TIMEOUT;
394 }
395 }
396 else {
397 while ((IS_BIT_SET(*reg, bit))) {
398 if (((ald_get_tick()) - tick) > timeout)
399 return TIMEOUT;
400 }
401 }
402
403 return OK;
404 }
405
406 /**
407 * @brief Configure interrupt.
408 * @param irq: Interrunpt type.
409 * @param preempt_prio: preempt priority(0-3).
410 * @param sub_prio: sub-priority(0-3).
411 * @param status: Status.
412 * @arg ENABLE
413 * @arg DISABLE
414 * @retval None
415 */
ald_mcu_irq_config(IRQn_Type irq,uint8_t preempt_prio,uint8_t sub_prio,type_func_t status)416 void ald_mcu_irq_config(IRQn_Type irq, uint8_t preempt_prio, uint8_t sub_prio, type_func_t status)
417 {
418 uint32_t pri;
419 uint8_t sub_bw, pre_bw;
420 uint8_t sub_mask = 0xF;
421
422 assert_param(IS_FUNC_STATE(status));
423 assert_param(IS_PREEMPT_PRIO(preempt_prio));
424 assert_param(IS_SUB_PRIO(sub_prio));
425
426 if (status == ENABLE) {
427 pre_bw = 7 - (((SCB->AIRCR) >> 8) & 7);
428 sub_bw = 4 - pre_bw;
429 sub_mask >>= pre_bw;
430
431 pri = preempt_prio << sub_bw;
432 pri |= sub_prio & sub_mask;
433
434 NVIC_SetPriority(irq, pri);
435 NVIC_EnableIRQ(irq);
436 }
437 else {
438 NVIC_DisableIRQ(irq);
439 }
440
441 return;
442 }
443
444 /**
445 * @brief Initialize core timestamp.
446 * @retval None
447 */
ald_mcu_timestamp_init(void)448 void ald_mcu_timestamp_init(void)
449 {
450 DEM_CR |= (uint32_t)DEM_CR_TRCENA;
451 DWT_CYCCNT = 0x0;
452 DWT_CR |= (uint32_t)DWT_CR_CYCCNTEA;
453
454 return;
455 }
456
457 /**
458 * @brief Get core timestamp.
459 * @retval None
460 */
ald_mcu_get_timestamp(void)461 uint32_t ald_mcu_get_timestamp(void)
462 {
463 return (uint32_t)DWT_CYCCNT;
464 }
465
466 /**
467 * @brief Get the CPU ID.
468 * @retval CPU ID.
469 */
ald_mcu_get_cpu_id(void)470 uint32_t ald_mcu_get_cpu_id(void)
471 {
472 return SCB->CPUID;
473 }
474
475 /**
476 * @brief Get the UID.
477 * @param buf: Pointer to UID, len: 12Bytes(96-bits)
478 * @retval None
479 */
ald_mcu_get_uid(uint8_t * buf)480 void ald_mcu_get_uid(uint8_t *buf)
481 {
482 memcpy(&buf[0], (void *)MCU_UID0_ADDR, 4);
483 memcpy(&buf[4], (void *)MCU_UID1_ADDR, 4);
484 memcpy(&buf[8], (void *)MCU_UID2_ADDR, 4);
485
486 return;
487 }
488
489 /**
490 * @brief Get the CHIPID
491 * @retval CHPID
492 */
ald_mcu_get_chipid(void)493 uint32_t ald_mcu_get_chipid(void)
494 {
495 return (uint32_t)*(uint32_t *)MCU_CHIPID_ADDR;
496 }
497
498 /**
499 * @brief Bypass bootroom and set VR1_Ref 0xA
500 * @retval None
501 */
sys_config(void)502 void sys_config(void)
503 {
504 uint32_t i = 0, tmp = 0;
505 uint8_t err = 0, flag = 0;
506 uint32_t inf014 = 0, inf0154 = 0, inf0244 = 0;
507 uint8_t cnt = 4;
508
509 uint32_t *inf0_addr = (uint32_t *)0x20003C00;
510 /* read bootroom cfg register */
511 inf014 = *((uint32_t *)(0x80000 + 56));
512 /* read VR1_VREF register */
513 inf0154 = *((uint32_t *)(0x80000 + 616));
514 /* read Chip_v */
515 inf0244 = *((uint32_t *)(0x80000 + 0x03D0));
516
517 /* if D version ,do nothing */
518 if (inf0244 == 0xFFFFFF44) return;
519
520 if (inf0154 == 0xFFFFFFFF)
521 while(1);
522
523 /* if bypass bootroom */
524 if ((0xFFFFFFFF != inf014)) {
525 /* change cfg_boot value = 0xffff */
526 inf014 = 0xFFFFFFFF;
527 flag = 0x1;
528 }
529
530 /* change CFG_VR1_VREF value, FLASH ref 0xA */
531 tmp = (inf0154 >> 8) & 0xF;
532 if (0xA != tmp) {
533 inf0154 &= (uint32_t)~(0xF << 8);
534 inf0154 |= (0xA << 8);
535 inf0154 = (inf0154 & (0x0000FFFF)) | ((~(inf0154 & 0xFFFF)) << 16);
536 flag = 0x1;
537 }
538
539 /* if flag reset, return */
540 if (0x0 == flag)
541 return;
542
543 /* 0x80000, 256words,INFO0 value */
544 for (i = 0; i < 256; i++)
545 inf0_addr[i] = *((uint32_t *)(0x80000 + i * 4));
546
547 /* refresh value */
548 inf0_addr[14] = inf014;
549 inf0_addr[154] = inf0154;
550
551 while(--cnt) {
552 err = 0;
553 /* unlock */
554 *((volatile uint32_t *)(0x40080000)) = 0x55AA6996;
555 *((volatile uint32_t *)(0x40080100)) = 0x5A962814;
556 *((volatile uint32_t *)(0x40080100)) = 0xE7CB69A5;
557
558 /* erase */
559 if (ald_iap_erase_page(0x80000) == OK) {
560 /* program 256*4bytes, info0 */
561 if (ald_iap_program_words(0x80000, (uint8_t *)inf0_addr, 1024, 0) == OK) {
562 /* check */
563 for (i = 0; i < 256; i++) {
564 if (inf0_addr[i] != *((uint32_t *)(0x80000 + i * 4))) {
565 err = 1;
566 break;;
567 }
568 }
569 if (err == 0) {
570 /* lock */
571 *((volatile uint32_t *)(0x40080100)) = 0x123456;
572 *((volatile uint32_t *)(0x40080100)) = 0x123456;
573 *((volatile uint32_t *)(0x40080000)) = 0x123456;
574 return;
575 }
576 }
577 else {
578 err = 1;
579 }
580 }
581 else {
582 err = 1;
583 }
584 }
585
586 if (err) {
587 ald_iap_erase_page(0x80000);
588 /* lock */
589 *((volatile uint32_t *)(0x40080100)) = 0x123456;
590 *((volatile uint32_t *)(0x40080100)) = 0x123456;
591 *((volatile uint32_t *)(0x40080000)) = 0x123456;
592 while(1);
593 }
594 }
595
596 /**
597 * @brief ADC adjust parameter config
598 * @retval None
599 */
adc_config(void)600 void adc_config(void)
601 {
602 uint32_t inf0176 = 0, inf0178 = 0;
603 uint32_t inf0250 = 0, inf0251 = 0;
604 uint32_t inf0242 = 0, i = 0;
605 uint8_t flag = 0, err = 0;
606 uint8_t cnt = 4;
607 /* ram store inf0 1k buffer, 15k ~ 16k */
608 uint32_t *inf0_addr = (uint32_t *)0x20003C00;
609
610 /* Read ADC_GE */
611 inf0242 = *((uint32_t *)(0x80000 + 968));
612
613 if (0xF5230ADC == inf0242) return;
614
615 /* read Lot ID */
616 inf0250 = *((uint32_t *)(0x80000 + 1000));
617 inf0251 = *((uint32_t *)(0x80000 + 1004));
618 inf0251 = (inf0251 & 0xFFFF0000) >> 16;
619
620 /* read CFG_ADC0DA/CFG_ADC1DA */
621 inf0176 = *((uint32_t *)(0x80000 + 704));
622 inf0178 = *((uint32_t *)(0x80000 + 712));
623
624 switch(inf0250) {
625 case 0x45465537:
626 if ((inf0251 == 0x3034) || (inf0251 == 0x3035))
627 flag = 1;
628 break;
629 case 0x45503931:
630 if ((inf0251 == 0x3732) || (inf0251 == 0x3734))
631 flag = 1;
632 break;
633 case 0x45503935:
634 if ((inf0251 == 0x3837) || (inf0251 == 0x3839))
635 flag = 1;
636 break;
637 default:
638 break;
639 }
640
641 if (!flag) return;
642
643 inf0176 ^= (0x1 << 15);
644 inf0176 = (inf0176 & 0x0000FFFF) | ((~(inf0176 & 0xFFFF)) << 16);
645
646 inf0178 ^= (0x1 << 15);
647 inf0178 = (inf0178 & 0x0000FFFF) | ((~(inf0178 & 0xFFFF)) << 16);
648
649 /* 0x80000, 256words,INFO0 value */
650 for (i = 0; i < 256; i++)
651 inf0_addr[i] = *((uint32_t *)(0x80000 + i * 4));
652
653 inf0_addr[176] = inf0176;
654 inf0_addr[178] = inf0178;
655 inf0_addr[242] = 0xF5230ADC;
656
657 while(--cnt) {
658 err = 0;
659 /* unlock */
660 *((volatile uint32_t *)(0x40080000)) = 0x55AA6996;
661 *((volatile uint32_t *)(0x40080100)) = 0x5A962814;
662 *((volatile uint32_t *)(0x40080100)) = 0xE7CB69A5;
663
664 /* erase */
665 if (ald_iap_erase_page(0x80000) == OK) {
666 /* program 256*4bytes, info0 */
667 if (ald_iap_program_words(0x80000, (uint8_t *)inf0_addr, 1024, 0) == OK) {
668 /* check */
669 for (i = 0; i < 256; i++) {
670 if (inf0_addr[i] != *((uint32_t *)(0x80000 + i * 4))) {
671 err = 1;
672 break;;
673 }
674 }
675 if (err == 0) {
676 /* lock */
677 *((volatile uint32_t *)(0x40080100)) = 0x123456;
678 *((volatile uint32_t *)(0x40080100)) = 0x123456;
679 *((volatile uint32_t *)(0x40080000)) = 0x123456;
680 return;
681 }
682 }
683 else {
684 err = 1;
685 }
686 }
687 else {
688 err = 1;
689 }
690 }
691
692 if (err) {
693 ald_iap_erase_page(0x80000);
694 /* lock */
695 *((volatile uint32_t *)(0x40080100)) = 0x123456;
696 *((volatile uint32_t *)(0x40080100)) = 0x123456;
697 *((volatile uint32_t *)(0x40080000)) = 0x123456;
698 while(1);
699 }
700 }
701 /**
702 * @}
703 */
704
705 /**
706 * @}
707 */
708
709 /**
710 * @}
711 */
712
713 /**
714 * @}
715 */
716