From 72da6659edb9bb60b79c76a2db3387c60104e2de Mon Sep 17 00:00:00 2001 From: Ralf Schlatterbeck Date: Thu, 26 Jun 2014 20:37:34 +0200 Subject: [PATCH] Inline timer init functions, no static storage Hardware init function profit a great deal from being inlined if the given parameters are constant -- which is the common use-case, we could probably call this for all timers and still have less overhead. The hwtimer_pwm_ini (which calls hwtimer_ini) gets completely computed at compile-time resulting only in the register settings of hwtimer_ini. This is now possible because we get rid of static storage for the max_ticks and instead compute this in hwtimer_pwm_max_ticks from the timer register settings. --- cpu/avr/Makefile.avr | 3 +- cpu/avr/hw_pwm_timer.c | 142 ----------------------------------- cpu/avr/hw_timer.c | 103 -------------------------- cpu/avr/hw_timer.h | 163 +++++++++++++++++++++++++++++++++++++++-- 4 files changed, 157 insertions(+), 254 deletions(-) delete mode 100644 cpu/avr/hw_pwm_timer.c delete mode 100644 cpu/avr/hw_timer.c diff --git a/cpu/avr/Makefile.avr b/cpu/avr/Makefile.avr index ee88407d6..d3ef8235f 100644 --- a/cpu/avr/Makefile.avr +++ b/cpu/avr/Makefile.avr @@ -14,8 +14,7 @@ CONTIKI_CPU=$(CONTIKI)/cpu/avr ### TARGETLIBS are platform-specific routines in the contiki library path CONTIKI_CPU_DIRS = . dev AVR = clock.c mtarch.c eeprom.c flash.c rs232.c leds-arch.c \ - watchdog.c rtimer-arch.c bootloader.c hw_timer.c \ - hw_pwm_timer.c + watchdog.c rtimer-arch.c bootloader.c ELFLOADER = elfloader.c elfloader-avr.c symtab-avr.c TARGETLIBS = random.c leds.c diff --git a/cpu/avr/hw_pwm_timer.c b/cpu/avr/hw_pwm_timer.c deleted file mode 100644 index 2c45ce729..000000000 --- a/cpu/avr/hw_pwm_timer.c +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (c) 2014, Ralf Schlatterbeck Open Source Consulting - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the Contiki operating system. - */ - -/** - * \addgroup hardware timer - * - * @{ - */ - -/** - * \file - * Alternative initialisation with period in microseconds - * \author - * Ralf Schlatterbeck - */ - -#include -#include "contiki.h" -#include "rtimer-arch.h" -#include "hw_timer.h" - -/* one for each possible timer */ -uint16_t hwt_max_ticks [6]; - -#define PERIOD_MAX (0xFFFFFFFF / (F_CPU / 1000000)) -/* for 16-bit timer: */ -#define TICKS_MAX 0xFFFF -#define TICKS_MIN 0xFF - -int8_t -hwtimer_pwm_ini (uint8_t timer, uint32_t period_us, uint8_t pwm_type, uint8_t ocra) -{ - uint32_t ticks = 0; - uint8_t clock = HWT_CLOCK_PRESCALER_1024; - uint8_t wgm = HWT_WGM_NORMAL; - HWT_CHECK_TIMER (timer); - if (period_us > PERIOD_MAX) { - period_us = PERIOD_MAX; - } - ticks = (F_CPU / 1000000) * period_us; - /* Non-fast PWM modes have half the frequency */ - if (pwm_type != HWT_PWM_FAST) { - ticks >>= 1; - } - - /* - * Divisors are 1, 8, 64, 256, 1024, shifts between these are - * 3, 3, 2, 2, respectively. We modify `ticks` in place, the AVR can - * shift only one bit in one instruction, so shifting isn't cheap. - * We try to get the *maximum* prescaler that still permits a tick - * resolution of at least 8 bit. - */ - if (ticks <= (TICKS_MIN << 3)) { - clock = HWT_CLOCK_PRESCALER_1; - } - else if ((ticks >>= 3) <= (TICKS_MIN << 3)) { - clock = HWT_CLOCK_PRESCALER_8; - } - else if ((ticks >>= 3) <= (TICKS_MIN << 2)) { - clock = HWT_CLOCK_PRESCALER_64; - } - else if ((ticks >>= 2) <= (TICKS_MIN << 2)) { - clock = HWT_CLOCK_PRESCALER_256; - } - else if ((ticks >>= 2) > TICKS_MAX) { - ticks = TICKS_MAX; - } - hwt_max_ticks [timer] = ticks; - switch (pwm_type) { - case HWT_PWM_FAST: - wgm = ocra ? HWT_WGM_PWM_FAST_OCRA : HWT_WGM_PWM_FAST_ICR; - break; - case HWT_PWM_PHASE_CORRECT: - wgm = ocra ? HWT_WGM_PWM_PHASE_OCRA : HWT_WGM_PWM_PHASE_ICR; - break; - case HWT_PWM_PHASE_FRQ_CORRECT: - default: - wgm = ocra ? HWT_WGM_PWM_PHASE_FRQ_OCRA : HWT_WGM_PWM_PHASE_FRQ_ICR; - break; - } - /* Special 8- 9- 10-bit modes */ - if (pwm_type == HWT_PWM_FAST || pwm_type == HWT_PWM_PHASE_CORRECT) { - if (ticks == 0xFF) { - wgm = (pwm_type == HWT_PWM_FAST) - ? HWT_WGM_PWM_FAST_8_BIT - : HWT_WGM_PWM_PHASE_8_BIT; - } - else if (ticks == 0x1FF) { - wgm = (pwm_type == HWT_PWM_FAST) - ? HWT_WGM_PWM_FAST_9_BIT - : HWT_WGM_PWM_PHASE_9_BIT; - } - else if (ticks == 0x3FF) { - wgm = (pwm_type == HWT_PWM_FAST) - ? HWT_WGM_PWM_FAST_10_BIT - : HWT_WGM_PWM_PHASE_10_BIT; - } - } - return hwtimer_ini (timer, wgm, clock, ticks); -} - -uint32_t hwtimer_pwm_max_ticks (uint8_t timer) -{ - if (timer > 5) { - return 0; - } - return hwt_max_ticks [timer]; -} - -/* - * ex:ts=8:et:sw=2 - */ - -/** @} */ diff --git a/cpu/avr/hw_timer.c b/cpu/avr/hw_timer.c deleted file mode 100644 index f3ec11930..000000000 --- a/cpu/avr/hw_timer.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2014, Ralf Schlatterbeck Open Source Consulting - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the Contiki operating system. - */ - -/** - * \addgroup hardware timer - * - * @{ - */ - -/** - * \file - * Header file for hardware timer of AVR microcontrollers - * \author - * Ralf Schlatterbeck - */ - -#include -#include "contiki.h" -#include "rtimer-arch.h" -#include "hw_timer.h" - -#ifndef PLAT_TIMER -#define PLAT_TIMER 0xFF /* invalid timer for comparison */ -#endif - -int8_t hwtimer_ini (uint8_t timer, uint8_t wgm, uint8_t clock, uint16_t maxt) -{ - int8_t i; - HWT_CHECK_TIMER (timer); - if (wgm > HWT_WGM_MASK || wgm == HWT_WGM_RESERVED) { - return HWT_ERR_INVALID_WGM; - } - if (clock > HWT_CLOCK_MASK) { - return HWT_ERR_INVALID_CLOCK; - } - /* Turn off clock, no need to disable interrupt */ - *HWT_TCCRB (timer) &= ~HWT_CLOCK_MASK; - - *HWT_TCCRA (timer) &= ~(HWT_WGM_MASK_LOW << HWT_WGM_SHIFT_LOW); - *HWT_TCCRA (timer) |= ((wgm & HWT_WGM_MASK_LOW) << HWT_WGM_SHIFT_LOW); - *HWT_TCCRB (timer) &= ~(HWT_WGM_MASK_HIGH << HWT_WGM_SHIFT_HIGH); - *HWT_TCCRB (timer) |= ((wgm & HWT_WGM_MASK_HIGH) << HWT_WGM_SHIFT_HIGH); - - for (i=0; i<3; i++) { - HWT_SET_COM (timer, i, HWT_COM_NORMAL); - } - - if ( wgm == HWT_WGM_PWM_PHASE_FRQ_ICR - || wgm == HWT_WGM_PWM_PHASE_ICR - || wgm == HWT_WGM_CTC_ICR - || wgm == HWT_WGM_PWM_FAST_ICR - ) - { - *HWT_ICR (timer) = maxt; - } - - if ( wgm == HWT_WGM_CTC_OCRA - || wgm == HWT_WGM_PWM_PHASE_FRQ_OCRA - || wgm == HWT_WGM_PWM_PHASE_OCRA - || wgm == HWT_WGM_PWM_FAST_OCRA - ) - { - *HWT_OCRA (timer) = maxt; - } - - /* Set clock, finally */ - *HWT_TCCRB (timer) |= clock; - return 0; -} - -/* - * ex:ts=8:et:sw=2 - */ - -/** @} */ diff --git a/cpu/avr/hw_timer.h b/cpu/avr/hw_timer.h index 06ac73699..f417b0d69 100644 --- a/cpu/avr/hw_timer.h +++ b/cpu/avr/hw_timer.h @@ -215,7 +215,57 @@ * Note that this sets the compare output mode COM registers to 0, * turning off PWM on outputs. */ -int8_t hwtimer_ini (uint8_t timer, uint8_t wgm, uint8_t clock, uint16_t maxt); +static inline int8_t +hwtimer_ini (uint8_t timer, uint8_t wgm, uint8_t clock, uint16_t maxt) +{ + int8_t i; + HWT_CHECK_TIMER (timer); + if (wgm > HWT_WGM_MASK || wgm == HWT_WGM_RESERVED) { + return HWT_ERR_INVALID_WGM; + } + if (clock > HWT_CLOCK_MASK) { + return HWT_ERR_INVALID_CLOCK; + } + /* Turn off clock, no need to disable interrupt */ + *HWT_TCCRB (timer) &= ~HWT_CLOCK_MASK; + + *HWT_TCCRA (timer) &= ~(HWT_WGM_MASK_LOW << HWT_WGM_SHIFT_LOW); + *HWT_TCCRA (timer) |= ((wgm & HWT_WGM_MASK_LOW) << HWT_WGM_SHIFT_LOW); + *HWT_TCCRB (timer) &= ~(HWT_WGM_MASK_HIGH << HWT_WGM_SHIFT_HIGH); + *HWT_TCCRB (timer) |= ((wgm & HWT_WGM_MASK_HIGH) << HWT_WGM_SHIFT_HIGH); + + for (i=0; i<3; i++) { + HWT_SET_COM (timer, i, HWT_COM_NORMAL); + } + + if ( wgm == HWT_WGM_PWM_PHASE_FRQ_ICR + || wgm == HWT_WGM_PWM_PHASE_ICR + || wgm == HWT_WGM_CTC_ICR + || wgm == HWT_WGM_PWM_FAST_ICR + ) + { + *HWT_ICR (timer) = maxt; + } + + if ( wgm == HWT_WGM_CTC_OCRA + || wgm == HWT_WGM_PWM_PHASE_FRQ_OCRA + || wgm == HWT_WGM_PWM_PHASE_OCRA + || wgm == HWT_WGM_PWM_FAST_OCRA + ) + { + *HWT_OCRA (timer) = maxt; + } + + /* Set clock, finally */ + *HWT_TCCRB (timer) |= clock; + return 0; +} + +/* Needed for implementation */ +#define HWT_PERIOD_MAX_ (0xFFFFFFFF / (F_CPU / 1000000)) +/* for 16-bit timer: */ +#define HWT_TICKS_MAX_ 0xFFFF +#define HWT_TICKS_MIN_ 0xFF /** * \brief Convenience function to initialize hardware timer for PWM @@ -248,8 +298,76 @@ int8_t hwtimer_ini (uint8_t timer, uint8_t wgm, uint8_t clock, uint16_t maxt); * pin associated with this register can not be used for PWM. Instead it * can be used to change the period. */ -int8_t -hwtimer_pwm_ini (uint8_t timer, uint32_t period_us, uint8_t pwm_type, uint8_t ocra); +static inline int8_t +hwtimer_pwm_ini (uint8_t timer, uint32_t period_us, uint8_t pwm_type, uint8_t ocra) +{ + uint32_t ticks = 0; + uint8_t clock = HWT_CLOCK_PRESCALER_1024; + uint8_t wgm = HWT_WGM_NORMAL; + HWT_CHECK_TIMER (timer); + if (period_us > HWT_PERIOD_MAX_) { + period_us = HWT_PERIOD_MAX_; + } + ticks = (F_CPU / 1000000) * period_us; + /* Non-fast PWM modes have half the frequency */ + if (pwm_type != HWT_PWM_FAST) { + ticks >>= 1; + } + + /* + * Divisors are 1, 8, 64, 256, 1024, shifts between these are + * 3, 3, 2, 2, respectively. We modify `ticks` in place, the AVR can + * shift only one bit in one instruction, so shifting isn't cheap. + * We try to get the *maximum* prescaler that still permits a tick + * resolution of at least 8 bit. + */ + if (ticks <= (HWT_TICKS_MIN_ << 3)) { + clock = HWT_CLOCK_PRESCALER_1; + } + else if ((ticks >>= 3) <= (HWT_TICKS_MIN_ << 3)) { + clock = HWT_CLOCK_PRESCALER_8; + } + else if ((ticks >>= 3) <= (HWT_TICKS_MIN_ << 2)) { + clock = HWT_CLOCK_PRESCALER_64; + } + else if ((ticks >>= 2) <= (HWT_TICKS_MIN_ << 2)) { + clock = HWT_CLOCK_PRESCALER_256; + } + else if ((ticks >>= 2) > HWT_TICKS_MAX_) { + ticks = HWT_TICKS_MAX_; + } + switch (pwm_type) { + case HWT_PWM_FAST: + wgm = ocra ? HWT_WGM_PWM_FAST_OCRA : HWT_WGM_PWM_FAST_ICR; + break; + case HWT_PWM_PHASE_CORRECT: + wgm = ocra ? HWT_WGM_PWM_PHASE_OCRA : HWT_WGM_PWM_PHASE_ICR; + break; + case HWT_PWM_PHASE_FRQ_CORRECT: + default: + wgm = ocra ? HWT_WGM_PWM_PHASE_FRQ_OCRA : HWT_WGM_PWM_PHASE_FRQ_ICR; + break; + } + /* Special 8- 9- 10-bit modes */ + if (pwm_type == HWT_PWM_FAST || pwm_type == HWT_PWM_PHASE_CORRECT) { + if (ticks == 0xFF) { + wgm = (pwm_type == HWT_PWM_FAST) + ? HWT_WGM_PWM_FAST_8_BIT + : HWT_WGM_PWM_PHASE_8_BIT; + } + else if (ticks == 0x1FF) { + wgm = (pwm_type == HWT_PWM_FAST) + ? HWT_WGM_PWM_FAST_9_BIT + : HWT_WGM_PWM_PHASE_9_BIT; + } + else if (ticks == 0x3FF) { + wgm = (pwm_type == HWT_PWM_FAST) + ? HWT_WGM_PWM_FAST_10_BIT + : HWT_WGM_PWM_PHASE_10_BIT; + } + } + return hwtimer_ini (timer, wgm, clock, ticks); +} /* * Simple init macro for sane default values @@ -260,11 +378,42 @@ hwtimer_pwm_ini (uint8_t timer, uint32_t period_us, uint8_t pwm_type, uint8_t oc /** * \brief Maximum timer value usable in hwtimer_set_pwm * \param timer: Timer to use - * \return - * - * + * \return max. timer value according to current timer setup + * negative value if wrong timer given + * a positive value is guaranteed to fit into 16 bit unsigned. */ -uint32_t hwtimer_pwm_max_ticks (uint8_t timer); +static inline int32_t hwtimer_pwm_max_ticks (uint8_t timer) +{ + uint8_t wgm = 0; + HWT_CHECK_TIMER (timer); + wgm = ((*HWT_TCCRA (timer) >> HWT_WGM_SHIFT_LOW) & HWT_WGM_MASK_LOW) + | ((*HWT_TCCRB (timer) >> HWT_WGM_SHIFT_HIGH) & HWT_WGM_MASK_HIGH) + ; + switch (wgm) { + case HWT_WGM_PWM_PHASE_8_BIT: + case HWT_WGM_PWM_FAST_8_BIT: + return 0xFF; + case HWT_WGM_PWM_PHASE_9_BIT: + case HWT_WGM_PWM_FAST_9_BIT: + return 0x1FF; + case HWT_WGM_PWM_PHASE_10_BIT: + case HWT_WGM_PWM_FAST_10_BIT: + return 0x3FF; + case HWT_WGM_CTC_OCRA: + case HWT_WGM_PWM_PHASE_FRQ_OCRA: + case HWT_WGM_PWM_PHASE_OCRA: + case HWT_WGM_PWM_FAST_OCRA: + return *HWT_OCRA (timer); + case HWT_WGM_PWM_PHASE_FRQ_ICR: + case HWT_WGM_PWM_PHASE_ICR: + case HWT_WGM_CTC_ICR: + case HWT_WGM_PWM_FAST_ICR: + return *HWT_ICR (timer); + case HWT_WGM_NORMAL: + return 0xFFFF; + } + return HWT_ERR_INVALID_WGM; +} /* * The following functions are defined inline to allow for compiler