From 87e4b85755680a122f690f445b8cb320ca4f05ad Mon Sep 17 00:00:00 2001 From: gdisirio Date: Sun, 31 Aug 2014 14:24:12 +0000 Subject: Improvements to the ICU driver (not finished). git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@7210 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- os/hal/include/icu.h | 96 ++++++++++++++++++++++-------- os/hal/ports/STM32/LLD/TIMv1/icu_lld.c | 105 ++++++++++++++++++++++++++------- os/hal/ports/STM32/LLD/TIMv1/icu_lld.h | 7 ++- os/hal/src/icu.c | 77 +++++++++++++++++++++--- testhal/STM32/STM32F30x/PWM-ICU/main.c | 8 +-- 5 files changed, 233 insertions(+), 60 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(); } diff --git a/testhal/STM32/STM32F30x/PWM-ICU/main.c b/testhal/STM32/STM32F30x/PWM-ICU/main.c index f0af2504a..df25de24d 100644 --- a/testhal/STM32/STM32F30x/PWM-ICU/main.c +++ b/testhal/STM32/STM32F30x/PWM-ICU/main.c @@ -48,13 +48,13 @@ icucnt_t last_width, last_period; static void icuwidthcb(ICUDriver *icup) { palSetPad(GPIOE, GPIOE_LED9_BLUE); - last_width = icuGetWidth(icup); + last_width = icuGetWidthX(icup); } static void icuperiodcb(ICUDriver *icup) { palClearPad(GPIOE, GPIOE_LED9_BLUE); - last_period = icuGetPeriod(icup); + last_period = icuGetPeriodX(icup); } static ICUConfig icucfg = { @@ -93,7 +93,7 @@ int main(void) { palSetPadMode(GPIOD, 12, PAL_MODE_ALTERNATE(2)); icuStart(&ICUD3, &icucfg); palSetPadMode(GPIOC, 6, PAL_MODE_ALTERNATE(2)); - icuEnable(&ICUD3); + icuStartCapture(&ICUD3); chThdSleepMilliseconds(2000); /* @@ -127,7 +127,7 @@ int main(void) { */ pwmDisableChannel(&PWMD4, 0); pwmStop(&PWMD4); - icuDisable(&ICUD3); + icuStopCapture(&ICUD3); icuStop(&ICUD3); palClearPad(GPIOE, GPIOE_LED4_BLUE); palClearPad(GPIOE, GPIOE_LED9_BLUE); -- cgit v1.2.3