aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--os/hal/platforms/AVR/icu_lld.c337
-rw-r--r--os/hal/platforms/AVR/icu_lld.h193
-rw-r--r--os/hal/platforms/AVR/platform.mk3
3 files changed, 532 insertions, 1 deletions
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