b6be226e69
We can now directly compile arduino sketches (.pde) files. Arduino compatible analogWrite works now. But there is still a long way to go, serial I/O and timer stuff (delay, millis etc) currently don't work (not tested but I don't expect this to work). It can be used in an arduino sketch or in a normal contiki program. We get a PWM frequency of 490.2 Hz (a period of 2.040 ms), that's Arduino compatible. If you need different frequencies see native timer usage in examples/osd/pwm-example In a contiki program you have to call arduino_pwm_timer_init to initialize the timer before pwm works. The arduino sketch wrapper already does this. For running a sketch, see examples/osd/arduino-sketch
372 lines
12 KiB
C
372 lines
12 KiB
C
/*
|
|
* 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.
|
|
*/
|
|
|
|
/**
|
|
* \defgroup hardware timer
|
|
*
|
|
* This module wraps hardware timers of AVR microcontrollers.
|
|
* Currently we only support 16-bit timers. The main focus is currently
|
|
* on PWM generation. But input capture and interrupt routines are on
|
|
* the TODO list, see below. We currently support the AVR ATmega128RFA1
|
|
* so this should be generalized to supported timers of other AVR
|
|
* microcontrollers.
|
|
*
|
|
* Datasheet references in the following refer to ATmega128RFA1 data sheet
|
|
*
|
|
* TODO: Allow input capture.
|
|
* TODO: Allow definition of interrupt routine; check if merkur board
|
|
* supports necessary pins.
|
|
* TODO: Generalize for 8-bit timers.
|
|
* TODO: Check other AVR microcontrollers and the supported timers.
|
|
*
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* \file
|
|
* Header file for hardware timer of AVR microcontrollers
|
|
* \author
|
|
* Ralf Schlatterbeck <rsc@runtux.com>
|
|
*
|
|
*/
|
|
|
|
#ifndef hw_timer_h
|
|
#define hw_timer_h
|
|
|
|
#include "contiki.h"
|
|
#include "rtimer-arch.h"
|
|
|
|
#ifndef PLAT_TIMER
|
|
#define PLAT_TIMER -1
|
|
#endif
|
|
|
|
/*
|
|
* All routines return a negative number for error and 0 for success.
|
|
* The negative return value indicates the error.
|
|
*/
|
|
#define HWT_ERR_INVALID_TIMER (-1)
|
|
#define HWT_ERR_INVALID_WGM (-2)
|
|
#define HWT_ERR_INVALID_COM (-3)
|
|
#define HWT_ERR_INVALID_CLOCK (-4)
|
|
#define HWT_ERR_INVALID_CHANNEL (-5)
|
|
|
|
/*
|
|
* Timer waveform generation modes (WGM), see data sheet
|
|
* chapter 18 "16-bit Timer/Counter (Timer/Counter 1,3,4, and 5)
|
|
* 18.9 "Modes of Operation", in particular Table 18-5
|
|
*/
|
|
#define HWT_WGM_NORMAL 0
|
|
#define HWT_WGM_PWM_PHASE_8_BIT 1
|
|
#define HWT_WGM_PWM_PHASE_9_BIT 2
|
|
#define HWT_WGM_PWM_PHASE_10_BIT 3
|
|
#define HWT_WGM_CTC_OCRA 4
|
|
#define HWT_WGM_PWM_FAST_8_BIT 5
|
|
#define HWT_WGM_PWM_FAST_9_BIT 6
|
|
#define HWT_WGM_PWM_FAST_10_BIT 7
|
|
#define HWT_WGM_PWM_PHASE_FRQ_ICR 8
|
|
#define HWT_WGM_PWM_PHASE_FRQ_OCRA 9
|
|
#define HWT_WGM_PWM_PHASE_ICR 10
|
|
#define HWT_WGM_PWM_PHASE_OCRA 11
|
|
#define HWT_WGM_CTC_ICR 12
|
|
#define HWT_WGM_RESERVED 13
|
|
#define HWT_WGM_PWM_FAST_ICR 14
|
|
#define HWT_WGM_PWM_FAST_OCRA 15
|
|
#define HWT_WGM_MASK 15
|
|
#define HWT_WGM_MASK_LOW 3
|
|
#define HWT_WGM_MASK_HIGH (HWT_WGM_MASK - HWT_WGM_MASK_LOW)
|
|
#define HWT_WGM_SHIFT_LOW 0
|
|
#define HWT_WGM_SHIFT_HIGH 1
|
|
|
|
/*
|
|
* Timer compare output modes (COM),
|
|
* chapter 18 "16-bit Timer/Counter (Timer/Counter 1,3,4, and 5)
|
|
* 18.8 "Compare Match Output Unit", in particular Tables 18-2,3,4
|
|
*/
|
|
#define HWT_COM_NORMAL 0
|
|
#define HWT_COM_TOGGLE 1
|
|
#define HWT_COM_CLEAR 2
|
|
#define HWT_COM_SET 3
|
|
#define HWT_COM_MASK 3
|
|
|
|
/*
|
|
* Clock select, clock can be off, use prescaler or external clock
|
|
* source on Tn pin. See Table 18-11 (for Timer 1 but this is the same
|
|
* for all the timers).
|
|
*/
|
|
#define HWT_CLOCK_OFF 0
|
|
#define HWT_CLOCK_PRESCALER_1 1
|
|
#define HWT_CLOCK_PRESCALER_8 2
|
|
#define HWT_CLOCK_PRESCALER_64 3
|
|
#define HWT_CLOCK_PRESCALER_256 4
|
|
#define HWT_CLOCK_PRESCALER_1024 5
|
|
#define HWT_CLOCK_EXTERN_FALLING 6
|
|
#define HWT_CLOCK_EXTERN_RISING 7
|
|
#define HWT_CLOCK_MASK 7
|
|
|
|
/*
|
|
* Timer channels A B C
|
|
*/
|
|
#define HWT_CHANNEL_A 0
|
|
#define HWT_CHANNEL_B 1
|
|
#define HWT_CHANNEL_C 2
|
|
#define HWT_CHANNEL_D 3
|
|
#define HWT_CHANNEL_MASK 3
|
|
|
|
/* The following macros are defined for timer values 1,3,4,5 */
|
|
#define HWT_ICR(t) \
|
|
((t)<4?((t)==1?(&ICR1) :(&ICR3)) :((t)==4?(&ICR4) :(&ICR5)))
|
|
#define HWT_OCRA(t) \
|
|
((t)<4?((t)==1?(&OCR1A):(&OCR3A)):((t)==4?(&OCR4A):(&OCR5A)))
|
|
#define HWT_OCRB(t) \
|
|
((t)<4?((t)==1?(&OCR1B):(&OCR3B)):((t)==4?(&OCR4B):(&OCR5B)))
|
|
#define HWT_OCRC(t) \
|
|
((t)<4?((t)==1?(&OCR1C):(&OCR3C)):((t)==4?(&OCR4C):(&OCR5C)))
|
|
|
|
#define HWT_OCR(t,c) \
|
|
( (c)==HWT_CHANNEL_A \
|
|
? (HWT_OCRA(t)) \
|
|
: ((c)==HWT_CHANNEL_B?HWT_OCRB(t):HWT_OCRC(t)) \
|
|
)
|
|
|
|
#define HWT_TCCRA(t) \
|
|
( (t)<4 \
|
|
? ((t)==1?(&TCCR1A):(&TCCR3A)) \
|
|
: ((t)==4?(&TCCR4A):(&TCCR5A)) \
|
|
)
|
|
#define HWT_TCCRB(t) \
|
|
( (t)<4 \
|
|
? ((t)==1?(&TCCR1B):(&TCCR3B)) \
|
|
: ((t)==4?(&TCCR4B):(&TCCR5B)) \
|
|
)
|
|
#define HWT_TCCRC(t) \
|
|
( (t)<4 \
|
|
? ((t)==1?(&TCCR1C):(&TCCR3C)) \
|
|
: ((t)==4?(&TCCR4C):(&TCCR5C)) \
|
|
)
|
|
#define HWT_TCNT(t) \
|
|
( (t)<4 \
|
|
? ((t)==1?(&TCNT1) :(&TCNT3)) \
|
|
: ((t)==4?(&TCNT4) :(&TCNT5)) \
|
|
)
|
|
|
|
#define HWT_SET_COM(timer, channel, com) \
|
|
((*HWT_TCCRA (timer) &= ~(HWT_COM_MASK << (6 - 2 * (channel)))) \
|
|
,(*HWT_TCCRA (timer) |= ((com) << (6 - 2 * (channel)))) \
|
|
)
|
|
|
|
#define HWT_CHECK_TIMER(timer) \
|
|
do { \
|
|
if ((timer) == 0 || (timer) == 2 || (timer) == PLAT_TIMER || (timer) > 5) {\
|
|
return HWT_ERR_INVALID_TIMER; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define HWT_CHECK_CHANNEL(chan) \
|
|
do { \
|
|
if ((chan) > HWT_CHANNEL_C) { \
|
|
return HWT_ERR_INVALID_CHANNEL; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define HWT_PWM_FAST 0
|
|
#define HWT_PWM_PHASE_CORRECT 1
|
|
#define HWT_PWM_PHASE_FRQ_CORRECT 2
|
|
|
|
/**
|
|
* \brief Initialize the hardware timer with the given settings
|
|
* \param timer: Timer to use
|
|
* \param wgm: waveform generation mode to use, see definitions
|
|
* \param clock: Prescaler or external clock settings
|
|
* \param maxt: Maximum counter value, not used for fixed modes, this
|
|
* sets ICRn for the ICR modes and OCRnA for the OCR modes
|
|
* \return see HWT_ERR definitions for return codes, returns 0 if ok
|
|
*
|
|
* The initial compare output mode is set to HWT_COM_NORMAL (off) for
|
|
* all outputs (pwm disabled).
|
|
*
|
|
* 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);
|
|
|
|
/**
|
|
* \brief Convenience function to initialize hardware timer for PWM
|
|
* \param timer: Timer to use
|
|
* \param pwm_type: See HWT_PWM* macros
|
|
* \param period_us: Period of the timer in µs
|
|
* \param ocra: Use OCRnA register if set, ICRn otherwise
|
|
* \return see HWT_ERR definitions for return codes, returns 0 if ok
|
|
*
|
|
* This function can be called instead of hwtimer_ini and sets up the
|
|
* timer for one of the PWM modes. There are fast, phase-correct and
|
|
* phase- and frequency correct modes, refer to the datasheet for
|
|
* semantics.
|
|
*
|
|
* The function tries to initialize the timer to a mode that doesn't use
|
|
* one of the internal registers OCRnA or ICRn for specifying the upper
|
|
* bound of the counter. For fast PWM and phase-correct PWM there are
|
|
* fixed 8-, 9-, and 10-bit modes that can be used if the computed value
|
|
* fits one of these setups.
|
|
*
|
|
* We try to get the *maximum* prescaler that still permits a tick
|
|
* resolution of at least 8 bit. This will not work for very high
|
|
* frequencies.
|
|
*
|
|
* If the specified period is too large to fit into a 16-bit timer we
|
|
* take the maximum period that is still possible, this may be
|
|
* substatially higher than specified.
|
|
*
|
|
* Note that when using OCRnA for the upper bound of the counter, the
|
|
* 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);
|
|
|
|
/*
|
|
* Simple init macro for sane default values
|
|
*/
|
|
#define hwtimer_pwm_ini_simple (timer, period_us) \
|
|
hwtimer_pwm_ini ((timer), HWT_PWM_PHASE_CORRECT, (period_us), 0)
|
|
|
|
/**
|
|
* \brief Maximum timer value usable in hwtimer_set_pwm
|
|
* \param timer: Timer to use
|
|
* \return
|
|
*
|
|
*
|
|
*/
|
|
uint32_t hwtimer_pwm_max_ticks (uint8_t timer);
|
|
|
|
/*
|
|
* The following functions are defined inline to allow for compiler
|
|
* optimizations if some of the parameters are constant.
|
|
*/
|
|
|
|
/**
|
|
* \brief Set PWM duty cycle
|
|
* \param timer: Timer to use
|
|
* \param channel: Channel to use, see HWT_CHANNEL definitions
|
|
* \param pwm: Duty cycle
|
|
* \return see HWT_ERR definitions for return codes, returns 0 if ok
|
|
*
|
|
* Note that the available range for the duty cycle depends on the timer
|
|
* setup and the chosen mode.
|
|
*/
|
|
static inline int8_t
|
|
hwtimer_set_pwm (uint8_t timer, uint8_t channel, uint16_t pwm)
|
|
{
|
|
uint8_t sreg = 0;
|
|
HWT_CHECK_TIMER (timer);
|
|
HWT_CHECK_CHANNEL (channel);
|
|
sreg = SREG;
|
|
cli ();
|
|
*HWT_OCR (timer, channel) = pwm;
|
|
SREG = sreg;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* \brief Set compare output mode
|
|
* \param timer: Timer to use
|
|
* \param channel: Channel to use, see HWT_CHANNEL definitions
|
|
* \param com: compare output mode for given channel
|
|
* \return see HWT_ERR definitions for return codes, returns 0 if ok
|
|
*/
|
|
static inline int8_t
|
|
hwtimer_set_com (uint8_t timer, uint8_t channel, uint8_t com)
|
|
{
|
|
HWT_CHECK_TIMER (timer);
|
|
HWT_CHECK_CHANNEL (channel);
|
|
if (com > HWT_COM_MASK) {
|
|
return HWT_ERR_INVALID_COM;
|
|
}
|
|
HWT_SET_COM (timer, channel, com);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* \brief Convenience function for setting compare output mode for PWM
|
|
* \param timer: Timer to use
|
|
* \param channel: Channel to use, see HWT_CHANNEL definitions
|
|
* \return see HWT_ERR definitions for return codes, returns 0 if ok
|
|
*/
|
|
static inline int8_t
|
|
hwtimer_pwm_enable (uint8_t timer, uint8_t channel)
|
|
{
|
|
return hwtimer_set_com (timer, channel, HWT_COM_CLEAR);
|
|
}
|
|
|
|
/**
|
|
* \brief Convenience function for inverse compare output mode for PWM
|
|
* \param timer: Timer to use
|
|
* \param channel: Channel to use, see HWT_CHANNEL definitions
|
|
* \return see HWT_ERR definitions for return codes, returns 0 if ok
|
|
*/
|
|
static inline int8_t
|
|
hwtimer_pwm_inverse (uint8_t timer, uint8_t channel)
|
|
{
|
|
return hwtimer_set_com (timer, channel, HWT_COM_SET);
|
|
}
|
|
|
|
/**
|
|
* \brief Convenience function for setting compare output mode to off
|
|
* \param timer: Timer to use
|
|
* \param channel: Channel to use, see HWT_CHANNEL definitions
|
|
* \return see HWT_ERR definitions for return codes, returns 0 if ok
|
|
*/
|
|
static inline int8_t
|
|
hwtimer_pwm_disable (uint8_t timer, uint8_t channel)
|
|
{
|
|
return hwtimer_set_com (timer, channel, HWT_COM_NORMAL);
|
|
}
|
|
|
|
/**
|
|
* \brief Turn off the clock
|
|
* \param timer: Timer to use
|
|
* \return see HWT_ERR definitions for return codes, returns 0 if ok
|
|
*/
|
|
static inline int8_t
|
|
hwtimer_fin (uint8_t timer)
|
|
{
|
|
HWT_CHECK_TIMER (timer);
|
|
*HWT_TCCRB (timer) &= ~HWT_CLOCK_MASK;
|
|
*HWT_TCCRB (timer) |= HWT_CLOCK_OFF; /* technically not necessary this is 0 */
|
|
return 0;
|
|
}
|
|
|
|
#endif /* hw_timer_h */
|
|
|
|
/*
|
|
* ex:ts=8:et:sw=2
|
|
*/
|
|
|
|
/** @} */
|