aboutsummaryrefslogtreecommitdiffstats
path: root/os/hal/platforms
diff options
context:
space:
mode:
authorgdisirio <gdisirio@35acf78f-673a-0410-8e92-d51de3d6d3f4>2012-12-26 16:31:34 +0000
committergdisirio <gdisirio@35acf78f-673a-0410-8e92-d51de3d6d3f4>2012-12-26 16:31:34 +0000
commit1c4914bb0d7f7cf942cd5634d3f21e0c7a85a357 (patch)
tree293bbaca872452cd3f899fbdb76c4f159c2a0ca6 /os/hal/platforms
parentcc94e31e381fce2321081643ede52b1f8b39edb4 (diff)
downloadChibiOS-1c4914bb0d7f7cf942cd5634d3f21e0c7a85a357.tar.gz
ChibiOS-1c4914bb0d7f7cf942cd5634d3f21e0c7a85a357.tar.bz2
ChibiOS-1c4914bb0d7f7cf942cd5634d3f21e0c7a85a357.zip
git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@4976 35acf78f-673a-0410-8e92-d51de3d6d3f4
Diffstat (limited to 'os/hal/platforms')
-rw-r--r--os/hal/platforms/STM32F3xx/adc_lld.c111
-rw-r--r--os/hal/platforms/STM32F3xx/adc_lld.h198
2 files changed, 150 insertions, 159 deletions
diff --git a/os/hal/platforms/STM32F3xx/adc_lld.c b/os/hal/platforms/STM32F3xx/adc_lld.c
index 9821d3b3b..03781c0cf 100644
--- a/os/hal/platforms/STM32F3xx/adc_lld.c
+++ b/os/hal/platforms/STM32F3xx/adc_lld.c
@@ -35,12 +35,6 @@
/* Driver local definitions. */
/*===========================================================================*/
-#if STM32_ADC_USE_ADC12 || STM32_ADC_USE_ADC34
-#define STM32_ADC_DUAL_MODE TRUE
-#else
-#define STM32_ADC_DUAL_MODE FALSE
-#endif
-
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
@@ -50,6 +44,11 @@
ADCDriver ADCD1;
#endif
+/** @brief ADC1 driver identifier.*/
+#if STM32_ADC_USE_ADC3 || defined(__DOXYGEN__)
+ADCDriver ADCD3;
+#endif
+
/*===========================================================================*/
/* Driver local variables. */
/*===========================================================================*/
@@ -78,7 +77,7 @@ static void adc_lld_stop_adc(ADC_TypeDef *adc) {
* @param[in] adcp pointer to the @p ADCDriver object
* @param[in] flags pre-shifted content of the ISR register
*/
-static void adc_lld_serve_rx_interrupt(ADCDriver *adcp, uint32_t flags) {
+static void adc_lld_serve_dma_interrupt(ADCDriver *adcp, uint32_t flags) {
/* DMA errors handling.*/
if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) {
@@ -102,44 +101,102 @@ static void adc_lld_serve_rx_interrupt(ADCDriver *adcp, uint32_t flags) {
}
}
-/*===========================================================================*/
-/* Driver interrupt handlers. */
-/*===========================================================================*/
-
-#if STM32_ADC_USE_ADC1 || defined(__DOXYGEN__)
/**
- * @brief ADC interrupt handler.
+ * @brief ADC ISR service routine.
*
- * @isr
+ * @param[in] adcp pointer to the @p ADCDriver object
+ * @param[in] isr pre-shifted content of the ISR register
*/
-CH_IRQ_HANDLER(ADC1_COMP_IRQHandler) {
- uint32_t isr;
-
- CH_IRQ_PROLOGUE();
-
- isr = ADC1->ISR;
- ADC1->ISR = isr;
+static void adc_lld_serve_interrupt(ADCDriver *adcp, uint32_t isr) {
/* It could be a spurious interrupt caused by overflows after DMA disabling,
just ignore it in this case.*/
- if (ADCD1.grpp != NULL) {
+ if (adcp->grpp != NULL) {
/* Note, an overflow may occur after the conversion ended before the driver
is able to stop the ADC, this is why the DMA channel is checked too.*/
if ((isr & ADC_ISR_OVR) &&
- (dmaStreamGetTransactionSize(ADCD1.dmastp) > 0)) {
+ (dmaStreamGetTransactionSize(adcp->dmastp) > 0)) {
/* ADC overflow condition, this could happen only if the DMA is unable
to read data fast enough.*/
- _adc_isr_error_code(&ADCD1, ADC_ERR_OVERFLOW);
+ _adc_isr_error_code(adcp, ADC_ERR_OVERFLOW);
}
if (isr & ADC_ISR_AWD) {
/* Analog watchdog error.*/
- _adc_isr_error_code(&ADCD1, ADC_ERR_AWD);
+ _adc_isr_error_code(adcp, ADC_ERR_AWD);
}
}
+}
+
+/*===========================================================================*/
+/* Driver interrupt handlers. */
+/*===========================================================================*/
+
+#if STM32_ADC_USE_ADC1 || defined(__DOXYGEN__)
+/**
+ * @brief ADC1/ADC2 interrupt handler.
+ *
+ * @isr
+ */
+CH_IRQ_HANDLER(Vector88) {
+ uint32_t isr;
+
+ CH_IRQ_PROLOGUE();
+
+#if STM32_ADC_DUAL_MODE
+ isr = ADC1->ISR;
+ isr |= ADC2->ISR;
+ ADC1->ISR = isr;
+ ADC2->ISR = isr;
+#else /* !STM32_ADC_DUAL_MODE */
+ isr = ADC1->ISR;
+ ADC1->ISR = isr;
+#endif /* !STM32_ADC_DUAL_MODE */
+
+ adc_lld_serve_interrupt(&ADCD1, isr);
CH_IRQ_EPILOGUE();
}
-#endif
+#endif /* STM32_ADC_USE_ADC1 */
+
+#if STM32_ADC_USE_ADC3 || defined(__DOXYGEN__)
+/**
+ * @brief ADC3 interrupt handler.
+ *
+ * @isr
+ */
+CH_IRQ_HANDLER(VectorFC) {
+ uint32_t isr;
+
+ CH_IRQ_PROLOGUE();
+
+ isr = ADC3->ISR;
+ ADC3->ISR = isr;
+
+ adc_lld_serve_interrupt(&ADCD3, isr);
+
+ CH_IRQ_EPILOGUE();
+}
+
+#if STM32_ADC_DUAL_MODE
+/**
+ * @brief ADC4 interrupt handler (as ADC3 slave).
+ *
+ * @isr
+ */
+CH_IRQ_HANDLER(Vector134) {
+ uint32_t isr;
+
+ CH_IRQ_PROLOGUE();
+
+ isr = ADC4->ISR;
+ ADC4->ISR = isr;
+
+ adc_lld_serve_interrupt(&ADCD3, isr);
+
+ CH_IRQ_EPILOGUE();
+}
+#endif /* STM32_ADC_DUAL_MODE */
+#endif /* STM32_ADC_USE_ADC3 */
/*===========================================================================*/
/* Driver exported functions. */
@@ -194,7 +251,7 @@ void adc_lld_start(ADCDriver *adcp) {
bool_t b;
b = dmaStreamAllocate(adcp->dmastp,
STM32_ADC_ADC1_DMA_IRQ_PRIORITY,
- (stm32_dmaisr_t)adc_lld_serve_rx_interrupt,
+ (stm32_dmaisr_t)adc_lld_serve_dma_interrupt,
(void *)adcp);
chDbgAssert(!b, "adc_lld_start(), #1", "stream already allocated");
dmaStreamSetPeripheral(adcp->dmastp, &ADC1->DR);
diff --git a/os/hal/platforms/STM32F3xx/adc_lld.h b/os/hal/platforms/STM32F3xx/adc_lld.h
index 7429e6928..215b8fe4e 100644
--- a/os/hal/platforms/STM32F3xx/adc_lld.h
+++ b/os/hal/platforms/STM32F3xx/adc_lld.h
@@ -152,14 +152,7 @@
#if !defined(STM32_ADC_USE_ADC1) || defined(__DOXYGEN__)
#define STM32_ADC_USE_ADC1 FALSE
#endif
-/**
- * @brief ADC1+ADC2 driver enable switch.
- * @details If set to @p TRUE the support for ADC1+ADC2 is included.
- * @note The default is @p FALSE.
- */
-#if !defined(STM32_ADC_USE_ADC12) || defined(__DOXYGEN__)
-#define STM32_ADC_USE_ADC12 FALSE
-#endif
+
/**
* @brief ADC3 driver enable switch.
* @details If set to @p TRUE the support for ADC3 is included.
@@ -168,69 +161,68 @@
#if !defined(STM32_ADC_USE_ADC3) || defined(__DOXYGEN__)
#define STM32_ADC_USE_ADC3 FALSE
#endif
-/**
- * @brief ADC3+ADC4 driver enable switch.
- * @details If set to @p TRUE the support for ADC3+ADC4 is included.
- * @note The default is @p FALSE.
- */
-#if !defined(STM32_ADC_USE_ADC34) || defined(__DOXYGEN__)
-#define STM32_ADC_USE_ADC34 FALSE
-#endif
/**
* @brief ADC1/ADC2 DMA priority (0..3|lowest..highest).
*/
-#if !defined(STM32_ADC_ADC12_DMA_PRIORITY) || defined(__DOXYGEN__)
-#define STM32_ADC_ADC12_DMA_PRIORITY 2
+#if !defined(STM32_ADC_ADC1_DMA_PRIORITY) || defined(__DOXYGEN__)
+#define STM32_ADC_ADC1_DMA_PRIORITY 2
#endif
/**
* @brief ADC3/ADC4 DMA priority (0..3|lowest..highest).
*/
-#if !defined(STM32_ADC_ADC34_DMA_PRIORITY) || defined(__DOXYGEN__)
-#define STM32_ADC_ADC34_DMA_PRIORITY 2
+#if !defined(STM32_ADC_ADC3_DMA_PRIORITY) || defined(__DOXYGEN__)
+#define STM32_ADC_ADC3_DMA_PRIORITY 2
#endif
/**
* @brief ADC1/ADC2 interrupt priority level setting.
*/
-#if !defined(STM32_ADC_ADC12_IRQ_PRIORITY) || defined(__DOXYGEN__)
-#define STM32_ADC_ADC12_IRQ_PRIORITY 2
+#if !defined(STM32_ADC_ADC1_IRQ_PRIORITY) || defined(__DOXYGEN__)
+#define STM32_ADC_ADC1_IRQ_PRIORITY 2
#endif
/**
* @brief ADC3/ADC4 interrupt priority level setting.
*/
-#if !defined(STM32_ADC34_IRQ_PRIORITY) || defined(__DOXYGEN__)
-#define STM32_ADC_ADC34_IRQ_PRIORITY 2
+#if !defined(STM32_ADC3_IRQ_PRIORITY) || defined(__DOXYGEN__)
+#define STM32_ADC_ADC3_IRQ_PRIORITY 2
#endif
/**
* @brief ADC1/ADC2 DMA interrupt priority level setting.
*/
-#if !defined(STM32_ADC_ADC12_DMA_IRQ_PRIORITY) || defined(__DOXYGEN__)
-#define STM32_ADC_ADC12_DMA_IRQ_PRIORITY 2
+#if !defined(STM32_ADC_ADC1_DMA_IRQ_PRIORITY) || defined(__DOXYGEN__)
+#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY 2
#endif
/**
* @brief ADC3/ADC4 DMA interrupt priority level setting.
*/
-#if !defined(STM32_ADC_ADC34_DMA_IRQ_PRIORITY) || defined(__DOXYGEN__)
-#define STM32_ADC_ADC34_DMA_IRQ_PRIORITY 2
+#if !defined(STM32_ADC_ADC3_DMA_IRQ_PRIORITY) || defined(__DOXYGEN__)
+#define STM32_ADC_ADC3_DMA_IRQ_PRIORITY 2
#endif
/**
* @brief ADC1/ADC2 clock source and mode.
*/
-#if !defined(STM32_ADC_ADC12_CLOCK_MODE) || defined(__DOXYGEN__)
-#define STM32_ADC_ADC12_CLOCK_MODE ADC_CCR_CKMODE_AHB_DIV1
+#if !defined(STM32_ADC_ADC1_CLOCK_MODE) || defined(__DOXYGEN__)
+#define STM32_ADC_ADC1_CLOCK_MODE ADC_CCR_CKMODE_AHB_DIV1
#endif
/**
* @brief ADC3/ADC4 clock source and mode.
*/
-#if !defined(STM32_ADC_ADC34_CLOCK_MODE) || defined(__DOXYGEN__)
-#define STM32_ADC_ADC34_CLOCK_MODE ADC_CCR_CKMODE_AHB_DIV1
+#if !defined(STM32_ADC_ADC3_CLOCK_MODE) || defined(__DOXYGEN__)
+#define STM32_ADC_ADC3_CLOCK_MODE ADC_CCR_CKMODE_AHB_DIV1
+#endif
+
+/**
+ * @brief Enables the ADC master/slave mode.
+ */
+#if !defined(STM32_ADC_DUAL_MODE) || defined(__DOXYGEN__)
+#define STM32_ADC_DUAL_MODE FALSE
#endif
/** @} */
@@ -242,113 +234,74 @@
#error "ADC1 not present in the selected device"
#endif
-#if STM32_ADC_USE_ADC12 && (!STM32_HAS_ADC1 || !STM32_HAS_ADC2)
-#error "ADC12 not present in the selected device"
-#endif
-
#if STM32_ADC_USE_ADC3 && !STM32_HAS_ADC3
#error "ADC3 not present in the selected device"
#endif
-#if STM32_ADC_USE_ADC34 && (!STM32_HAS_ADC3 || !STM32_HAS_ADC4)
-#error "ADC34 not present in the selected device"
-#endif
-
-#if !STM32_ADC_USE_ADC1 || !STM32_ADC_USE_ADC12 || \
- !STM32_ADC_USE_ADC3 || !STM32_ADC_USE_ADC34
+#if !STM32_ADC_USE_ADC1 ||!STM32_ADC_USE_ADC3
#error "ADC driver activated but no ADC peripheral assigned"
#endif
#if STM32_ADC_USE_ADC1 && \
- !CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_ADC_ADC12_IRQ_PRIORITY)
+ !CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_ADC_ADC1_IRQ_PRIORITY)
#error "Invalid IRQ priority assigned to ADC1"
#endif
#if STM32_ADC_USE_ADC1 && \
- !CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_ADC_ADC12_DMA_IRQ_PRIORITY)
+ !CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_ADC_ADC1_DMA_IRQ_PRIORITY)
#error "Invalid IRQ priority assigned to ADC1 DMA"
#endif
#if STM32_ADC_USE_ADC1 && \
- !STM32_DMA_IS_VALID_PRIORITY(STM32_ADC_ADC12_DMA_PRIORITY)
+ !STM32_DMA_IS_VALID_PRIORITY(STM32_ADC_ADC1_DMA_PRIORITY)
#error "Invalid DMA priority assigned to ADC1"
#endif
-#if STM32_ADC_USE_ADC12 && \
- !CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_ADC_ADC12_IRQ_PRIORITY)
-#error "Invalid IRQ priority assigned to ADC12"
-#endif
-
-#if STM32_ADC_USE_ADC12 && \
- !CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_ADC_ADC12_DMA_IRQ_PRIORITY)
-#error "Invalid IRQ priority assigned to ADC12 DMA"
-#endif
-
-#if STM32_ADC_USE_ADC12 && \
- !STM32_DMA_IS_VALID_PRIORITY(STM32_ADC_ADC12_DMA_PRIORITY)
-#error "Invalid DMA priority assigned to ADC12"
-#endif
-
#if STM32_ADC_USE_ADC3 && \
- !CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_ADC_ADC34_IRQ_PRIORITY)
+ !CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_ADC_ADC3_IRQ_PRIORITY)
#error "Invalid IRQ priority assigned to ADC3"
#endif
#if STM32_ADC_USE_ADC3 && \
- !CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_ADC_ADC34_DMA_IRQ_PRIORITY)
+ !CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_ADC_ADC3_DMA_IRQ_PRIORITY)
#error "Invalid IRQ priority assigned to ADC3 DMA"
#endif
#if STM32_ADC_USE_ADC3 && \
- !STM32_DMA_IS_VALID_PRIORITY(STM32_ADC_ADC34_DMA_PRIORITY)
+ !STM32_DMA_IS_VALID_PRIORITY(STM32_ADC_ADC3_DMA_PRIORITY)
#error "Invalid DMA priority assigned to ADC3"
#endif
-#if STM32_ADC_USE_ADC34 && \
- !CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_ADC_ADC34_IRQ_PRIORITY)
-#error "Invalid IRQ priority assigned to ADC34"
-#endif
-
-#if STM32_ADC_USE_ADC34 && \
- !CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_ADC_ADC34_DMA_IRQ_PRIORITY)
-#error "Invalid IRQ priority assigned to ADC34 DMA"
-#endif
-
-#if STM32_ADC_USE_ADC34 && \
- !STM32_DMA_IS_VALID_PRIORITY(STM32_ADC_ADC34_DMA_PRIORITY)
-#error "Invalid DMA priority assigned to ADC34"
-#endif
-
-#if STM32_ADC_ADC12_CLOCK_MODE == ADC_CCR_CKMODE_ADCCK
-#define STM32_ADC12_CLOCK STM32ADC12CLK
-#elif STM32_ADC_ADC12_CLOCK_MODE == ADC_CCR_CKMODE_AHB_DIV1
-#define STM32_ADC12_CLOCK (STM32_HCLK / 1)
-#elif STM32_ADC_ADC12_CLOCK_MODE == ADC_CCR_CKMODE_AHB_DIV2
-#define STM32_ADC12_CLOCK (STM32_HCLK / 2)
-#elif STM32_ADC_ADC12_CLOCK_MODE == ADC_CCR_CKMODE_AHB_DIV4
-#define STM32_ADC12_CLOCK (STM32_HCLK / 4)
+#if STM32_ADC_ADC1_CLOCK_MODE == ADC_CCR_CKMODE_ADCCK
+#define STM32_ADC1_CLOCK STM32ADC1CLK
+#elif STM32_ADC_ADC1_CLOCK_MODE == ADC_CCR_CKMODE_AHB_DIV1
+#define STM32_ADC1_CLOCK (STM32_HCLK / 1)
+#elif STM32_ADC_ADC1_CLOCK_MODE == ADC_CCR_CKMODE_AHB_DIV2
+#define STM32_ADC1_CLOCK (STM32_HCLK / 2)
+#elif STM32_ADC_ADC1_CLOCK_MODE == ADC_CCR_CKMODE_AHB_DIV4
+#define STM32_ADC1_CLOCK (STM32_HCLK / 4)
#else
-#error "invalid clock mode selected for STM32_ADC_ADC12_CLOCK_MODE"
+#error "invalid clock mode selected for STM32_ADC_ADC1_CLOCK_MODE"
#endif
-#if STM32_ADC_ADC34_CLOCK_MODE == ADC_CCR_CKMODE_ADCCK
-#define STM32_ADC34_CLOCK STM32ADC34CLK
-#elif STM32_ADC_ADC34_CLOCK_MODE == ADC_CCR_CKMODE_AHB_DIV1
-#define STM32_ADC34_CLOCK (STM32_HCLK / 1)
-#elif STM32_ADC_ADC34_CLOCK_MODE == ADC_CCR_CKMODE_AHB_DIV2
-#define STM32_ADC34_CLOCK (STM32_HCLK / 2)
-#elif STM32_ADC_ADC34_CLOCK_MODE == ADC_CCR_CKMODE_AHB_DIV4
-#define STM32_ADC34_CLOCK (STM32_HCLK / 4)
+#if STM32_ADC_ADC3_CLOCK_MODE == ADC_CCR_CKMODE_ADCCK
+#define STM32_ADC3_CLOCK STM32ADC3CLK
+#elif STM32_ADC_ADC3_CLOCK_MODE == ADC_CCR_CKMODE_AHB_DIV1
+#define STM32_ADC3_CLOCK (STM32_HCLK / 1)
+#elif STM32_ADC_ADC3_CLOCK_MODE == ADC_CCR_CKMODE_AHB_DIV2
+#define STM32_ADC3_CLOCK (STM32_HCLK / 2)
+#elif STM32_ADC_ADC3_CLOCK_MODE == ADC_CCR_CKMODE_AHB_DIV4
+#define STM32_ADC3_CLOCK (STM32_HCLK / 4)
#else
-#error "invalid clock mode selected for STM32_ADC_ADC12_CLOCK_MODE"
+#error "invalid clock mode selected for STM32_ADC_ADC1_CLOCK_MODE"
#endif
-#if STM32_ADC12_CLOCK > 72000000
-#error "STM32_ADC12_CLOCK exceeding maximum frequency (72000000)"
+#if STM32_ADC1_CLOCK > 72000000
+#error "STM32_ADC1_CLOCK exceeding maximum frequency (72000000)"
#endif
-#if STM32_ADC34_CLOCK > 72000000
-#error "STM32_ADC34_CLOCK exceeding maximum frequency (72000000)"
+#if STM32_ADC3_CLOCK > 72000000
+#error "STM32_ADC3_CLOCK exceeding maximum frequency (72000000)"
#endif
#if !defined(STM32_DMA_REQUIRED)
@@ -442,37 +395,24 @@ typedef struct {
*/
uint32_t tr1;
/**
- * @brief ADC SMPR1 register initialization data.
- * @details In this field must be specified the sample times for channels
- * 0...9.
+ * @brief ADC SMPRx registers initialization data.
*/
- uint32_t smpr1;
+ uint32_t smpr[2];
/**
- * @brief ADC SMPR2 register initialization data.
- * @details In this field must be specified the sample times for channels
- * 10...18.
+ * @brief ADC SQRx register initialization data.
*/
- uint32_t smpr2;
+ uint32_t sqr[4];
+#if STM32_ADC_DUAL_MODE || defined(__DOXYGEN__)
/**
- * @brief ADC SQR1 register initialization data.
- * @details Conversion group sequence 1...4 + sequence length.
+ * @brief Slave ADC SMPRx registers initialization data.
*/
- uint32_t sqr1;
+ uint32_t ssmpr[2];
/**
- * @brief ADC SQR2 register initialization data.
- * @details Conversion group sequence 5...9.
+ * @brief Slave ADC SQRx register initialization data.
*/
- uint32_t sqr2;
- /**
- * @brief ADC SQR3 register initialization data.
- * @details Conversion group sequence 10...15.
- */
- uint32_t sqr3;
- /**
- * @brief ADC SQR4 register initialization data.
- * @details Conversion group sequence 16...17.
- */
- uint32_t sqr4;
+ uint32_t ssqr[4];
+
+#endif /* STM32_ADC_DUAL_MODE */
} ADCConversionGroup;
/**
@@ -531,10 +471,12 @@ struct ADCDriver {
* @brief Pointer to the master ADCx registers block.
*/
ADC_TypeDef *adcm;
+#if STM32_ADC_DUAL_MODE || defined(__DOXYGEN__)
/**
* @brief Pointer to the slave ADCx registers block.
*/
ADC_TypeDef *adcs;
+#endif /* STM32_ADC_DUAL_MODE */
/**
* @brief Pointer to associated DMA channel.
*/
@@ -612,18 +554,10 @@ struct ADCDriver {
extern ADCDriver ADCD1;
#endif
-#if STM32_ADC_USE_ADC12 && !defined(__DOXYGEN__)
-extern ADCDriver ADCD12;
-#endif
-
#if STM32_ADC_USE_ADC3 && !defined(__DOXYGEN__)
extern ADCDriver ADCD3;
#endif
-#if STM32_ADC_USE_ADC34 && !defined(__DOXYGEN__)
-extern ADCDriver ADCD34;
-#endif
-
#ifdef __cplusplus
extern "C" {
#endif