diff options
author | Giovanni Di Sirio <gdisirio@gmail.com> | 2015-08-05 14:40:07 +0000 |
---|---|---|
committer | Giovanni Di Sirio <gdisirio@gmail.com> | 2015-08-05 14:40:07 +0000 |
commit | adb186c90adbf6ae1fc37bbc68673943286715bf (patch) | |
tree | 490418b377c22c2ee664faf7d2e8a554f5a63317 /os | |
parent | 0f926c98c2daf116eb94edcc752dce1d4f4f7456 (diff) | |
download | ChibiOS-adb186c90adbf6ae1fc37bbc68673943286715bf.tar.gz ChibiOS-adb186c90adbf6ae1fc37bbc68673943286715bf.tar.bz2 ChibiOS-adb186c90adbf6ae1fc37bbc68673943286715bf.zip |
Added support for I2C3 and I2C4 to the STM32 I2Cv2 I2C driver.
git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@8164 35acf78f-673a-0410-8e92-d51de3d6d3f4
Diffstat (limited to 'os')
-rw-r--r-- | os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.c | 262 | ||||
-rw-r--r-- | os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.h | 125 | ||||
-rw-r--r-- | os/hal/ports/STM32/STM32F7xx/stm32_rcc.h | 25 |
3 files changed, 411 insertions, 1 deletions
diff --git a/os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.c b/os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.c index a870a40ae..e0bc31979 100644 --- a/os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.c +++ b/os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.c @@ -51,6 +51,22 @@ #define I2C2_TX_DMA_CHANNEL \
STM32_DMA_GETCHANNEL(STM32_I2C_I2C2_TX_DMA_STREAM, \
STM32_I2C2_TX_DMA_CHN)
+
+#define I2C3_RX_DMA_CHANNEL \
+ STM32_DMA_GETCHANNEL(STM32_I2C_I2C3_RX_DMA_STREAM, \
+ STM32_I2C3_RX_DMA_CHN)
+
+#define I2C3_TX_DMA_CHANNEL \
+ STM32_DMA_GETCHANNEL(STM32_I2C_I2C3_TX_DMA_STREAM, \
+ STM32_I2C3_TX_DMA_CHN)
+
+#define I2C4_RX_DMA_CHANNEL \
+ STM32_DMA_GETCHANNEL(STM32_I2C_I2C4_RX_DMA_STREAM, \
+ STM32_I2C4_RX_DMA_CHN)
+
+#define I2C4_TX_DMA_CHANNEL \
+ STM32_DMA_GETCHANNEL(STM32_I2C_I2C4_TX_DMA_STREAM, \
+ STM32_I2C4_TX_DMA_CHN)
#endif /* STM32_I2C_USE_DMA == TRUE */
#if STM32_I2C_USE_DMA == TRUE
@@ -87,6 +103,16 @@ I2CDriver I2CD1; I2CDriver I2CD2;
#endif
+/** @brief I2C3 driver identifier.*/
+#if STM32_I2C_USE_I2C3 || defined(__DOXYGEN__)
+I2CDriver I2CD3;
+#endif
+
+/** @brief I2C4 driver identifier.*/
+#if STM32_I2C_USE_I2C4 || defined(__DOXYGEN__)
+I2CDriver I2CD4;
+#endif
+
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
@@ -474,6 +500,116 @@ OSAL_IRQ_HANDLER(STM32_I2C2_ERROR_HANDLER) { #endif
#endif /* STM32_I2C_USE_I2C2 */
+#if STM32_I2C_USE_I2C3 || defined(__DOXYGEN__)
+#if defined(STM32_I2C3_GLOBAL_HANDLER) || defined(__DOXYGEN__)
+/**
+ * @brief I2C3 event interrupt handler.
+ *
+ * @notapi
+ */
+OSAL_IRQ_HANDLER(STM32_I2C3_GLOBAL_HANDLER) {
+ uint32_t isr = I2CD3.i2c->ISR;
+
+ OSAL_IRQ_PROLOGUE();
+
+ /* Clearing IRQ bits.*/
+ I2CD3.i2c->ICR = isr;
+
+ if (isr & I2C_ERROR_MASK)
+ i2c_lld_serve_error_interrupt(&I2CD3, isr);
+ else if (isr & I2C_INT_MASK)
+ i2c_lld_serve_interrupt(&I2CD3, isr);
+
+ OSAL_IRQ_EPILOGUE();
+}
+
+#elif defined(STM32_I2C3_EVENT_HANDLER) && defined(STM32_I2C3_ERROR_HANDLER)
+OSAL_IRQ_HANDLER(STM32_I2C3_EVENT_HANDLER) {
+ uint32_t isr = I2CD3.i2c->ISR;
+
+ OSAL_IRQ_PROLOGUE();
+
+ /* Clearing IRQ bits.*/
+ I2CD3.i2c->ICR = isr & I2C_INT_MASK;
+
+ i2c_lld_serve_interrupt(&I2CD3, isr);
+
+ OSAL_IRQ_EPILOGUE();
+}
+
+OSAL_IRQ_HANDLER(STM32_I2C3_ERROR_HANDLER) {
+ uint32_t isr = I2CD3.i2c->ISR;
+
+ OSAL_IRQ_PROLOGUE();
+
+ /* Clearing IRQ bits.*/
+ I2CD3.i2c->ICR = isr & I2C_ERROR_MASK;
+
+ i2c_lld_serve_error_interrupt(&I2CD3, isr);
+
+ OSAL_IRQ_EPILOGUE();
+}
+
+#else
+#error "I2C3 interrupt handlers not defined"
+#endif
+#endif /* STM32_I2C_USE_I2C3 */
+
+#if STM32_I2C_USE_I2C4 || defined(__DOXYGEN__)
+#if defined(STM32_I2C4_GLOBAL_HANDLER) || defined(__DOXYGEN__)
+/**
+ * @brief I2C4 event interrupt handler.
+ *
+ * @notapi
+ */
+OSAL_IRQ_HANDLER(STM32_I2C4_GLOBAL_HANDLER) {
+ uint32_t isr = I2CD4.i2c->ISR;
+
+ OSAL_IRQ_PROLOGUE();
+
+ /* Clearing IRQ bits.*/
+ I2CD4.i2c->ICR = isr;
+
+ if (isr & I2C_ERROR_MASK)
+ i2c_lld_serve_error_interrupt(&I2CD4, isr);
+ else if (isr & I2C_INT_MASK)
+ i2c_lld_serve_interrupt(&I2CD4, isr);
+
+ OSAL_IRQ_EPILOGUE();
+}
+
+#elif defined(STM32_I2C4_EVENT_HANDLER) && defined(STM32_I2C4_ERROR_HANDLER)
+OSAL_IRQ_HANDLER(STM32_I2C4_EVENT_HANDLER) {
+ uint32_t isr = I2CD4.i2c->ISR;
+
+ OSAL_IRQ_PROLOGUE();
+
+ /* Clearing IRQ bits.*/
+ I2CD4.i2c->ICR = isr & I2C_INT_MASK;
+
+ i2c_lld_serve_interrupt(&I2CD4, isr);
+
+ OSAL_IRQ_EPILOGUE();
+}
+
+OSAL_IRQ_HANDLER(STM32_I2C4_ERROR_HANDLER) {
+ uint32_t isr = I2CD4.i2c->ISR;
+
+ OSAL_IRQ_PROLOGUE();
+
+ /* Clearing IRQ bits.*/
+ I2CD4.i2c->ICR = isr & I2C_ERROR_MASK;
+
+ i2c_lld_serve_error_interrupt(&I2CD4, isr);
+
+ OSAL_IRQ_EPILOGUE();
+}
+
+#else
+#error "I2C4 interrupt handlers not defined"
+#endif
+#endif /* STM32_I2C_USE_I2C4 */
+
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
@@ -504,6 +640,26 @@ void i2c_lld_init(void) { I2CD2.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C2_TX_DMA_STREAM);
#endif
#endif /* STM32_I2C_USE_I2C2 */
+
+#if STM32_I2C_USE_I2C3
+ i2cObjectInit(&I2CD3);
+ I2CD3.thread = NULL;
+ I2CD3.i2c = I2C3;
+#if STM32_I2C_USE_DMA == TRUE
+ I2CD3.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C3_RX_DMA_STREAM);
+ I2CD3.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C3_TX_DMA_STREAM);
+#endif
+#endif /* STM32_I2C_USE_I2C3 */
+
+#if STM32_I2C_USE_I2C4
+ i2cObjectInit(&I2CD4);
+ I2CD4.thread = NULL;
+ I2CD4.i2c = I2C4;
+#if STM32_I2C_USE_DMA == TRUE
+ I2CD4.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C4_RX_DMA_STREAM);
+ I2CD4.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C4_TX_DMA_STREAM);
+#endif
+#endif /* STM32_I2C_USE_I2C4 */
}
/**
@@ -603,6 +759,82 @@ void i2c_lld_start(I2CDriver *i2cp) { #endif
}
#endif /* STM32_I2C_USE_I2C2 */
+
+#if STM32_I2C_USE_I2C3
+ if (&I2CD3 == i2cp) {
+
+ rccResetI2C3();
+ rccEnableI2C3(FALSE);
+#if STM32_I2C_USE_DMA == TRUE
+ {
+ bool b;
+
+ b = dmaStreamAllocate(i2cp->dmarx,
+ STM32_I2C_I2C3_IRQ_PRIORITY,
+ NULL,
+ (void *)i2cp);
+ osalDbgAssert(!b, "stream already allocated");
+ b = dmaStreamAllocate(i2cp->dmatx,
+ STM32_I2C_I2C3_IRQ_PRIORITY,
+ NULL,
+ (void *)i2cp);
+ osalDbgAssert(!b, "stream already allocated");
+
+ i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C3_RX_DMA_CHANNEL) |
+ STM32_DMA_CR_PL(STM32_I2C_I2C3_DMA_PRIORITY);
+ i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C3_TX_DMA_CHANNEL) |
+ STM32_DMA_CR_PL(STM32_I2C_I2C3_DMA_PRIORITY);
+ }
+#endif /*STM32_I2C_USE_DMA == TRUE */
+
+#if defined(STM32_I2C3_GLOBAL_NUMBER) || defined(__DOXYGEN__)
+ nvicEnableVector(STM32_I2C3_GLOBAL_NUMBER, STM32_I2C_I2C3_IRQ_PRIORITY);
+#elif defined(STM32_I2C3_EVENT_NUMBER) && defined(STM32_I2C3_ERROR_NUMBER)
+ nvicEnableVector(STM32_I2C3_EVENT_NUMBER, STM32_I2C_I2C3_IRQ_PRIORITY);
+ nvicEnableVector(STM32_I2C3_ERROR_NUMBER, STM32_I2C_I2C3_IRQ_PRIORITY);
+#else
+#error "I2C3 interrupt numbers not defined"
+#endif
+ }
+#endif /* STM32_I2C_USE_I2C3 */
+
+#if STM32_I2C_USE_I2C4
+ if (&I2CD4 == i2cp) {
+
+ rccResetI2C4();
+ rccEnableI2C4(FALSE);
+#if STM32_I2C_USE_DMA == TRUE
+ {
+ bool b;
+
+ b = dmaStreamAllocate(i2cp->dmarx,
+ STM32_I2C_I2C4_IRQ_PRIORITY,
+ NULL,
+ (void *)i2cp);
+ osalDbgAssert(!b, "stream already allocated");
+ b = dmaStreamAllocate(i2cp->dmatx,
+ STM32_I2C_I2C4_IRQ_PRIORITY,
+ NULL,
+ (void *)i2cp);
+ osalDbgAssert(!b, "stream already allocated");
+
+ i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C4_RX_DMA_CHANNEL) |
+ STM32_DMA_CR_PL(STM32_I2C_I2C4_DMA_PRIORITY);
+ i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C4_TX_DMA_CHANNEL) |
+ STM32_DMA_CR_PL(STM32_I2C_I2C4_DMA_PRIORITY);
+ }
+#endif /*STM32_I2C_USE_DMA == TRUE */
+
+#if defined(STM32_I2C4_GLOBAL_NUMBER) || defined(__DOXYGEN__)
+ nvicEnableVector(STM32_I2C4_GLOBAL_NUMBER, STM32_I2C_I2C4_IRQ_PRIORITY);
+#elif defined(STM32_I2C4_EVENT_NUMBER) && defined(STM32_I2C4_ERROR_NUMBER)
+ nvicEnableVector(STM32_I2C4_EVENT_NUMBER, STM32_I2C_I2C4_IRQ_PRIORITY);
+ nvicEnableVector(STM32_I2C4_ERROR_NUMBER, STM32_I2C_I2C4_IRQ_PRIORITY);
+#else
+#error "I2C4 interrupt numbers not defined"
+#endif
+ }
+#endif /* STM32_I2C_USE_I2C4 */
}
#if STM32_I2C_USE_DMA == TRUE
@@ -670,6 +902,36 @@ void i2c_lld_stop(I2CDriver *i2cp) { rccDisableI2C2(FALSE);
}
#endif
+
+#if STM32_I2C_USE_I2C3
+ if (&I2CD3 == i2cp) {
+#if defined(STM32_I2C3_GLOBAL_NUMBER) || defined(__DOXYGEN__)
+ nvicDisableVector(STM32_I2C3_GLOBAL_NUMBER);
+#elif defined(STM32_I2C3_EVENT_NUMBER) && defined(STM32_I2C3_ERROR_NUMBER)
+ nvicDisableVector(STM32_I2C3_EVENT_NUMBER);
+ nvicDisableVector(STM32_I2C3_ERROR_NUMBER);
+#else
+#error "I2C3 interrupt numbers not defined"
+#endif
+
+ rccDisableI2C3(FALSE);
+ }
+#endif
+
+#if STM32_I2C_USE_I2C4
+ if (&I2CD4 == i2cp) {
+#if defined(STM32_I2C4_GLOBAL_NUMBER) || defined(__DOXYGEN__)
+ nvicDisableVector(STM32_I2C4_GLOBAL_NUMBER);
+#elif defined(STM32_I2C4_EVENT_NUMBER) && defined(STM32_I2C4_ERROR_NUMBER)
+ nvicDisableVector(STM32_I2C4_EVENT_NUMBER);
+ nvicDisableVector(STM32_I2C4_ERROR_NUMBER);
+#else
+#error "I2C4 interrupt numbers not defined"
+#endif
+
+ rccDisableI2C4(FALSE);
+ }
+#endif
}
}
diff --git a/os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.h b/os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.h index 3a67bc121..c2fa0cbd6 100644 --- a/os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.h +++ b/os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.h @@ -78,6 +78,24 @@ #endif
/**
+ * @brief I2C3 driver enable switch.
+ * @details If set to @p TRUE the support for I2C3 is included.
+ * @note The default is @p FALSE.
+ */
+#if !defined(STM32_I2C_USE_I2C3) || defined(__DOXYGEN__)
+#define STM32_I2C_USE_I2C3 FALSE
+#endif
+
+/**
+ * @brief I2C4 driver enable switch.
+ * @details If set to @p TRUE the support for I2C4 is included.
+ * @note The default is @p FALSE.
+ */
+#if !defined(STM32_I2C_USE_I2C4) || defined(__DOXYGEN__)
+#define STM32_I2C_USE_I2C4 FALSE
+#endif
+
+/**
* @brief I2C timeout on busy condition in milliseconds.
*/
#if !defined(STM32_I2C_BUSY_TIMEOUT) || defined(__DOXYGEN__)
@@ -99,6 +117,20 @@ #endif
/**
+ * @brief I2C3 interrupt priority level setting.
+ */
+#if !defined(STM32_I2C_I2C3_IRQ_PRIORITY) || defined(__DOXYGEN__)
+#define STM32_I2C_I2C3_IRQ_PRIORITY 10
+#endif
+
+/**
+ * @brief I2C4 interrupt priority level setting.
+ */
+#if !defined(STM32_I2C_I2C4_IRQ_PRIORITY) || defined(__DOXYGEN__)
+#define STM32_I2C_I2C4_IRQ_PRIORITY 10
+#endif
+
+/**
* @brief DMA use switch.
*/
#if !defined(STM32_I2C_USE_DMA) || defined(__DOXYGEN__)
@@ -126,6 +158,26 @@ #endif
/**
+ * @brief I2C3 DMA priority (0..3|lowest..highest).
+ * @note The priority level is used for both the TX and RX DMA streams but
+ * because of the streams ordering the RX stream has always priority
+ * over the TX stream.
+ */
+#if !defined(STM32_I2C_I2C3_DMA_PRIORITY) || defined(__DOXYGEN__)
+#define STM32_I2C_I2C3_DMA_PRIORITY 1
+#endif
+
+/**
+ * @brief I2C4 DMA priority (0..3|lowest..highest).
+ * @note The priority level is used for both the TX and RX DMA streams but
+ * because of the streams ordering the RX stream has always priority
+ * over the TX stream.
+ */
+#if !defined(STM32_I2C_I2C4_DMA_PRIORITY) || defined(__DOXYGEN__)
+#define STM32_I2C_I2C4_DMA_PRIORITY 1
+#endif
+
+/**
* @brief I2C DMA error hook.
* @note The default action for DMA errors is a system halt because DMA
* error can only happen because programming errors.
@@ -148,7 +200,16 @@ #error "I2C2 not present in the selected device"
#endif
-#if !STM32_I2C_USE_I2C1 && !STM32_I2C_USE_I2C2
+#if STM32_I2C_USE_I2C3 && !STM32_HAS_I2C3
+#error "I2C3 not present in the selected device"
+#endif
+
+#if STM32_I2C_USE_I2C4 && !STM32_HAS_I2C4
+#error "I2C4 not present in the selected device"
+#endif
+
+#if !STM32_I2C_USE_I2C1 && !STM32_I2C_USE_I2C2 && !STM32_I2C_USE_I2C3 && \
+ !STM32_I2C_USE_I2C4
#error "I2C driver activated but no I2C peripheral assigned"
#endif
@@ -162,6 +223,16 @@ #error "Invalid IRQ priority assigned to I2C2"
#endif
+#if STM32_I2C_USE_I2C3 && \
+ !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2C_I2C3_IRQ_PRIORITY)
+#error "Invalid IRQ priority assigned to I2C3"
+#endif
+
+#if STM32_I2C_USE_I2C4 && \
+ !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2C_I2C4_IRQ_PRIORITY)
+#error "Invalid IRQ priority assigned to I2C4"
+#endif
+
#if STM32_I2C_USE_DMA == TRUE
#if STM32_I2C_USE_I2C1 && \
!STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C1_DMA_PRIORITY)
@@ -173,6 +244,16 @@ #error "Invalid DMA priority assigned to I2C2"
#endif
+#if STM32_I2C_USE_I2C3 && \
+ !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C3_DMA_PRIORITY)
+#error "Invalid DMA priority assigned to I2C3"
+#endif
+
+#if STM32_I2C_USE_I2C4 && \
+ !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C4_DMA_PRIORITY)
+#error "Invalid DMA priority assigned to I2C4"
+#endif
+
/* The following checks are only required when there is a DMA able to
reassign streams to different channels.*/
#if STM32_ADVANCED_DMA
@@ -187,6 +268,16 @@ #error "I2C2 DMA streams not defined"
#endif
+#if STM32_I2C_USE_I2C3 && (!defined(STM32_I2C_I2C3_RX_DMA_STREAM) || \
+ !defined(STM32_I2C_I2C3_TX_DMA_STREAM))
+#error "I2C3 DMA streams not defined"
+#endif
+
+#if STM32_I2C_USE_I2C4 && (!defined(STM32_I2C_I2C4_RX_DMA_STREAM) || \
+ !defined(STM32_I2C_I2C4_TX_DMA_STREAM))
+#error "I2C4 DMA streams not defined"
+#endif
+
/* Check on the validity of the assigned DMA channels.*/
#if STM32_I2C_USE_I2C1 && \
!STM32_DMA_IS_VALID_ID(STM32_I2C_I2C1_RX_DMA_STREAM, \
@@ -211,6 +302,30 @@ STM32_I2C2_TX_DMA_MSK)
#error "invalid DMA stream associated to I2C2 TX"
#endif
+
+#if STM32_I2C_USE_I2C3 && \
+ !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C3_RX_DMA_STREAM, \
+ STM32_I2C3_RX_DMA_MSK)
+#error "invalid DMA stream associated to I2C3 RX"
+#endif
+
+#if STM32_I2C_USE_I2C3 && \
+ !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C3_TX_DMA_STREAM, \
+ STM32_I2C3_TX_DMA_MSK)
+#error "invalid DMA stream associated to I2C3 TX"
+#endif
+
+#if STM32_I2C_USE_I2C4 && \
+ !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C4_RX_DMA_STREAM, \
+ STM32_I2C4_RX_DMA_MSK)
+#error "invalid DMA stream associated to I2C4 RX"
+#endif
+
+#if STM32_I2C_USE_I2C4 && \
+ !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C4_TX_DMA_STREAM, \
+ STM32_I2C4_TX_DMA_MSK)
+#error "invalid DMA stream associated to I2C4 TX"
+#endif
#endif /* STM32_ADVANCED_DMA */
#if !defined(STM32_DMA_REQUIRED)
@@ -353,6 +468,14 @@ extern I2CDriver I2CD1; extern I2CDriver I2CD2;
#endif
+#if STM32_I2C_USE_I2C3
+extern I2CDriver I2CD3;
+#endif
+
+#if STM32_I2C_USE_I2C4
+extern I2CDriver I2CD4;
+#endif
+
#endif /* !defined(__DOXYGEN__) */
#ifdef __cplusplus
diff --git a/os/hal/ports/STM32/STM32F7xx/stm32_rcc.h b/os/hal/ports/STM32/STM32F7xx/stm32_rcc.h index cce276e1b..49e9c63f1 100644 --- a/os/hal/ports/STM32/STM32F7xx/stm32_rcc.h +++ b/os/hal/ports/STM32/STM32F7xx/stm32_rcc.h @@ -639,6 +639,31 @@ * @api
*/
#define rccResetI2C3() rccResetAPB1(RCC_APB1RSTR_I2C3RST)
+
+/**
+ * @brief Enables the I2C4 peripheral clock.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableI2C4(lp) rccEnableAPB1(RCC_APB1ENR_I2C4EN, lp)
+
+/**
+ * @brief Disables the I2C4 peripheral clock.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccDisableI2C4(lp) rccDisableAPB1(RCC_APB1ENR_I2C4EN, lp)
+
+/**
+ * @brief Resets the I2C4 peripheral.
+ *
+ * @api
+ */
+#define rccResetI2C4() rccResetAPB1(RCC_APB1RSTR_I2C4RST)
/** @} */
/**
|