diff options
Diffstat (limited to 'os/hal')
-rw-r--r-- | os/hal/include/icu.h | 96 | ||||
-rw-r--r-- | os/hal/ports/STM32/LLD/TIMv1/icu_lld.c | 105 | ||||
-rw-r--r-- | os/hal/ports/STM32/LLD/TIMv1/icu_lld.h | 7 | ||||
-rw-r--r-- | os/hal/src/icu.c | 77 |
4 files changed, 229 insertions, 56 deletions
diff --git a/os/hal/include/icu.h b/os/hal/include/icu.h index 30ae9c45b..1839c214f 100644 --- a/os/hal/include/icu.h +++ b/os/hal/include/icu.h @@ -54,9 +54,8 @@ typedef enum { ICU_UNINIT = 0, /**< Not initialized. */
ICU_STOP = 1, /**< Stopped. */
ICU_READY = 2, /**< Ready. */
- ICU_WAITING = 3, /**< Waiting first edge. */
- ICU_ACTIVE = 4, /**< Active cycle phase. */
- ICU_IDLE = 5, /**< Idle cycle phase. */
+ ICU_WAITING = 3, /**< Waiting for first front. */
+ ICU_ACTIVE = 4, /**< First front detected. */
} icustate_t;
/**
@@ -82,50 +81,94 @@ typedef void (*icucallback_t)(ICUDriver *icup); * @{
*/
/**
- * @brief Enables the input capture.
+ * @brief Starts the input capture.
*
* @param[in] icup pointer to the @p ICUDriver object
*
* @iclass
*/
-#define icuEnableI(icup) icu_lld_enable(icup)
+#define icuStartCaptureI(icup) do { \
+ icu_lld_start_capture(icup); \
+ icup->state = ICU_WAITING; \
+} while (0)
/**
- * @brief Disables the input capture.
+ * @brief Waits for the first cycle activation edge.
+ * @details The function waits for the next PWM input activation front then
+ * brings the driver in the @p ICU_ACTIVE state.
+ * @note If notifications are enabled then the transition to the
+ * @p ICU_ACTIVE state is done automatically on the first edge.
*
* @param[in] icup pointer to the @p ICUDriver object
*
* @iclass
*/
-#define icuDisableI(icup) icu_lld_disable(icup)
+#define icuWaitCaptureI(icup) do { \
+ icu_lld_wait_capture(icup); \
+ icup->state = ICU_ACTIVE; \
+} while (0)
+
+/**
+ * @brief Stops the input capture.
+ *
+ * @param[in] icup pointer to the @p ICUDriver object
+ *
+ * @iclass
+ */
+#define icuStopCaptureI(icup) do { \
+ icu_lld_stop_capture(icup); \
+ icup->state = ICU_READY; \
+} while (0)
+
+/**
+ * @brief Enables notifications.
+ * @pre The ICU unit must have been activated using @p icuStart().
+ * @note If the notification is already enabled then the call has no effect.
+ *
+ * @param[in] icup pointer to the @p ICUDriver object
+ *
+ * @iclass
+ */
+#define icuEnableNotificationsI(icup) icu_enable_notifications(icup)
+
+/**
+ * @brief Disables notifications.
+ * @pre The ICU unit must have been activated using @p icuStart().
+ * @note If the notification is already disabled then the call has no effect.
+ *
+ * @param[in] icup pointer to the @p ICUDriver object
+ *
+ * @iclass
+ */
+#define icuDisableNotificationsI(icup) icu_disable_notifications(icup)
/**
* @brief Returns the width of the latest pulse.
* @details The pulse width is defined as number of ticks between the start
* edge and the stop edge.
* @note This function is meant to be invoked from the width capture
- * callback only.
+ * callback.
*
* @param[in] icup pointer to the @p ICUDriver object
* @return The number of ticks.
*
- * @special
+ * @xclass
*/
-#define icuGetWidth(icup) icu_lld_get_width(icup)
+#define icuGetWidthX(icup) icu_lld_get_width(icup)
/**
* @brief Returns the width of the latest cycle.
* @details The cycle width is defined as number of ticks between a start
* edge and the next start edge.
* @note This function is meant to be invoked from the width capture
- * callback only.
+ * callback.
*
* @param[in] icup pointer to the @p ICUDriver object
* @return The number of ticks.
*
- * @special
+ * @xclass
*/
-#define icuGetPeriod(icup) icu_lld_get_period(icup)
+#define icuGetPeriodX(icup) icu_lld_get_period(icup)
/** @} */
/**
@@ -139,12 +182,11 @@ typedef void (*icucallback_t)(ICUDriver *icup); *
* @notapi
*/
-#define _icu_isr_invoke_width_cb(icup) { \
- if ((icup)->state != ICU_WAITING) { \
- (icup)->state = ICU_IDLE; \
+#define _icu_isr_invoke_width_cb(icup) do { \
+ if (((icup)->state != ICU_WAITING) && \
+ ((icup)->config->period_cb != NULL)) \
(icup)->config->width_cb(icup); \
- } \
-}
+} while (0)
/**
* @brief Common ISR code, ICU period event.
@@ -153,12 +195,13 @@ typedef void (*icucallback_t)(ICUDriver *icup); *
* @notapi
*/
-#define _icu_isr_invoke_period_cb(icup) { \
+#define _icu_isr_invoke_period_cb(icup) do { \
icustate_t previous_state = (icup)->state; \
(icup)->state = ICU_ACTIVE; \
- if (previous_state != ICU_WAITING) \
+ if ((previous_state != ICU_WAITING) && \
+ ((icup)->config->period_cb != NULL)) \
(icup)->config->period_cb(icup); \
-}
+} while (0)
/**
* @brief Common ISR code, ICU timer overflow event.
@@ -167,9 +210,9 @@ typedef void (*icucallback_t)(ICUDriver *icup); *
* @notapi
*/
-#define _icu_isr_invoke_overflow_cb(icup) { \
+#define _icu_isr_invoke_overflow_cb(icup) do { \
(icup)->config->overflow_cb(icup); \
-}
+} while (0)
/** @} */
/*===========================================================================*/
@@ -183,8 +226,11 @@ extern "C" { void icuObjectInit(ICUDriver *icup);
void icuStart(ICUDriver *icup, const ICUConfig *config);
void icuStop(ICUDriver *icup);
- void icuEnable(ICUDriver *icup);
- void icuDisable(ICUDriver *icup);
+ void icuStartCapture(ICUDriver *icup);
+ void icuWaitCapture(ICUDriver *icup);
+ void icuStopCapture(ICUDriver *icup);
+ void icuEnableNotifications(ICUDriver *icup);
+ void icuDisableNotifications(ICUDriver *icup);
#ifdef __cplusplus
}
#endif
diff --git a/os/hal/ports/STM32/LLD/TIMv1/icu_lld.c b/os/hal/ports/STM32/LLD/TIMv1/icu_lld.c index 1e935fd54..1602ac1d1 100644 --- a/os/hal/ports/STM32/LLD/TIMv1/icu_lld.c +++ b/os/hal/ports/STM32/LLD/TIMv1/icu_lld.c @@ -596,43 +596,108 @@ void icu_lld_stop(ICUDriver *icup) { }
/**
- * @brief Enables the input capture.
+ * @brief Starts the input capture.
*
* @param[in] icup pointer to the @p ICUDriver object
*
* @notapi
*/
-void icu_lld_enable(ICUDriver *icup) {
+void icu_lld_start_capture(ICUDriver *icup) {
+ /* Triggering an UG and clearing the IRQ status.*/
icup->tim->EGR |= STM32_TIM_EGR_UG;
- icup->tim->SR = 0; /* Clear pending IRQs (if any). */
- if (icup->config->channel == ICU_CHANNEL_1) {
- if (icup->config->period_cb != NULL)
- icup->tim->DIER |= STM32_TIM_DIER_CC1IE;
- if (icup->config->width_cb != NULL)
- icup->tim->DIER |= STM32_TIM_DIER_CC2IE;
- } else {
- if (icup->config->width_cb != NULL)
- icup->tim->DIER |= STM32_TIM_DIER_CC1IE;
- if (icup->config->period_cb != NULL)
- icup->tim->DIER |= STM32_TIM_DIER_CC2IE;
- }
- if (icup->config->overflow_cb != NULL)
- icup->tim->DIER |= STM32_TIM_DIER_UIE;
+ icup->tim->SR = 0;
+
+ /* Timer is started.*/
icup->tim->CR1 = STM32_TIM_CR1_URS | STM32_TIM_CR1_CEN;
}
/**
- * @brief Disables the input capture.
+ * @brief Waits for the first cycle activation edge.
+ * @details The function waits for the next PWM input activation front then
+ * brings the driver in the @p ICU_ACTIVE state.
+ * @note If notifications are enabled then the transition to the
+ * @p ICU_ACTIVE state is done automatically on the first edge.
*
* @param[in] icup pointer to the @p ICUDriver object
*
* @notapi
*/
-void icu_lld_disable(ICUDriver *icup) {
+void icu_lld_wait_capture(ICUDriver *icup) {
+
+}
+
+/**
+ * @brief Stops the input capture.
+ *
+ * @param[in] icup pointer to the @p ICUDriver object
+ *
+ * @notapi
+ */
+void icu_lld_stop_capture(ICUDriver *icup) {
+
+ /* Timer stopped.*/
+ icup->tim->CR1 = 0;
+
+ /* All interrupts disabled.*/
+ icup->tim->DIER &= ~STM32_TIM_DIER_IRQ_MASK;
+}
+
+/**
+ * @brief Enables notifications.
+ * @pre The ICU unit must have been activated using @p icuStart().
+ * @note If the notification is already enabled then the call has no effect.
+ *
+ * @param[in] icup pointer to the @p ICUDriver object
+ *
+ * @api
+ */
+void icu_enable_notifications(ICUDriver *icup) {
+ uint32_t dier = icup->tim->DIER;
+
+ /* If interrupts were already enabled then the operation is skipped.
+ This is done in order to avoid clearing the SR and risk losing
+ pending interrupts.*/
+ if ((dier & STM32_TIM_DIER_IRQ_MASK) != 0) {
+ /* Previously triggered IRQs are ignored, status cleared.*/
+ icup->tim->SR = 0;
+
+ if (icup->config->channel == ICU_CHANNEL_1) {
+ /* Enabling periodic callback on CC1.*/
+ dier |= STM32_TIM_DIER_CC1IE;
+
+ /* Optionally enabling width callback on CC2.*/
+ if (icup->config->width_cb != NULL)
+ dier |= STM32_TIM_DIER_CC2IE;
+ } else {
+ /* Enabling periodic callback on CC2.*/
+ dier |= STM32_TIM_DIER_CC2IE;
+
+ /* Optionally enabling width callback on CC1.*/
+ if (icup->config->width_cb != NULL)
+ dier |= STM32_TIM_DIER_CC1IE;
+ }
+
+ /* If an overflow callback is defined then also the overflow callback
+ is enabled.*/
+ if (icup->config->overflow_cb != NULL)
+ dier |= STM32_TIM_DIER_UIE;
- icup->tim->CR1 = 0; /* Initially stopped. */
- icup->tim->SR = 0; /* Clear pending IRQs (if any). */
+ /* One single atomic write.*/
+ icup->tim->DIER = dier;
+ }
+}
+
+/**
+ * @brief Disables notifications.
+ * @pre The ICU unit must have been activated using @p icuStart().
+ * @note If the notification is already disabled then the call has no effect.
+ *
+ * @param[in] icup pointer to the @p ICUDriver object
+ *
+ * @api
+ */
+void icu_disable_notifications(ICUDriver *icup) {
/* All interrupts disabled.*/
icup->tim->DIER &= ~STM32_TIM_DIER_IRQ_MASK;
diff --git a/os/hal/ports/STM32/LLD/TIMv1/icu_lld.h b/os/hal/ports/STM32/LLD/TIMv1/icu_lld.h index 87b5dfd4d..2559e9643 100644 --- a/os/hal/ports/STM32/LLD/TIMv1/icu_lld.h +++ b/os/hal/ports/STM32/LLD/TIMv1/icu_lld.h @@ -399,8 +399,11 @@ extern "C" { void icu_lld_init(void);
void icu_lld_start(ICUDriver *icup);
void icu_lld_stop(ICUDriver *icup);
- void icu_lld_enable(ICUDriver *icup);
- void icu_lld_disable(ICUDriver *icup);
+ void icu_lld_start_capture(ICUDriver *icup);
+ void icu_lld_wait_capture(ICUDriver *icup);
+ void icu_lld_stop_capture(ICUDriver *icup);
+ void icu_enable_notifications(ICUDriver *icup);
+ void icu_disable_notifications(ICUDriver *icup);
#ifdef __cplusplus
}
#endif
diff --git a/os/hal/src/icu.c b/os/hal/src/icu.c index 0ef635edd..fcccab727 100644 --- a/os/hal/src/icu.c +++ b/os/hal/src/icu.c @@ -116,40 +116,99 @@ void icuStop(ICUDriver *icup) { }
/**
- * @brief Enables the input capture.
+ * @brief Starts the input capture.
*
* @param[in] icup pointer to the @p ICUDriver object
*
* @api
*/
-void icuEnable(ICUDriver *icup) {
+void icuStartCapture(ICUDriver *icup) {
osalDbgCheck(icup != NULL);
osalSysLock();
osalDbgAssert(icup->state == ICU_READY, "invalid state");
- icu_lld_enable(icup);
- icup->state = ICU_WAITING;
+ icuStartCaptureI(icup);
osalSysUnlock();
}
/**
- * @brief Disables the input capture.
+ * @brief Waits for the first cycle activation edge.
+ * @details The function waits for the next PWM input activation front then
+ * brings the driver in the @p ICU_ACTIVE state.
+ * @note If notifications are enabled then the transition to the
+ * @p ICU_ACTIVE state is done automatically on the first edge.
*
* @param[in] icup pointer to the @p ICUDriver object
*
* @api
*/
-void icuDisable(ICUDriver *icup) {
+void icuWaitCapture(ICUDriver *icup) {
+
+ osalDbgCheck(icup != NULL);
+
+ osalSysLock();
+ osalDbgAssert(icup->state == ICU_WAITING, "invalid state");
+ icuWaitCaptureI(icup);
+ osalSysUnlock();
+}
+
+/**
+ * @brief Stops the input capture.
+ *
+ * @param[in] icup pointer to the @p ICUDriver object
+ *
+ * @api
+ */
+void icuStopCapture(ICUDriver *icup) {
osalDbgCheck(icup != NULL);
osalSysLock();
osalDbgAssert((icup->state == ICU_READY) || (icup->state == ICU_WAITING) ||
- (icup->state == ICU_ACTIVE) || (icup->state == ICU_IDLE),
+ (icup->state == ICU_ACTIVE),
"invalid state");
- icu_lld_disable(icup);
- icup->state = ICU_READY;
+ icuStopCaptureI(icup);
+ osalSysUnlock();
+}
+
+/**
+ * @brief Enables notifications.
+ * @pre The ICU unit must have been activated using @p icuStart().
+ * @note If the notification is already enabled then the call has no effect.
+ *
+ * @param[in] icup pointer to the @p ICUDriver object
+ *
+ * @api
+ */
+void icuEnableNotifications(ICUDriver *icup) {
+
+ osalDbgCheck(icup != NULL);
+
+ osalSysLock();
+ osalDbgAssert((icup->state == ICU_WAITING) || (icup->state == ICU_ACTIVE),
+ "invalid state");
+ icuEnableNotificationsI(icup);
+ osalSysUnlock();
+}
+
+/**
+ * @brief Disables notifications.
+ * @pre The ICU unit must have been activated using @p icuStart().
+ * @note If the notification is already disabled then the call has no effect.
+ *
+ * @param[in] icup pointer to the @p ICUDriver object
+ *
+ * @api
+ */
+void icuDisableNotifications(ICUDriver *icup) {
+
+ osalDbgCheck(icup != NULL);
+
+ osalSysLock();
+ osalDbgAssert((icup->state == ICU_WAITING) || (icup->state == ICU_ACTIVE),
+ "invalid state");
+ icuDisableNotificationsI(icup);
osalSysUnlock();
}
|