From 76d46cec817812902a5ac36436512c2c88f6a18e Mon Sep 17 00:00:00 2001 From: utzig Date: Mon, 8 Jul 2013 19:11:31 +0000 Subject: [PATCH] Add initial ICU driver for AVR git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@5939 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- os/hal/platforms/AVR/icu_lld.c | 337 +++++++++++++++++++++++++++++++ os/hal/platforms/AVR/icu_lld.h | 193 ++++++++++++++++++ os/hal/platforms/AVR/platform.mk | 3 +- 3 files changed, 532 insertions(+), 1 deletion(-) create mode 100644 os/hal/platforms/AVR/icu_lld.c create mode 100644 os/hal/platforms/AVR/icu_lld.h diff --git a/os/hal/platforms/AVR/icu_lld.c b/os/hal/platforms/AVR/icu_lld.c new file mode 100644 index 000000000..8a5b38edc --- /dev/null +++ b/os/hal/platforms/AVR/icu_lld.c @@ -0,0 +1,337 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file AVR/icu_lld.c + * @brief AVR ICU driver subsystem low level driver source. + * + * @addtogroup ICU + * @{ + */ + +#include "ch.h" +#include "hal.h" + +#if HAL_USE_ICU || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +typedef struct { + volatile uint8_t *tccra; + volatile uint8_t *tccrb; + volatile uint16_t *tcnt; + volatile uint8_t *timsk; +} icu_registers_t; + +static icu_registers_t regs_table[]= +{ +#if AVR_ICU_USE_ICU1 || defined(__DOXYGEN__) + {&TCCR1A, &TCCR1B, &TCNT1, &TIMSK1}, +#endif +#if AVR_ICU_USE_ICU3 || defined(__DOXYGEN__) + {&TCCR3A, &TCCR3B, &TCNT3, &TIMSK3}, +#endif +#if AVR_ICU_USE_ICU4 || defined(__DOXYGEN__) + {&TCCR4A, &TCCR4B, &TCNT4, &TIMSK4}, +#endif +#if AVR_ICU_USE_ICU5 || defined(__DOXYGEN__) + {&TCCR5A, &TCCR5B, &TCNT5, &TIMSK5}, +#endif +}; + + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** + * @brief ICU1 driver identifier. + */ +#if AVR_ICU_USE_ICU1 || defined(__DOXYGEN__) +ICUDriver ICUD1; +#endif +/** + * @brief ICU3 driver identifier. + */ +#if AVR_ICU_USE_ICU3 || defined(__DOXYGEN__) +ICUDriver ICUD3; +#endif +/** + * @brief ICU4 driver identifier. + */ +#if AVR_ICU_USE_ICU4 || defined(__DOXYGEN__) +ICUDriver ICUD4; +#endif +/** + * @brief ICU5 driver identifier. + */ +#if AVR_ICU_USE_ICU5 || defined(__DOXYGEN__) +ICUDriver ICUD5; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +static inline void handle_capture_isr(ICUDriver *icup, + volatile uint16_t *icr, + volatile uint8_t *tccrb, + volatile uint16_t *tcnt) +{ + uint16_t value = *icr; + uint8_t rising = (*tccrb & (1 << ICES1)) ? 1 : 0; + *tccrb ^= (1 << ICES1); + if ((icup->config->mode == ICU_INPUT_ACTIVE_HIGH && rising) || + (icup->config->mode == ICU_INPUT_ACTIVE_LOW && !rising)) { + icup->width = value; + if (icup->config->width_cb != NULL) + icup->config->width_cb(icup); + } else { + icup->period = value; + if (icup->config->period_cb != NULL) + icup->config->period_cb(icup); + /* Reset counter at the end of every cycle */ + *tcnt = 0; + } +} + +static uint8_t index(ICUDriver *icup) +{ + uint8_t index = 0; +#if AVR_ICU_USE_ICU1 || defined(__DOXYGEN__) + if (icup == &ICUD1) return index; + else index++; +#endif +#if AVR_ICU_USE_ICU3 || defined(__DOXYGEN__) + if (icup == &ICUD3) return index; + else index++; +#endif +#if AVR_ICU_USE_ICU4 || defined(__DOXYGEN__) + if (icup == &ICUD4) return index; + else index++; +#endif +#if AVR_ICU_USE_ICU5 || defined(__DOXYGEN__) + if (icup == &ICUD5) return index; + else index++; +#endif +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if AVR_ICU_USE_ICU1 || defined(__DOXYGEN__) +CH_IRQ_HANDLER(TIMER1_CAPT_vect) +{ + CH_IRQ_PROLOGUE(); + handle_capture_isr(&ICUD1, &ICR1, &TCCR1B, &TCNT1); + CH_IRQ_EPILOGUE(); +} + +CH_IRQ_HANDLER(TIMER1_OVF_vect) +{ + CH_IRQ_PROLOGUE(); + ICUD1.config->overflow_cb(&ICUD1); + CH_IRQ_EPILOGUE(); +} +#endif + +#if AVR_ICU_USE_ICU3 || defined(__DOXYGEN__) +CH_IRQ_HANDLER(TIMER3_CAPT_vect) +{ + CH_IRQ_PROLOGUE(); + handle_capture_isr(&ICUD3, &ICR3, &TCCR3B, &TCNT3); + CH_IRQ_EPILOGUE(); +} + +CH_IRQ_HANDLER(TIMER3_OVF_vect) +{ + CH_IRQ_PROLOGUE(); + ICUD3.config->overflow_cb(&ICUD3); + CH_IRQ_EPILOGUE(); +} +#endif + +#if AVR_ICU_USE_ICU4 || defined(__DOXYGEN__) +CH_IRQ_HANDLER(TIMER4_CAPT_vect) +{ + CH_IRQ_PROLOGUE(); + handle_capture_isr(&ICUD4, &ICR4, &TCCR4B, &TCNT4); + CH_IRQ_EPILOGUE(); +} + +CH_IRQ_HANDLER(TIMER4_OVF_vect) +{ + CH_IRQ_PROLOGUE(); + ICUD4.config->overflow_cb(&ICUD4); + CH_IRQ_EPILOGUE(); +} +#endif + +#if AVR_ICU_USE_ICU5 || defined(__DOXYGEN__) +CH_IRQ_HANDLER(TIMER5_CAPT_vect) +{ + CH_IRQ_PROLOGUE(); + handle_capture_isr(&ICUD5, &ICR5, &TCCR5B, &TCNT5); + CH_IRQ_EPILOGUE(); +} + +CH_IRQ_HANDLER(TIMER5_OVF_vect) +{ + CH_IRQ_PROLOGUE(); + ICUD5.config->overflow_cb(&ICUD5); + CH_IRQ_EPILOGUE(); +} +#endif + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level ICU driver initialization. + * + * @notapi + */ +void icu_lld_init(void) { + +#if AVR_ICU_USE_ICU1 + icuObjectInit(&ICUD1); +#endif +#if AVR_ICU_USE_ICU3 + icuObjectInit(&ICUD3); +#endif +#if AVR_ICU_USE_ICU4 + icuObjectInit(&ICUD4); +#endif +#if AVR_ICU_USE_ICU5 + icuObjectInit(&ICUD5); +#endif +} + +/** + * @brief Configures and activates the ICU peripheral. + * + * @param[in] icup pointer to the @p ICUDriver object + * + * @notapi + */ +void icu_lld_start(ICUDriver *icup) { + + if (icup->state == ICU_STOP) { + uint8_t i = index(icup); + /* Normal waveform generation (counts from 0 to 0xFFFF) */ + *regs_table[i].tccra &= ~((1 << WGM11) | (1 << WGM10)); + *regs_table[i].tccrb &= ~((1 << WGM13) | (1 << WGM12)); + /* Enable noise canceler, set prescale to CLK/1024 */ + *regs_table[i].tccrb |= (1 << ICNC1) | (1 << CS12) | (1 << CS10); + if (icup->config->mode == ICU_INPUT_ACTIVE_HIGH) + *regs_table[i].tccrb |= (1 << ICES1); + else + *regs_table[i].tccrb &= ~(1 << ICES1); + } +} + +/** + * @brief Deactivates the ICU peripheral. + * + * @param[in] icup pointer to the @p ICUDriver object + * + * @notapi + */ +void icu_lld_stop(ICUDriver *icup) { + + if (icup->state == ICU_READY) { + /* Resets the peripheral.*/ + + /* Disables the peripheral.*/ +#if AVR_ICU_USE_ICU1 + if (&ICUD1 == icup) { + + } +#endif /* AVR_ICU_USE_ICU1 */ + } +} + +/** + * @brief Enables the input capture. + * + * @param[in] icup pointer to the @p ICUDriver object + * + * @notapi + */ +void icu_lld_enable(ICUDriver *icup) { + + uint8_t i = index(icup); + icup->width = icup->period = 0; + *regs_table[i].tcnt = 0; + *regs_table[i].timsk |= (1 << ICIE1); + if (icup->config->overflow_cb != NULL) + *regs_table[i].timsk |= (1 << TOIE1); +} + +/** + * @brief Disables the input capture. + * + * @param[in] icup pointer to the @p ICUDriver object + * + * @notapi + */ +void icu_lld_disable(ICUDriver *icup) { + + uint8_t i = index(icup); + *regs_table[i].timsk &= ~((1 << ICIE1) | (1 << TOIE1)); +} + +/** + * @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. + * + * @param[in] icup pointer to the @p ICUDriver object + * @return The number of ticks. + * + * @notapi + */ +icucnt_t icu_lld_get_width(ICUDriver *icup) { + + return icup->width; +} + +/** + * @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. + * + * @param[in] icup pointer to the @p ICUDriver object + * @return The number of ticks. + * + * @notapi + */ +icucnt_t icu_lld_get_period(ICUDriver *icup) { + + return icup->period; +} + +#endif /* HAL_USE_ICU */ + +/** @} */ diff --git a/os/hal/platforms/AVR/icu_lld.h b/os/hal/platforms/AVR/icu_lld.h new file mode 100644 index 000000000..c63967513 --- /dev/null +++ b/os/hal/platforms/AVR/icu_lld.h @@ -0,0 +1,193 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file AVR/icu_lld.h + * @brief AVR ICU driver subsystem low level driver header. + * + * @addtogroup ICU + * @{ + */ + +#ifndef _ICU_LLD_H_ +#define _ICU_LLD_H_ + +#if HAL_USE_ICU || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief ICU driver enable switch. + * @details If set to @p TRUE the support for ICU1 is included. + */ +#if !defined(AVR_ICU_USE_ICU1) || defined(__DOXYGEN__) +#define AVR_ICU_USE_ICU1 FALSE +#endif +/** + * @brief ICU driver enable switch. + * @details If set to @p TRUE the support for ICU3 is included. + */ +#if !defined(AVR_ICU_USE_ICU3) || defined(__DOXYGEN__) +#define AVR_ICU_USE_ICU3 FALSE +#endif +/** + * @brief ICU driver enable switch. + * @details If set to @p TRUE the support for ICU4 is included. + */ +#if !defined(AVR_ICU_USE_ICU4) || defined(__DOXYGEN__) +#define AVR_ICU_USE_ICU4 FALSE +#endif +/** + * @brief ICU driver enable switch. + * @details If set to @p TRUE the support for ICU5 is included. + */ +#if !defined(AVR_ICU_USE_ICU5) || defined(__DOXYGEN__) +#define AVR_ICU_USE_ICU5 FALSE +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief ICU driver mode. + */ +typedef enum { + ICU_INPUT_ACTIVE_HIGH = 0, /**< Trigger on rising edge. */ + ICU_INPUT_ACTIVE_LOW = 1, /**< Trigger on falling edge. */ +} icumode_t; + +/** + * @brief ICU frequency type. + */ +typedef uint16_t icufreq_t; + +/** + * @brief ICU counter type. + */ +typedef uint16_t icucnt_t; + +/** + * @brief Driver configuration structure. + * @note It could be empty on some architectures. + */ +typedef struct { + /** + * @brief Driver mode. + */ + icumode_t mode; + /** + * @brief Timer clock in Hz. + * @note The low level can use assertions in order to catch invalid + * frequency specifications. + */ + icufreq_t frequency; + /** + * @brief Callback for pulse width measurement. + */ + icucallback_t width_cb; + /** + * @brief Callback for cycle period measurement. + */ + icucallback_t period_cb; + /** + * @brief Callback for timer overflow. + */ + icucallback_t overflow_cb; + /* End of the mandatory fields.*/ +} ICUConfig; + +/** + * @brief Structure representing an ICU driver. + */ +struct ICUDriver { + /** + * @brief Driver state. + */ + icustate_t state; + /** + * @brief Current configuration data. + */ + const ICUConfig *config; +#if defined(ICU_DRIVER_EXT_FIELDS) + ICU_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief Width value read by ISR. + */ + icucnt_t width; + /** + * @brief Period value read by ISR. + */ + icucnt_t period; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if AVR_ICU_USE_ICU1 && !defined(__DOXYGEN__) +extern ICUDriver ICUD1; +#endif +#if AVR_ICU_USE_ICU3 && !defined(__DOXYGEN__) +extern ICUDriver ICUD3; +#endif +#if AVR_ICU_USE_ICU4 && !defined(__DOXYGEN__) +extern ICUDriver ICUD4; +#endif +#if AVR_ICU_USE_ICU5 && !defined(__DOXYGEN__) +extern ICUDriver ICUD5; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + 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); + icucnt_t icu_lld_get_width(ICUDriver *icup); + icucnt_t icu_lld_get_period(ICUDriver *icup); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_ICU */ + +#endif /* _ICU_LLD_H_ */ + +/** @} */ diff --git a/os/hal/platforms/AVR/platform.mk b/os/hal/platforms/AVR/platform.mk index 05d90d1c1..e2e8c1101 100644 --- a/os/hal/platforms/AVR/platform.mk +++ b/os/hal/platforms/AVR/platform.mk @@ -6,7 +6,8 @@ PLATFORMSRC = ${CHIBIOS}/os/hal/platforms/AVR/hal_lld.c \ ${CHIBIOS}/os/hal/platforms/AVR/i2c_lld.c \ ${CHIBIOS}/os/hal/platforms/AVR/spi_lld.c \ ${CHIBIOS}/os/hal/platforms/AVR/gpt_lld.c \ - ${CHIBIOS}/os/hal/platforms/AVR/pwm_lld.c + ${CHIBIOS}/os/hal/platforms/AVR/pwm_lld.c \ + ${CHIBIOS}/os/hal/platforms/AVR/icu_lld.c # Required include directories PLATFORMINC = ${CHIBIOS}/os/hal/platforms/AVR