Got timer and pwm functions working with the ATmega168 (including pwm on pin 3); no longer relying on timer.c or timer.h from avrlib.

This commit is contained in:
David A. Mellis 2006-08-26 09:56:40 +00:00
parent 8b6864f3b8
commit 2f97a4b146
6 changed files with 133 additions and 836 deletions

View File

@ -30,6 +30,7 @@ UPDATES
0005 0005
ATmega168 support on the way (currently timers and pwm working).
New Wiring-compatible randomSeed(), random(max) and random(min, max) functions New Wiring-compatible randomSeed(), random(max) and random(min, max) functions
(except operating on longs instead of floats). (except operating on longs instead of floats).
Fixed bug that sometimes caused uploading of old versions of a sketch. Fixed bug that sometimes caused uploading of old versions of a sketch.
@ -115,4 +116,4 @@ Yaniv Steiner and Giorgio Olivero have been supporting the project and are
working at using it with the Instant Soup platform. working at using it with the Instant Soup platform.
Arduino uses the GNU avr-gcc toolchain, uisp, avr-libc, avrlib, and code Arduino uses the GNU avr-gcc toolchain, uisp, avr-libc, avrlib, and code
from Processing and Wiring. from Processing and Wiring.

View File

@ -105,6 +105,35 @@ pin_t digital_pin_to_port_array[NUM_DIGITAL_PINS] = {
pin_t *digital_pin_to_port = digital_pin_to_port_array; pin_t *digital_pin_to_port = digital_pin_to_port_array;
int analog_out_pin_to_timer_array[NUM_DIGITAL_PINS] = {
NOT_ON_TIMER,
NOT_ON_TIMER,
NOT_ON_TIMER,
// on the ATmega168, digital pin 3 has pwm
#if defined(__AVR_ATmega168__)
TIMER2B,
#else
NOT_ON_TIMER,
#endif
NOT_ON_TIMER,
NOT_ON_TIMER,
NOT_ON_TIMER,
NOT_ON_TIMER,
NOT_ON_TIMER,
TIMER1A,
TIMER1B,
#if defined(__AVR_ATmega168__)
TIMER2A,
#else
TIMER2,
#endif
NOT_ON_TIMER,
NOT_ON_TIMER,
};
int *analog_out_pin_to_timer = analog_out_pin_to_timer_array;
/*
// Some of the digital pins also support hardware PWM (analog output). // Some of the digital pins also support hardware PWM (analog output).
pin_t analog_out_pin_to_port_array[NUM_DIGITAL_PINS] = { pin_t analog_out_pin_to_port_array[NUM_DIGITAL_PINS] = {
{ NOT_A_PIN, NOT_A_PIN }, { NOT_A_PIN, NOT_A_PIN },
@ -124,7 +153,7 @@ pin_t analog_out_pin_to_port_array[NUM_DIGITAL_PINS] = {
}; };
pin_t *analog_out_pin_to_port = analog_out_pin_to_port_array; pin_t *analog_out_pin_to_port = analog_out_pin_to_port_array;
*/
pin_t analog_in_pin_to_port_array[NUM_ANALOG_IN_PINS] = { pin_t analog_in_pin_to_port_array[NUM_ANALOG_IN_PINS] = {
{ PC, 0 }, { PC, 0 },
{ PC, 1 }, { PC, 1 },

View File

@ -1,483 +0,0 @@
/*! \file timer.c \brief System Timer function library. */
//*****************************************************************************
//
// File Name : 'timer.c'
// Title : System Timer function library
// Author : Pascal Stang - Copyright (C) 2000-2002
// Created : 11/22/2000
// Revised : 07/09/2003
// Version : 1.1
// Target MCU : Atmel AVR Series
// Editor Tabs : 4
//
// This code is distributed under the GNU Public License
// which can be found at http://www.gnu.org/licenses/gpl.txt
//
//*****************************************************************************
//
// Modified by David A. Mellis, 9 July 2006.
#ifndef WIN32
#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/sleep.h>
#endif
#include "global.h"
#include "timer.h"
#include "rprintf.h"
// Program ROM constants
// the prescale division values stored in order of timer control register index
// STOP, CLK, CLK/8, CLK/64, CLK/256, CLK/1024
unsigned short __attribute__ ((progmem)) TimerPrescaleFactor[] = {0,1,8,64,256,1024};
// the prescale division values stored in order of timer control register index
// STOP, CLK, CLK/8, CLK/32, CLK/64, CLK/128, CLK/256, CLK/1024
unsigned short __attribute__ ((progmem)) TimerRTCPrescaleFactor[] = {0,1,8,32,64,128,256,1024};
// Global variables
// time registers
volatile unsigned long TimerPauseReg;
volatile unsigned long Timer0Reg0;
volatile unsigned long Timer2Reg0;
typedef void (*voidFuncPtr)(void);
volatile static voidFuncPtr TimerIntFunc[TIMER_NUM_INTERRUPTS];
/*
// delay for a minimum of <us> microseconds
// the time resolution is dependent on the time the loop takes
// e.g. with 4Mhz and 5 cycles per loop, the resolution is 1.25 us
void delay_us(unsigned short time_us)
{
unsigned short delay_loops;
register unsigned short i;
delay_loops = (time_us+3)/5*CYCLES_PER_US; // +3 for rounding up (dirty)
// one loop takes 5 cpu cycles
for (i=0; i < delay_loops; i++) {};
}
*/
/*
void delay_ms(unsigned char time_ms)
{
unsigned short delay_count = F_CPU / 4000;
unsigned short cnt;
asm volatile ("\n"
"L_dl1%=:\n\t"
"mov %A0, %A2\n\t"
"mov %B0, %B2\n"
"L_dl2%=:\n\t"
"sbiw %A0, 1\n\t"
"brne L_dl2%=\n\t"
"dec %1\n\t" "brne L_dl1%=\n\t":"=&w" (cnt)
:"r"(time_ms), "r"((unsigned short) (delay_count))
);
}
*/
/*
void timerInit(void)
{
u08 intNum;
// detach all user functions from interrupts
for(intNum=0; intNum<TIMER_NUM_INTERRUPTS; intNum++)
timerDetach(intNum);
// initialize all timers
timer0Init();
timer1Init();
#ifdef TCNT2 // support timer2 only if it exists
timer2Init();
#endif
// enable interrupts
sei();
}
void timer0Init()
{
// initialize timer 0
timer0SetPrescaler( TIMER0PRESCALE ); // set prescaler
outb(TCNT0, 0); // reset TCNT0
sbi(TIMSK, TOIE0); // enable TCNT0 overflow interrupt
timer0ClearOverflowCount(); // initialize time registers
}
*/
void timer1Init(void)
{
// initialize timer 1
timer1SetPrescaler( TIMER1PRESCALE ); // set prescaler
outb(TCNT1H, 0); // reset TCNT1
outb(TCNT1L, 0);
sbi(TIMSK, TOIE1); // enable TCNT1 overflow
}
#ifdef TCNT2 // support timer2 only if it exists
void timer2Init(void)
{
// initialize timer 2
timer2SetPrescaler( TIMER2PRESCALE ); // set prescaler
outb(TCNT2, 0); // reset TCNT2
sbi(TIMSK, TOIE2); // enable TCNT2 overflow
timer2ClearOverflowCount(); // initialize time registers
}
#endif
/*
void timer0SetPrescaler(u08 prescale)
{
// set prescaler on timer 0
outb(TCCR0, (inb(TCCR0) & ~TIMER_PRESCALE_MASK) | prescale);
}
*/
void timer1SetPrescaler(u08 prescale)
{
// set prescaler on timer 1
outb(TCCR1B, (inb(TCCR1B) & ~TIMER_PRESCALE_MASK) | prescale);
}
#ifdef TCNT2 // support timer2 only if it exists
void timer2SetPrescaler(u08 prescale)
{
// set prescaler on timer 2
outb(TCCR2, (inb(TCCR2) & ~TIMER_PRESCALE_MASK) | prescale);
}
#endif
/*
u16 timer0GetPrescaler(void)
{
// get the current prescaler setting
return (pgm_read_word(TimerPrescaleFactor+(inb(TCCR0) & TIMER_PRESCALE_MASK)));
}
u16 timer1GetPrescaler(void)
{
// get the current prescaler setting
return (pgm_read_word(TimerPrescaleFactor+(inb(TCCR1B) & TIMER_PRESCALE_MASK)));
}
#ifdef TCNT2 // support timer2 only if it exists
u16 timer2GetPrescaler(void)
{
//TODO: can we assume for all 3-timer AVR processors,
// that timer2 is the RTC timer?
// get the current prescaler setting
return (pgm_read_word(TimerRTCPrescaleFactor+(inb(TCCR2) & TIMER_PRESCALE_MASK)));
}
#endif
void timerAttach(u08 interruptNum, void (*userFunc)(void) )
{
// make sure the interrupt number is within bounds
if(interruptNum < TIMER_NUM_INTERRUPTS)
{
// set the interrupt function to run
// the supplied user's function
TimerIntFunc[interruptNum] = userFunc;
}
}
void timerDetach(u08 interruptNum)
{
// make sure the interrupt number is within bounds
if(interruptNum < TIMER_NUM_INTERRUPTS)
{
// set the interrupt function to run nothing
TimerIntFunc[interruptNum] = 0;
}
}
*/
/*
u32 timerMsToTics(u16 ms)
{
// calculate the prescaler division rate
u16 prescaleDiv = 1<<(pgm_read_byte(TimerPrescaleFactor+inb(TCCR0)));
// calculate the number of timer tics in x milliseconds
return (ms*(F_CPU/(prescaleDiv*256)))/1000;
}
u16 timerTicsToMs(u32 tics)
{
// calculate the prescaler division rate
u16 prescaleDiv = 1<<(pgm_read_byte(TimerPrescaleFactor+inb(TCCR0)));
// calculate the number of milliseconds in x timer tics
return (tics*1000*(prescaleDiv*256))/F_CPU;
}
*/
/*
void timerPause(unsigned short pause_ms)
{
// pauses for exactly <pause_ms> number of milliseconds
u08 timerThres;
u32 ticRateHz;
u32 pause;
// capture current pause timer value
timerThres = inb(TCNT0);
// reset pause timer overflow count
TimerPauseReg = 0;
// calculate delay for [pause_ms] milliseconds
// prescaler division = 1<<(pgm_read_byte(TimerPrescaleFactor+inb(TCCR0)))
ticRateHz = F_CPU/timer0GetPrescaler();
// precision management
// prevent overflow and precision underflow
// -could add more conditions to improve accuracy
if( ((ticRateHz < 429497) && (pause_ms <= 10000)) )
pause = (pause_ms*ticRateHz)/1000;
else
pause = pause_ms*(ticRateHz/1000);
// loop until time expires
while( ((TimerPauseReg<<8) | inb(TCNT0)) < (pause+timerThres) )
{
// DAM: these lines (in particular, the call to set_sleep_mode)
// caused avr-gcc to give an error about a misplaced } in linux,
// presumambly caused by a macro somewhere. Since they're not
// vital (and are causing problems), I'm commenting them out.
//if( TimerPauseReg < (pause>>8));
//{
// save power by idling the processor
//set_sleep_mode(SLEEP_MODE_IDLE);
//sleep_mode();
//}
}
// old inaccurate code, for reference
// calculate delay for [pause_ms] milliseconds
//u16 prescaleDiv = 1<<(pgm_read_byte(TimerPrescaleFactor+inb(TCCR0)));
//u32 pause = (pause_ms*(F_CPU/(prescaleDiv*256)))/1000;
//TimerPauseReg = 0;
//while(TimerPauseReg < pause);
}
void timer0ClearOverflowCount(void)
{
// clear the timer overflow counter registers
Timer0Reg0 = 0; // initialize time registers
}
long timer0GetOverflowCount(void)
{
// return the current timer overflow count
// (this is since the last timer0ClearOverflowCount() command was called)
return Timer0Reg0;
}
*/
#ifdef TCNT2 // support timer2 only if it exists
void timer2ClearOverflowCount(void)
{
// clear the timer overflow counter registers
Timer2Reg0 = 0; // initialize time registers
}
/*
long timer2GetOverflowCount(void)
{
// return the current timer overflow count
// (this is since the last timer2ClearOverflowCount() command was called)
return Timer2Reg0;
}
*/
#endif
void timer1PWMInit(u08 bitRes)
{
// configures timer1 for use with PWM output
// on OC1A and OC1B pins
// enable timer1 as 8,9,10bit PWM
if(bitRes == 9)
{ // 9bit mode
sbi(TCCR1A,PWM11);
cbi(TCCR1A,PWM10);
}
else if( bitRes == 10 )
{ // 10bit mode
sbi(TCCR1A,PWM11);
sbi(TCCR1A,PWM10);
}
else
{ // default 8bit mode
cbi(TCCR1A,PWM11);
sbi(TCCR1A,PWM10);
}
// clear output compare value A
outb(OCR1AH, 0);
outb(OCR1AL, 0);
// clear output compare value B
outb(OCR1BH, 0);
outb(OCR1BL, 0);
}
#ifdef WGM10
// include support for arbitrary top-count PWM
// on new AVR processors that support it
void timer1PWMInitICR(u16 topcount)
{
// set PWM mode with ICR top-count
cbi(TCCR1A,WGM10);
sbi(TCCR1A,WGM11);
sbi(TCCR1B,WGM12);
sbi(TCCR1B,WGM13);
// set top count value
ICR1 = topcount;
// clear output compare value A
OCR1A = 0;
// clear output compare value B
OCR1B = 0;
}
#endif
void timer1PWMOff(void)
{
// turn off timer1 PWM mode
cbi(TCCR1A,PWM11);
cbi(TCCR1A,PWM10);
// set PWM1A/B (OutputCompare action) to none
timer1PWMAOff();
timer1PWMBOff();
}
void timer1PWMAOn(void)
{
// turn on channel A (OC1A) PWM output
// set OC1A as non-inverted PWM
sbi(TCCR1A,COM1A1);
cbi(TCCR1A,COM1A0);
}
void timer1PWMBOn(void)
{
// turn on channel B (OC1B) PWM output
// set OC1B as non-inverted PWM
sbi(TCCR1A,COM1B1);
cbi(TCCR1A,COM1B0);
}
void timer1PWMAOff(void)
{
// turn off channel A (OC1A) PWM output
// set OC1A (OutputCompare action) to none
cbi(TCCR1A,COM1A1);
cbi(TCCR1A,COM1A0);
}
void timer1PWMBOff(void)
{
// turn off channel B (OC1B) PWM output
// set OC1B (OutputCompare action) to none
cbi(TCCR1A,COM1B1);
cbi(TCCR1A,COM1B0);
}
void timer1PWMASet(u16 pwmDuty)
{
// set PWM (output compare) duty for channel A
// this PWM output is generated on OC1A pin
// NOTE: pwmDuty should be in the range 0-255 for 8bit PWM
// pwmDuty should be in the range 0-511 for 9bit PWM
// pwmDuty should be in the range 0-1023 for 10bit PWM
//outp( (pwmDuty>>8), OCR1AH); // set the high 8bits of OCR1A
//outp( (pwmDuty&0x00FF), OCR1AL); // set the low 8bits of OCR1A
OCR1A = pwmDuty;
}
void timer1PWMBSet(u16 pwmDuty)
{
// set PWM (output compare) duty for channel B
// this PWM output is generated on OC1B pin
// NOTE: pwmDuty should be in the range 0-255 for 8bit PWM
// pwmDuty should be in the range 0-511 for 9bit PWM
// pwmDuty should be in the range 0-1023 for 10bit PWM
//outp( (pwmDuty>>8), OCR1BH); // set the high 8bits of OCR1B
//outp( (pwmDuty&0x00FF), OCR1BL); // set the low 8bits of OCR1B
OCR1B = pwmDuty;
}
/*
//! Interrupt handler for tcnt0 overflow interrupt
TIMER_INTERRUPT_HANDLER(SIG_OVERFLOW0)
{
Timer0Reg0++; // increment low-order counter
// increment pause counter
TimerPauseReg++;
// if a user function is defined, execute it too
if(TimerIntFunc[TIMER0OVERFLOW_INT])
TimerIntFunc[TIMER0OVERFLOW_INT]();
}
*/
//! Interrupt handler for tcnt1 overflow interrupt
TIMER_INTERRUPT_HANDLER(SIG_OVERFLOW1)
{
// if a user function is defined, execute it
if(TimerIntFunc[TIMER1OVERFLOW_INT])
TimerIntFunc[TIMER1OVERFLOW_INT]();
}
#ifdef TCNT2 // support timer2 only if it exists
//! Interrupt handler for tcnt2 overflow interrupt
TIMER_INTERRUPT_HANDLER(SIG_OVERFLOW2)
{
Timer2Reg0++; // increment low-order counter
// if a user function is defined, execute it
if(TimerIntFunc[TIMER2OVERFLOW_INT])
TimerIntFunc[TIMER2OVERFLOW_INT]();
}
#endif
#ifdef OCR0
/*
// include support for Output Compare 0 for new AVR processors that support it
//! Interrupt handler for OutputCompare0 match (OC0) interrupt
TIMER_INTERRUPT_HANDLER(SIG_OUTPUT_COMPARE0)
{
// if a user function is defined, execute it
if(TimerIntFunc[TIMER0OUTCOMPARE_INT])
TimerIntFunc[TIMER0OUTCOMPARE_INT]();
}
*/
#endif
//! Interrupt handler for CutputCompare1A match (OC1A) interrupt
TIMER_INTERRUPT_HANDLER(SIG_OUTPUT_COMPARE1A)
{
// if a user function is defined, execute it
if(TimerIntFunc[TIMER1OUTCOMPAREA_INT])
TimerIntFunc[TIMER1OUTCOMPAREA_INT]();
}
//! Interrupt handler for OutputCompare1B match (OC1B) interrupt
TIMER_INTERRUPT_HANDLER(SIG_OUTPUT_COMPARE1B)
{
// if a user function is defined, execute it
if(TimerIntFunc[TIMER1OUTCOMPAREB_INT])
TimerIntFunc[TIMER1OUTCOMPAREB_INT]();
}
//! Interrupt handler for InputCapture1 (IC1) interrupt
TIMER_INTERRUPT_HANDLER(SIG_INPUT_CAPTURE1)
{
// if a user function is defined, execute it
if(TimerIntFunc[TIMER1INPUTCAPTURE_INT])
TimerIntFunc[TIMER1INPUTCAPTURE_INT]();
}
//! Interrupt handler for OutputCompare2 match (OC2) interrupt
TIMER_INTERRUPT_HANDLER(SIG_OUTPUT_COMPARE2)
{
// if a user function is defined, execute it
if(TimerIntFunc[TIMER2OUTCOMPARE_INT])
TimerIntFunc[TIMER2OUTCOMPARE_INT]();
}

View File

@ -1,278 +0,0 @@
/*! \file timer.h \brief System Timer function library. */
//*****************************************************************************
//
// File Name : 'timer.h'
// Title : System Timer function library
// Author : Pascal Stang - Copyright (C) 2000-2002
// Created : 11/22/2000
// Revised : 02/10/2003
// Version : 1.1
// Target MCU : Atmel AVR Series
// Editor Tabs : 4
//
// This code is distributed under the GNU Public License
// which can be found at http://www.gnu.org/licenses/gpl.txt
//
//
// Notes: The Atmel AVR Series Processors each contain at least one
// hardware timer/counter. Many of the processors contain 2 or 3
// timers. Generally speaking, a timer is a hardware counter inside
// the processor which counts at a rate related to the main CPU clock
// frequency. Because the counter value increasing (counting up) at
// a precise rate, we can use it as a timer to create or measure
// precise delays, schedule events, or generate signals of a certain
// frequency or pulse-width.
// As an example, the ATmega163 processor has 3 timer/counters.
// Timer0, Timer1, and Timer2 are 8, 16, and 8 bits wide respectively.
// This means that they overflow, or roll over back to zero, at a
// count value of 256 for 8bits or 65536 for 16bits. A prescaler is
// avaiable for each timer, and the prescaler allows you to pre-divide
// the main CPU clock rate down to a slower speed before feeding it to
// the counting input of a timer. For example, if the CPU clock
// frequency is 3.69MHz, and Timer0's prescaler is set to divide-by-8,
// then Timer0 will "tic" at 3690000/8 = 461250Hz. Because Timer0 is
// an 8bit timer, it will count to 256 in just 256/461250Hz = 0.555ms.
// In fact, when it hits 255, it will overflow and start again at
// zero. In this case, Timer0 will overflow 461250/256 = 1801.76
// times per second.
// Timer0 can be used a number of ways simultaneously. First, the
// value of the timer can be read by accessing the CPU register TCNT0.
// We could, for example, figure out how long it takes to execute a
// C command by recording the value of TCNT0 before and after
// execution, then subtract (after-before) = time elapsed. Or we can
// enable the overflow interrupt which goes off every time T0
// overflows and count out longer delays (multiple overflows), or
// execute a special periodic function at every overflow.
// The other timers (Timer1 and Timer2) offer all the abilities of
// Timer0 and many more features. Both T1 and T2 can operate as
// general-purpose timers, but T1 has special hardware allowing it to
// generate PWM signals, while T2 is specially designed to help count
// out real time (like hours, minutes, seconds). See the
// Timer/Counter section of the processor datasheet for more info.
//
//*****************************************************************************
#ifndef TIMER_H
#define TIMER_H
#include "global.h"
// constants/macros/typdefs
// processor compatibility fixes
#ifdef __AVR_ATmega323__
// redefinition for the Mega323
#define CTC1 CTC10
#endif
#ifndef PWM10
// mega128 PWM bits
#define PWM10 WGM10
#define PWM11 WGM11
#endif
// Timer/clock prescaler values and timer overflow rates
// tics = rate at which the timer counts up
// 8bitoverflow = rate at which the timer overflows 8bits (or reaches 256)
// 16bit [overflow] = rate at which the timer overflows 16bits (65536)
//
// overflows can be used to generate periodic interrupts
//
// for 8MHz crystal
// 0 = STOP (Timer not counting)
// 1 = CLOCK tics= 8MHz 8bitoverflow= 31250Hz 16bit= 122.070Hz
// 2 = CLOCK/8 tics= 1MHz 8bitoverflow= 3906.25Hz 16bit= 15.259Hz
// 3 = CLOCK/64 tics= 125kHz 8bitoverflow= 488.28Hz 16bit= 1.907Hz
// 4 = CLOCK/256 tics= 31250Hz 8bitoverflow= 122.07Hz 16bit= 0.477Hz
// 5 = CLOCK/1024 tics= 7812.5Hz 8bitoverflow= 30.52Hz 16bit= 0.119Hz
// 6 = External Clock on T(x) pin (falling edge)
// 7 = External Clock on T(x) pin (rising edge)
// for 4MHz crystal
// 0 = STOP (Timer not counting)
// 1 = CLOCK tics= 4MHz 8bitoverflow= 15625Hz 16bit= 61.035Hz
// 2 = CLOCK/8 tics= 500kHz 8bitoverflow= 1953.125Hz 16bit= 7.629Hz
// 3 = CLOCK/64 tics= 62500Hz 8bitoverflow= 244.141Hz 16bit= 0.954Hz
// 4 = CLOCK/256 tics= 15625Hz 8bitoverflow= 61.035Hz 16bit= 0.238Hz
// 5 = CLOCK/1024 tics= 3906.25Hz 8bitoverflow= 15.259Hz 16bit= 0.060Hz
// 6 = External Clock on T(x) pin (falling edge)
// 7 = External Clock on T(x) pin (rising edge)
// for 3.69MHz crystal
// 0 = STOP (Timer not counting)
// 1 = CLOCK tics= 3.69MHz 8bitoverflow= 14414Hz 16bit= 56.304Hz
// 2 = CLOCK/8 tics= 461250Hz 8bitoverflow= 1801.758Hz 16bit= 7.038Hz
// 3 = CLOCK/64 tics= 57625.25Hz 8bitoverflow= 225.220Hz 16bit= 0.880Hz
// 4 = CLOCK/256 tics= 14414.063Hz 8bitoverflow= 56.305Hz 16bit= 0.220Hz
// 5 = CLOCK/1024 tics= 3603.516Hz 8bitoverflow= 14.076Hz 16bit= 0.055Hz
// 6 = External Clock on T(x) pin (falling edge)
// 7 = External Clock on T(x) pin (rising edge)
// for 32.768KHz crystal on timer 2 (use for real-time clock)
// 0 = STOP
// 1 = CLOCK tics= 32.768kHz 8bitoverflow= 128Hz
// 2 = CLOCK/8 tics= 4096kHz 8bitoverflow= 16Hz
// 3 = CLOCK/32 tics= 1024kHz 8bitoverflow= 4Hz
// 4 = CLOCK/64 tics= 512Hz 8bitoverflow= 2Hz
// 5 = CLOCK/128 tics= 256Hz 8bitoverflow= 1Hz
// 6 = CLOCK/256 tics= 128Hz 8bitoverflow= 0.5Hz
// 7 = CLOCK/1024 tics= 32Hz 8bitoverflow= 0.125Hz
#define TIMER_CLK_STOP 0x00 ///< Timer Stopped
#define TIMER_CLK_DIV1 0x01 ///< Timer clocked at F_CPU
#define TIMER_CLK_DIV8 0x02 ///< Timer clocked at F_CPU/8
#define TIMER_CLK_DIV64 0x03 ///< Timer clocked at F_CPU/64
#define TIMER_CLK_DIV256 0x04 ///< Timer clocked at F_CPU/256
#define TIMER_CLK_DIV1024 0x05 ///< Timer clocked at F_CPU/1024
#define TIMER_CLK_T_FALL 0x06 ///< Timer clocked at T falling edge
#define TIMER_CLK_T_RISE 0x07 ///< Timer clocked at T rising edge
#define TIMER_PRESCALE_MASK 0x07 ///< Timer Prescaler Bit-Mask
#define TIMERRTC_CLK_STOP 0x00 ///< RTC Timer Stopped
#define TIMERRTC_CLK_DIV1 0x01 ///< RTC Timer clocked at F_CPU
#define TIMERRTC_CLK_DIV8 0x02 ///< RTC Timer clocked at F_CPU/8
#define TIMERRTC_CLK_DIV32 0x03 ///< RTC Timer clocked at F_CPU/32
#define TIMERRTC_CLK_DIV64 0x04 ///< RTC Timer clocked at F_CPU/64
#define TIMERRTC_CLK_DIV128 0x05 ///< RTC Timer clocked at F_CPU/128
#define TIMERRTC_CLK_DIV256 0x06 ///< RTC Timer clocked at F_CPU/256
#define TIMERRTC_CLK_DIV1024 0x07 ///< RTC Timer clocked at F_CPU/1024
#define TIMERRTC_PRESCALE_MASK 0x07 ///< RTC Timer Prescaler Bit-Mask
// default prescale settings for the timers
// these settings are applied when you call
// timerInit or any of the timer<x>Init
#define TIMER0PRESCALE TIMER_CLK_DIV8 ///< timer 0 prescaler default
#define TIMER1PRESCALE TIMER_CLK_DIV64 ///< timer 1 prescaler default
#define TIMER2PRESCALE TIMERRTC_CLK_DIV64 ///< timer 2 prescaler default
// interrupt macros for attaching user functions to timer interrupts
// use these with timerAttach( intNum, function )
#define TIMER0OVERFLOW_INT 0
#define TIMER1OVERFLOW_INT 1
#define TIMER1OUTCOMPAREA_INT 2
#define TIMER1OUTCOMPAREB_INT 3
#define TIMER1INPUTCAPTURE_INT 4
#define TIMER2OVERFLOW_INT 5
#define TIMER2OUTCOMPARE_INT 6
#ifdef OCR0 // for processors that support output compare on Timer0
#define TIMER0OUTCOMPARE_INT 7
#define TIMER_NUM_INTERRUPTS 8
#else
#define TIMER_NUM_INTERRUPTS 7
#endif
// default type of interrupt handler to use for timers
// *do not change unless you know what you're doing
// Value may be SIGNAL or INTERRUPT
#ifndef TIMER_INTERRUPT_HANDLER
#define TIMER_INTERRUPT_HANDLER SIGNAL
#endif
// functions
#define delay delay_us
#define delay_ms timerPause
void delay_us(unsigned short time_us);
//! initializes timing system (all timers)
// runs all timer init functions
// sets all timers to default prescale values #defined in systimer.c
void timerInit(void);
// default initialization routines for each timer
void timer0Init(void); ///< initialize timer0
void timer1Init(void); ///< initialize timer1
#ifdef TCNT2 // support timer2 only if it exists
void timer2Init(void); ///< initialize timer2
#endif
// Clock prescaler set/get commands for each timer/counter
// For setting the prescaler, you should use one of the #defines
// above like TIMER_CLK_DIVx, where [x] is the division rate
// you want.
// When getting the current prescaler setting, the return value
// will be the [x] division value currently set.
void timer0SetPrescaler(u08 prescale); ///< set timer0 prescaler
u16 timer0GetPrescaler(void); ///< get timer0 prescaler
void timer1SetPrescaler(u08 prescale); ///< set timer1 prescaler
u16 timer1GetPrescaler(void); ///< get timer0 prescaler
#ifdef TCNT2 // support timer2 only if it exists
void timer2SetPrescaler(u08 prescale); ///< set timer2 prescaler
u16 timer2GetPrescaler(void); ///< get timer2 prescaler
#endif
// TimerAttach and Detach commands
// These functions allow the attachment (or detachment) of any user function
// to a timer interrupt. "Attaching" one of your own functions to a timer
// interrupt means that it will be called whenever that interrupt happens.
// Using attach is better than rewriting the actual INTERRUPT() function
// because your code will still work and be compatible if the timer library
// is updated. Also, using Attach allows your code and any predefined timer
// code to work together and at the same time. (ie. "attaching" your own
// function to the timer0 overflow doesn't prevent timerPause from working,
// but rather allows you to share the interrupt.)
//
// timerAttach(TIMER1OVERFLOW_INT, myOverflowFunction);
// timerDetach(TIMER1OVERFLOW_INT)
//
// timerAttach causes the myOverflowFunction() to be attached, and therefore
// execute, whenever an overflow on timer1 occurs. timerDetach removes the
// association and executes no user function when the interrupt occurs.
// myOverflowFunction must be defined with no return value and no arguments:
//
// void myOverflowFunction(void) { ... }
//! Attach a user function to a timer interrupt
void timerAttach(u08 interruptNum, void (*userFunc)(void) );
//! Detach a user function from a timer interrupt
void timerDetach(u08 interruptNum);
// timing commands
//! timerPause pauses for the number of milliseconds specified in <pause_ms>
void timerPause(unsigned short pause_ms);
// overflow counters
void timer0ClearOverflowCount(void); ///< clear timer0's overflow counter
long timer0GetOverflowCount(void); ///< read timer0's overflow counter
#ifdef TCNT2 // support timer2 only if it exists
void timer2ClearOverflowCount(void); ///< clear timer2's overflow counter
long timer2GetOverflowCount(void); ///< read timer0's overflow counter
#endif
// PWM initialization and set commands for timer1
// timer1PWMInit()
// configures the timer1 hardware for PWM mode on pins OC1A and OC1B.
// bitRes should be 8,9,or 10 for 8,9,or 10bit PWM resolution
//
// timer1PWMOff()
// turns off all timer1 PWM output and set timer mode to normal state
//
// timer1PWMAOn() and timer1PWMBOn()
// turn on output of PWM signals to OC1A or OC1B pins
// NOTE: Until you define the OC1A and OC1B pins as outputs, and run
// this "on" command, no PWM output will be output
//
// timer1PWMAOff() and timer1PWMBOff()
// turn off output of PWM signals to OC1A or OC1B pins
//
// timer1PWMASet() and timer1PWMBSet()
// sets the PWM duty cycle for each channel
// NOTE: <pwmDuty> should be in the range 0-255 for 8bit PWM
// <pwmDuty> should be in the range 0-511 for 9bit PWM
// <pwmDuty> should be in the range 0-1023 for 10bit PWM
// NOTE: the PWM frequency can be controlled in increments by setting the
// prescaler for timer1
void timer1PWMInit(u08 bitRes); ///< initialize and set timer1 mode to PWM
void timer1PWMInitICR(u16 topcount);///< initialize and set timer1 mode to PWM with specific top count
void timer1PWMOff(void); ///< turn off all timer1 PWM output and set timer mode to normal
void timer1PWMAOn(void); ///< turn on timer1 Channel A (OC1A) PWM output
void timer1PWMBOn(void); ///< turn on timer1 Channel B (OC1B) PWM output
void timer1PWMAOff(void); ///< turn off timer1 Channel A (OC1A) PWM output
void timer1PWMBOff(void); ///< turn off timer1 Channel B (OC1B) PWM output
void timer1PWMASet(u16 pwmDuty); ///< set duty of timer1 Channel A (OC1A) PWM output
void timer1PWMBSet(u16 pwmDuty); ///< set duty of timer1 Channel B (OC1B) PWM output
// Pulse generation commands have been moved to the pulse.c library
#endif

View File

@ -37,15 +37,8 @@
#endif #endif
// from Pascal's avrlib // from Pascal's avrlib
#include "global.h"
//#include "a2d.h"
#include "timer.h"
#include "uart.h" #include "uart.h"
// timer.h #defines delay to be delay_us, we need to undefine
// it so our delay can be in milliseconds.
#undef delay
#include "wiring.h" #include "wiring.h"
// The number of times timer 0 has overflowed since the program started. // The number of times timer 0 has overflowed since the program started.
@ -66,14 +59,9 @@ int digitalPinToBit(int pin)
return digital_pin_to_port[pin].bit; return digital_pin_to_port[pin].bit;
} }
int analogOutPinToPort(int pin) int analogOutPinToTimer(int pin)
{ {
return analog_out_pin_to_port[pin].port; return analog_out_pin_to_timer[pin];
}
int analogOutPinToBit(int pin)
{
return analog_out_pin_to_port[pin].bit;
} }
int analogInPinToBit(int pin) int analogInPinToBit(int pin)
@ -81,26 +69,6 @@ int analogInPinToBit(int pin)
return analog_in_pin_to_port[pin].bit; return analog_in_pin_to_port[pin].bit;
} }
void timer2PWMOn()
{
// configure timer 2 for normal (non-inverting) pwm operation
// this attaches the timer to the pwm pin
sbi(TCCR2, COM21);
cbi(TCCR2, COM20);
}
void timer2PWMOff()
{
// disconnect the timer from the pwm pin
cbi(TCCR2, COM21);
cbi(TCCR2, COM20);
}
void timer2PWMSet(unsigned char val)
{
OCR2 = val;
}
void pinMode(int pin, int mode) void pinMode(int pin, int mode)
{ {
if (digitalPinToPort(pin) != NOT_A_PIN) { if (digitalPinToPort(pin) != NOT_A_PIN) {
@ -119,14 +87,22 @@ void digitalWrite(int pin, int val)
// If the pin that support PWM output, we need to turn it off // If the pin that support PWM output, we need to turn it off
// before doing a digital write. // before doing a digital write.
if (analogOutPinToBit(pin) == 1) if (analogOutPinToTimer(pin) == TIMER1A)
timer1PWMAOff(); cbi(TCCR1A, COM1A1);
if (analogOutPinToBit(pin) == 2) if (analogOutPinToTimer(pin) == TIMER1B)
timer1PWMBOff(); cbi(TCCR1A, COM1B1);
if (analogOutPinToBit(pin) == 3) #if defined(__AVR_ATmega168__)
timer2PWMOff(); if (analogOutPinToTimer(pin) == TIMER2A)
cbi(TCCR2A, COM2A1);
if (analogOutPinToTimer(pin) == TIMER2B)
cbi(TCCR2A, COM2B1);
#else
if (analogOutPinToTimer(pin) == TIMER2)
cbi(TCCR2, COM21);
#endif
if (val == LOW) if (val == LOW)
cbi(_SFR_IO8(port_to_output[digitalPinToPort(pin)]), cbi(_SFR_IO8(port_to_output[digitalPinToPort(pin)]),
@ -143,14 +119,22 @@ int digitalRead(int pin)
// If the pin that support PWM output, we need to turn it off // If the pin that support PWM output, we need to turn it off
// before getting a digital reading. // before getting a digital reading.
if (analogOutPinToBit(pin) == 1) if (analogOutPinToTimer(pin) == TIMER1A)
timer1PWMAOff(); cbi(TCCR1A, COM1A1);
if (analogOutPinToBit(pin) == 2) if (analogOutPinToTimer(pin) == TIMER1B)
timer1PWMBOff(); cbi(TCCR1A, COM1B1);
if (analogOutPinToBit(pin) == 3) #if defined(__AVR_ATmega168__)
timer2PWMOff(); if (analogOutPinToTimer(pin) == TIMER2A)
cbi(TCCR2A, COM2A1);
if (analogOutPinToTimer(pin) == TIMER2B)
cbi(TCCR2A, COM2B1);
#else
if (analogOutPinToTimer(pin) == TIMER2)
cbi(TCCR2, COM21);
#endif
return (_SFR_IO8(port_to_input[digitalPinToPort(pin)]) >> return (_SFR_IO8(port_to_input[digitalPinToPort(pin)]) >>
digitalPinToBit(pin)) & 0x01; digitalPinToBit(pin)) & 0x01;
@ -197,18 +181,36 @@ void analogWrite(int pin, int val)
// writing with them. Also, make sure the pin is in output mode // writing with them. Also, make sure the pin is in output mode
// for consistenty with Wiring, which doesn't require a pinMode // for consistenty with Wiring, which doesn't require a pinMode
// call for the analog output pins. // call for the analog output pins.
if (analogOutPinToBit(pin) == 1) { pinMode(pin, OUTPUT);
pinMode(pin, OUTPUT);
timer1PWMAOn(); if (analogOutPinToTimer(pin) == TIMER1A) {
timer1PWMASet(val); // connect pwm to pin on timer 1, channel A
} else if (analogOutPinToBit(pin) == 2) { sbi(TCCR1A, COM1A1);
pinMode(pin, OUTPUT); // set pwm duty
timer1PWMBOn(); OCR1A = val;
timer1PWMBSet(val); } else if (analogOutPinToTimer(pin) == TIMER1B) {
} else if (analogOutPinToBit(pin) == 3) { // connect pwm to pin on timer 1, channel B
pinMode(pin, OUTPUT); sbi(TCCR1A, COM1B1);
timer2PWMOn(); // set pwm duty
timer2PWMSet(val); OCR1B = val;
#if defined(__AVR_ATmega168__)
} else if (analogOutPinToTimer(pin) == TIMER2A) {
// connect pwm to pin on timer 2, channel A
sbi(TCCR2A, COM2A1);
// set pwm duty
OCR2A = val;
} else if (analogOutPinToTimer(pin) == TIMER2B) {
// connect pwm to pin on timer 2, channel B
sbi(TCCR2A, COM2B1);
// set pwm duty
OCR2B = val;
#else
} else if (analogOutPinToTimer(pin) == TIMER2) {
// connect pwm to pin on timer 2, channel B
sbi(TCCR2, COM21);
// set pwm duty
OCR2 = val;
#endif
} else if (val < 128) } else if (val < 128)
digitalWrite(pin, LOW); digitalWrite(pin, LOW);
else else
@ -319,7 +321,7 @@ void print(const char *format, ...)
} }
*/ */
TIMER_INTERRUPT_HANDLER(SIG_OVERFLOW0) SIGNAL(SIG_OVERFLOW0)
{ {
timer0_overflow_count++; timer0_overflow_count++;
} }
@ -434,30 +436,47 @@ unsigned long pulseIn(int pin, int state)
int main(void) int main(void)
{ {
// this needs to be called before setup() or some functions won't // this needs to be called before setup() or some functions won't
// work there // work there
sei(); sei();
// timer 0 is used for millis() and delay() // timer 0 is used for millis() and delay()
//timer0Init();
timer0_overflow_count = 0; timer0_overflow_count = 0;
// set timer 0 prescale factor to 8 // set timer 0 prescale factor to 8
#if defined(__AVR_ATmega168__)
sbi(TCCR0B, CS01);
#else
sbi(TCCR0, CS01); sbi(TCCR0, CS01);
#endif
// enable timer 0 overflow interrupt // enable timer 0 overflow interrupt
#if defined(__AVR_ATmega168__)
sbi(TIMSK0, TOIE0);
#else
sbi(TIMSK, TOIE0); sbi(TIMSK, TOIE0);
#endif
// timers 1 & 2 are used for the hardware pwm // timers 1 and 2 are used for phase-correct hardware pwm
timer1Init();
//timer1SetPrescaler(TIMER_CLK_DIV1);
timer1PWMInit(8);
timer2Init();
// configure timer 2 for phase correct pwm
// this is better for motors as it ensures an even waveform // this is better for motors as it ensures an even waveform
// note, however, that fast pwm mode can achieve a frequency of up // note, however, that fast pwm mode can achieve a frequency of up
// 8 MHz (with a 16 MHz clock) at 50% duty cycle // 8 MHz (with a 16 MHz clock) at 50% duty cycle
cbi(TCCR2, WGM21);
// set timer 1 prescale factor to 64
sbi(TCCR1B, CS11);
sbi(TCCR1B, CS10);
// put timer 1 in 8-bit phase correct pwm mode
sbi(TCCR1A, WGM10);
// set timer 2 prescale factor to 64
#if defined(__AVR_ATmega168__)
sbi(TCCR2B, CS22);
#else
sbi(TCCR2, CS22);
#endif
// configure timer 2 for phase correct pwm (8-bit)
#if defined(__AVR_ATmega168__)
sbi(TCCR2A, WGM20);
#else
sbi(TCCR2, WGM20); sbi(TCCR2, WGM20);
#endif
// set a2d reference to AVCC (5 volts) // set a2d reference to AVCC (5 volts)
cbi(ADMUX, REFS1); cbi(ADMUX, REFS1);

View File

@ -89,6 +89,15 @@ void loop(void);
#define NOT_A_PIN 0 #define NOT_A_PIN 0
#define NOT_A_PORT -1 #define NOT_A_PORT -1
#define NOT_ON_TIMER -1
#define TIMER0A 0
#define TIMER0B 1
#define TIMER1A 2
#define TIMER1B 3
#define TIMER2 4
#define TIMER2A 5
#define TIMER2B 6
typedef struct { typedef struct {
int port; int port;
int bit; int bit;
@ -99,7 +108,7 @@ extern int port_to_input[];
extern int port_to_output[]; extern int port_to_output[];
extern pin_t *digital_pin_to_port; extern pin_t *digital_pin_to_port;
extern pin_t *analog_in_pin_to_port; extern pin_t *analog_in_pin_to_port;
extern pin_t *analog_out_pin_to_port; extern int *analog_out_pin_to_timer;
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"