PWM.h
/*
Copyright (c) 2012 Sam Knight
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
This library is built to support two of the AVR Architecture 'groups' that Arduino uses
a) ATmega48/88/168/328,
b) ATmega640/1280/1281/2560/2561
*/
#ifndef PWM_H_
#define PWM_H_
#include "avr/pgmspace.h"
#include "math.h"
#if defined(__AVR_ATmega640__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__)
#include "utility/ATimerDefs.h"
#elif defined(__AVR_ATmega48__) || defined(__AVR_ATmega88__) || defined(__AVR_ATmega88P__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__)
#include "utility/BTimerDefs.h"
#endif
#if defined(__AVR_ATmega640__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__)
// 16 bit timers
extern uint32_t GetFrequency_16(const int16_t timerOffset);
extern bool SetFrequency_16(const int16_t timerOffset, uint32_t f);
extern uint16_t GetPrescaler_16(const int16_t timerOffset);
extern void SetPrescaler_16(const int16_t timerOffset, prescaler psc);
extern void SetTop_16(const int16_t timerOffset, uint16_t top);
extern uint16_t GetTop_16(const int16_t timerOffset);
extern void Initialize_16(const int16_t timerOffset);
extern float GetResolution_16(const int16_t timerOffset);
// 8 bit timers
extern uint32_t GetFrequency_8(const int16_t timerOffset);
extern bool SetFrequency_8(const int16_t timerOffset, uint32_t f);
extern uint16_t GetPrescaler_8(const int16_t timerOffset);
extern void SetPrescaler_8(const int16_t timerOffset, prescaler psc);
extern void SetPrescalerAlt_8(const int16_t timerOffset, prescaler_alt psc);
extern void SetTop_8(const int16_t timerOffset, uint8_t top);
extern uint8_t GetTop_8(const int16_t timerOffset);
extern void Initialize_8(const int16_t timerOffset);
extern float GetResolution_8(const int16_t timerOffset);
#endif
#if defined(__AVR_ATmega48__) || defined(__AVR_ATmega88__) || defined(__AVR_ATmega88P__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__)
// 16 bit timers
extern uint32_t GetFrequency_16();
extern bool SetFrequency_16(uint32_t f);
extern uint16_t GetPrescaler_16();
extern void SetPrescaler_16(prescaler psc);
extern void SetTop_16(uint16_t top);
extern uint16_t GetTop_16();
extern void Initialize_16();
extern float GetResolution_16();
// 8 bit timers
extern uint32_t GetFrequency_8(const int16_t timerOffset);
extern bool SetFrequency_8(const int16_t timerOffset, uint32_t f);
extern uint16_t GetPrescaler_8(const int16_t timerOffset);
extern void SetPrescaler_8(const int16_t timerOffset, prescaler psc);
extern void SetPrescalerAlt_8(const int16_t timerOffset, prescaler_alt psc);
extern void SetTop_8(const int16_t timerOffset, uint8_t top);
extern uint8_t GetTop_8(const int16_t timerOffset);
extern void Initialize_8(const int16_t timerOffset);
extern float GetResolution_8(const int16_t timerOffset);
#endif
//common functions
extern void InitTimers();
extern void InitTimersSafe(); //doesn't init timers responsible for time keeping functions
extern void pwmWrite(uint8_t pin, uint8_t val);
extern void pwmWriteHR(uint8_t pin, uint16_t val); //accepts a 16 bit value and maps it down to the timer for maximum resolution
extern bool SetPinFrequency(int8_t pin, uint32_t frequency);
extern bool SetPinFrequencySafe(int8_t pin, uint32_t frequency); //does not set timers responsible for time keeping functions
extern float GetPinResolution(uint8_t pin); //gets the PWM resolution of a pin in base 2, 0 is returned if the pin is not connected to a timer
#endif /* PWM_H_ */
ATimerDefs.h
/*
Copyright (c) 2012 Sam Knight
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef ATIMERDEFS_H_
#define ATIMERDEFS_H_
#if defined(__AVR_ATmega640__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__)
//bit offset from timer 1 in the register
#define TIMER1_OFFSET 0x00
#define TIMER3_OFFSET 0x10
#define TIMER4_OFFSET 0x20
#define TIMER5_OFFSET 0xA0
//generic register-accessing macros (eliminates some branching)
#define TCCRA_16(tmr_offset) _SFR_MEM8(0x80 + tmr_offset)
#define TCCRB_16(tmr_offset) _SFR_MEM8(0x81 + tmr_offset)
#define TCCRC_16(tmr_offset) _SFR_MEM8(0x82 + tmr_offset)
#define ICR_16(tmr_offset) _SFR_MEM16(0x86 + tmr_offset)
//physical memory locations. Used in pwmWrite()
#define OCR1A_MEM 0x88
#define OCR1B_MEM 0x8A
#define OCR1C_MEM 0x8C
#define OCR3A_MEM 0x98
#define OCR3B_MEM 0x9A
#define OCR3C_MEM 0x9C
#define OCR4A_MEM 0xA8
#define OCR4B_MEM 0xAA
#define OCR4C_MEM 0xAC
#define OCR5A_MEM 0x128
#define OCR5B_MEM 0x12A
#define OCR5C_MEM 0x12C
#define OCR0A_MEM 0x47
#define OCR0B_MEM 0x48
#define OCR2A_MEM 0xB3
#define OCR2B_MEM 0xB4
#define TCCR1A_MEM 0x80
#define TCCR1B_MEM 0x81
#define TCCR1C_MEM 0x82
#define TCCR3A_MEM 0x90
#define TCCR3B_MEM 0x91
#define TCCR4A_MEM 0xA0
#define TCCR4B_MEM 0xA1
#define TCCR4C_MEM 0xA2
#define TCCR5A_MEM 0x120
#define TCCR5B_MEM 0x121
#define TCCR5C_MEM 0x122
#define TCCR0A_MEM 0x44
#define TCCR0B_MEM 0x45
#define TCCR2A_MEM 0xB0
#define TCCR2B_MEM 0xB1
#define ICR1_MEM 0x86
#define ICR3_MEM 0x96
#define ICR4_MEM 0xA6
#define ICR5_MEM 0x126
//8 bit timers
#define TIMER0_OFFSET 0x00
#define TIMER2_OFFSET 0x6C
#define TCCRA_8(tmr_offset) _SFR_MEM8(0x44 + tmr_offset)
#define TCCRB_8(tmr_offset) _SFR_MEM8(0x45 + tmr_offset)
#define OCRA_8(tmr_offset) _SFR_MEM8(0x47 + tmr_offset)
static const uint16_t pscLst[] = { 0, 1, 8, 64, 256, 1024};
static const uint16_t pscLst_alt[] = {0, 1, 8, 32, 64, 128, 256, 1024};
struct TimerData //each instance is 4 bytes
{
uint16_t TimerTopRegLoc: 9;
uint16_t ChannelRegLoc: 9;
uint16_t PinConnectRegLoc: 9;
uint8_t PinConnectBits: 4;
bool Is16Bit: 1;
};
//4 bytes each, 18 elements, 72 Bytes total
const TimerData timer_to_pwm_data[] = {
{0, 0, 0, 0}, //NOT_ON_TIMER
{0, 0, 0, 0}, //TIMER0A disabled when initialized
{OCR0A_MEM, OCR0B_MEM, TCCR0A_MEM, COM0B1, false}, //TIMER0B
{ICR1_MEM, OCR1A_MEM, TCCR1A_MEM, COM1A1, true}, //TIMER1A
{ICR1_MEM, OCR1B_MEM, TCCR1A_MEM, COM1B1, true}, //TIMER1B no C channel on timer 1?
{0, 0, 0, 0, 0}, //TIMER2
{0, 0, 0, 0, 0}, //TIMER2A disabled when initialized
{OCR2A_MEM, OCR2B_MEM, TCCR2A_MEM, COM2B1, false}, //TIMER2B
{ICR3_MEM, OCR3A_MEM, TCCR3A_MEM, COM3A1, true}, //TIMER3A
{ICR3_MEM, OCR3B_MEM, TCCR3A_MEM, COM3B1, true}, //TIMER3B
{ICR3_MEM, OCR3C_MEM, TCCR3A_MEM, COM3C1, true}, //TIMER3C
{ICR4_MEM, OCR4A_MEM, TCCR4A_MEM, COM4A1, true}, //TIMER4A
{ICR4_MEM, OCR4B_MEM, TCCR4A_MEM, COM4B1, true}, //TIMER4B
{ICR4_MEM, OCR4C_MEM, TCCR4A_MEM, COM4C1, true}, //TIMER4C
{0, 0, 0, 0, 0}, //TIMER4D
{ICR5_MEM, OCR5A_MEM, TCCR5A_MEM, COM5A1, true}, //TIMER5A
{ICR5_MEM, OCR5B_MEM, TCCR5A_MEM, COM5B1, true}, //TIMER5B
{ICR5_MEM, OCR5C_MEM, TCCR5A_MEM, COM5C1, true}, //TIMER5C
};
enum prescaler
{
ps_1 = 1,
ps_8 = 2,
ps_64 = 3,
ps_256 = 4,
ps_1024 = 5
};
//certain 8 bit timers read the CSn register differently
enum prescaler_alt
{
psalt_1 = 1,
psalt_8 = 2,
psalt_32 = 3,
psalt_64 = 4,
psalt_128 = 5,
psalt_256 = 6,
psalt_1024 = 7
};
//macros for each timer 'object'
#define Timer0_GetFrequency() GetFrequency_8(TIMER0_OFFSET)
#define Timer0_SetFrequency(x) SetFrequency_8(TIMER0_OFFSET, x)
#define Timer0_GetPrescaler() GetPrescaler_8(TIMER0_OFFSET)
#define Timer0_SetPrescaler(x) SetPrescaler_8(TIMER0_OFFSET, x)
#define Timer0_GetTop() GetTop_8(TIMER0_OFFSET)
#define Timer0_SetTop(x) SetTop_8(TIMER0_OFFSET, x)
#define Timer0_Initialize() Initialize_8(TIMER0_OFFSET)
#define Timer0_GetResolution() GetResolution_8(TIMER0_OFFSET)
#define Timer1_GetFrequency() GetFrequency_16(TIMER1_OFFSET)
#define Timer1_SetFrequency(x) SetFrequency_16(TIMER1_OFFSET, x)
#define Timer1_GetPrescaler() GetPrescaler_16(TIMER1_OFFSET)
#define Timer1_SetPrescaler(x) SetPrescaler_16(TIMER1_OFFSET, x)
#define Timer1_GetTop() GetTop_16(TIMER1_OFFSET)
#define Timer1_SetTop(x) SetTop_16(TIMER1_OFFSET, x)
#define Timer1_Initialize() Initialize_16(TIMER1_OFFSET)
#define Timer1_GetResolution() GetResolution_16(TIMER1_OFFSET)
#define Timer2_GetFrequency() GetFrequency_8(TIMER2_OFFSET)
#define Timer2_SetFrequency(x) SetFrequency_8(TIMER2_OFFSET, x)
#define Timer2_GetPrescaler() GetPrescaler_8(TIMER2_OFFSET)
#define Timer2_SetPrescaler(x) SetPrescalerAlt_8(TIMER2_OFFSET, x)
#define Timer2_GetTop() GetTop_8(TIMER2_OFFSET)
#define Timer2_SetTop(x) SetTop_8(TIMER2_OFFSET, x)
#define Timer2_Initialize() Initialize_8(TIMER2_OFFSET)
#define Timer2_GetResolution() GetResolution_8(TIMER2_OFFSET)
#define Timer3_GetFrequency() GetFrequency_16(TIMER3_OFFSET)
#define Timer3_SetFrequency(x) SetFrequency_16(TIMER3_OFFSET, x)
#define Timer3_GetPrescaler() GetPrescaler_16(TIMER3_OFFSET)
#define Timer3_SetPrescaler(x) SetPrescaler_16(TIMER3_OFFSET, x)
#define Timer3_GetTop() GetTop_16(TIMER3_OFFSET)
#define Timer3_SetTop(x) SetTop_16(TIMER3_OFFSET, x)
#define Timer3_Initialize() Initialize_16(TIMER3_OFFSET)
#define Timer3_GetResolution() GetResolution_16(TIMER3_OFFSET)
#define Timer4_GetFrequency() GetFrequency_16(TIMER4_OFFSET)
#define Timer4_SetFrequency(x) SetFrequency_16(TIMER4_OFFSET, x)
#define Timer4_GetPrescaler() GetPrescaler_16(TIMER4_OFFSET)
#define Timer4_SetPrescaler(x) SetPrescaler_16(TIMER4_OFFSET, x)
#define Timer4_GetTop() GetTop_16(TIMER4_OFFSET)
#define Timer4_SetTop(x) SetTop_16(TIMER4_OFFSET, x)
#define Timer4_Initialize() Initialize_16(TIMER4_OFFSET)
#define Timer4_GetResolution(x) GetResolution_16(TIMER4_OFFSET)
#define Timer5_GetFrequency() GetFrequency_16(TIMER5_OFFSET)
#define Timer5_SetFrequency(x) SetFrequency_16(TIMER5_OFFSET, x)
#define Timer5_GetPrescaler() GetPrescaler_16(TIMER5_OFFSET)
#define Timer5_SetPrescaler(x) SetPrescaler_16(TIMER5_OFFSET, x)
#define Timer5_GetTop() GetTop_16(TIMER5_OFFSET)
#define Timer5_SetTop(x) SetTop_16(TIMER5_OFFSET, x)
#define Timer5_Initialize() Initialize_16(TIMER5_OFFSET)
#define Timer5_GetResolution() GetResolution_16(TIMER5_OFFSET)
#else
#error "ATimerDefs.h only supports ATMega640, ATMega1280, ATMega1281, ATMega2560, and ATMega2561"
#endif
#endif /* ATIMERDEFS_H_ */
ATimerDefs.cpp
/*
Copyright (c) 2012 Sam Knight
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#if defined(__AVR_ATmega640__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__)
#include "wiring_private.h"
#include "../PWM.h"
#define UINT16_MAX 65535
#define UINT8_MAX 255
//--------------------------------------------------------------------------------
// Helper Functions
//--------------------------------------------------------------------------------
static float toBaseTwo(double baseTenNum)
{
return log(baseTenNum + 1)/log(2);
}
//--------------------------------------------------------------------------------
// 16 Bit Timer Functions
//--------------------------------------------------------------------------------
uint32_t GetFrequency_16(const int16_t timerOffset)
{
return (int32_t)(F_CPU/(2 * (int32_t)GetTop_16(timerOffset) * GetPrescaler_16(timerOffset)));
}
bool SetFrequency_16(const int16_t timerOffset, uint32_t f)
{
if(f > 2000000 || f < 1)
return false;
//find the smallest usable multiplier
uint16_t multiplier = (int16_t)(F_CPU / (2 * f * UINT16_MAX));
uint8_t iterate = 0;
while(multiplier > pscLst[iterate++]);
multiplier = pscLst[iterate]; //multiplier holds the clock select value, and iterate holds the corresponding CS flag
//getting the timer top using the new multiplier
uint16_t timerTop = (uint16_t)(F_CPU/(2* f * multiplier));
SetTop_16(timerOffset, timerTop);
SetPrescaler_16(timerOffset, (prescaler)iterate);
return true;
}
uint16_t GetPrescaler_16(const int16_t timerOffset)
{
return pscLst[(TCCRB_16(timerOffset) & 7)];
}
void SetPrescaler_16(const int16_t timerOffset, prescaler psc)
{
TCCRB_16(timerOffset) = (TCCRB_16(timerOffset) & ~7) | (psc & 7);
}
void SetTop_16(const int16_t timerOffset, uint16_t top)
{
ICR_16(timerOffset) = top;
}
uint16_t GetTop_16(const int16_t timerOffset)
{
return ICR_16(timerOffset);
}
void Initialize_16(const int16_t timerOffset)
{
//setting the waveform generation mode
uint8_t wgm = 8;
TCCRA_16(timerOffset) = (TCCRA_16(timerOffset) & B11111100) | (wgm & 3);
TCCRB_16(timerOffset) = (TCCRB_16(timerOffset) & B11100111) | ((wgm & 12) << 1);
SetFrequency_16(timerOffset, 500);
}
float GetResolution_16(const int16_t timerOffset)
{
return toBaseTwo(ICR_16(timerOffset));
}
//--------------------------------------------------------------------------------
// 8 Bit Timer Functions
//--------------------------------------------------------------------------------
uint32_t GetFrequency_8(const int16_t timerOffset)
{
return (uint32_t)(F_CPU/((uint32_t)2 * GetTop_8(timerOffset) * GetPrescaler_8(timerOffset)));
}
bool SetFrequency_8(const int16_t timerOffset, uint32_t f)
{
if(f > 2000000 || f < 31)
return false;
//find the smallest usable multiplier
uint16_t multiplier = (F_CPU / (2 * (uint32_t)f * UINT8_MAX));
uint8_t iterate = 0;
uint16_t timerTop;
do
{
if(TIMER2_OFFSET != timerOffset)
{
while(multiplier > pscLst[++iterate]);
multiplier = pscLst[iterate];
}
else
{
while(multiplier > pscLst_alt[++iterate]);
multiplier = pscLst_alt[iterate];
}
//getting the timer top using the new multiplier
timerTop = (F_CPU/(2* f * (uint32_t)multiplier));
} while (timerTop > UINT8_MAX);
SetTop_8(timerOffset, timerTop);
if(timerOffset != TIMER2_OFFSET)
SetPrescaler_8(timerOffset, (prescaler)iterate);
else
SetPrescalerAlt_8(timerOffset, (prescaler_alt)iterate);
return true;
}
uint16_t GetPrescaler_8(const int16_t timerOffset)
{
if(timerOffset != TIMER2_OFFSET)
return pscLst[(TCCRB_8(timerOffset) & 7)];
else
return pscLst_alt[(TCCRB_8(timerOffset) & 7)];
}
void SetPrescalerAlt_8(const int16_t timerOffset, prescaler_alt psc)
{
TCCRB_8(timerOffset) = (TCCRB_8(timerOffset) & ~7) | (psc & 7);
}
void SetPrescaler_8(const int16_t timerOffset, prescaler psc)
{
TCCRB_8(timerOffset) = (TCCRB_8(timerOffset) & ~7) | (psc & 7);
}
void SetTop_8(const int16_t timerOffset, uint8_t top)
{
OCRA_8(timerOffset) = top;
}
uint8_t GetTop_8(const int16_t timerOffset)
{
return OCRA_8(timerOffset);
}
void Initialize_8(const int16_t timerOffset)
{
//setting the waveform generation mode
uint8_t wgm = 5;
TCCRA_8(timerOffset) = (TCCRA_8(timerOffset) & B11111100) | (wgm & 3);
TCCRB_8(timerOffset) = (TCCRB_8(timerOffset) & B11110111) | ((wgm & 12) << 1);
//disable timer0's interrupt when initialization is called, otherwise handler will eat
//up processor cycles when PWM on timer0 is set to high frequencies. This will effectively disable
//Arduino's time keeping functions, which the user should be aware of before initializing timer0
if(timerOffset == 0)
{
TIMSK0 &= B11111110;
}
SetFrequency_8(timerOffset, 500);
}
float GetResolution_8(const int16_t timerOffset)
{
return toBaseTwo(OCRA_8(timerOffset));
}
//--------------------------------------------------------------------------------
// Timer Independent Functions
//--------------------------------------------------------------------------------
void pwmWrite(uint8_t pin, uint8_t val)
{
pinMode(pin, OUTPUT);
uint32_t tmp = val;
if (val == 0)
digitalWrite(pin, LOW);
else if (val == 255)
digitalWrite(pin, HIGH);
else
{
TimerData td = timer_to_pwm_data[digitalPinToTimer(pin)];
if(td.ChannelRegLoc) //null checking
{
if(td.Is16Bit)
{
sbi(_SFR_MEM8(td.PinConnectRegLoc), td.PinConnectBits);
_SFR_MEM16(td.ChannelRegLoc) = (tmp * _SFR_MEM16(td.TimerTopRegLoc)) / 255;
}
else
{
sbi(_SFR_MEM8(td.PinConnectRegLoc), td.PinConnectBits);
_SFR_MEM8(td.ChannelRegLoc) = (tmp * _SFR_MEM8(td.TimerTopRegLoc)) / 255;
}
}
}
}
//takes a 16 bit value instead of an 8 bit value for maximum resolution
void pwmWriteHR(uint8_t pin, uint16_t val)
{
pinMode(pin, OUTPUT);
uint32_t tmp = val;
if (val == 0)
digitalWrite(pin, LOW);
else if (val == 65535)
digitalWrite(pin, HIGH);
else
{
TimerData td = timer_to_pwm_data[digitalPinToTimer(pin)];
if(td.ChannelRegLoc) //null checking
{
if(td.Is16Bit)
{
sbi(_SFR_MEM8(td.PinConnectRegLoc), td.PinConnectBits);
_SFR_MEM16(td.ChannelRegLoc) = (tmp * _SFR_MEM16(td.TimerTopRegLoc)) / 65535;
}
else
{
sbi(_SFR_MEM8(td.PinConnectRegLoc), td.PinConnectBits);
_SFR_MEM8(td.ChannelRegLoc) = (tmp * _SFR_MEM8(td.TimerTopRegLoc)) / 65535;
}
}
}
}
//Initializes all timer objects, setting them to modes compatible with frequency manipulation. All timers are set to 488 - 500 Hz at the end of initialization.
void InitTimers()
{
Timer0_Initialize();
InitTimersSafe();
}
//initializes all timer objects except for timer 0, which is necessary for the Arduino's time keeping functions
void InitTimersSafe()
{
Timer1_Initialize();
Timer2_Initialize();
Timer3_Initialize();
Timer4_Initialize();
Timer5_Initialize();
}
bool SetPinFrequency(int8_t pin, uint32_t frequency)
{
uint8_t timer = digitalPinToTimer(pin);
if(timer == TIMER0B)
return Timer0_SetFrequency(frequency);
else if(timer == TIMER1A || timer == TIMER1B)
return Timer1_SetFrequency(frequency);
else if(timer == TIMER2B)
return Timer2_SetFrequency(frequency);
else if(timer == TIMER3A || timer == TIMER3B || timer == TIMER3C)
return Timer3_SetFrequency(frequency);
else if(timer == TIMER4A || timer == TIMER4B || timer == TIMER4C)
return Timer4_SetFrequency(frequency);
else if(timer == TIMER5A || timer == TIMER5B || timer == TIMER5C)
return Timer5_SetFrequency(frequency);
else
return false;
}
bool SetPinFrequencySafe(int8_t pin, uint32_t frequency)
{
uint8_t timer = digitalPinToTimer(pin);
if(timer == TIMER1A || timer == TIMER1B)
return Timer1_SetFrequency(frequency);
else if(timer == TIMER2B)
return Timer2_SetFrequency(frequency);
else if(timer == TIMER3A || timer == TIMER3B || timer == TIMER3C)
return Timer3_SetFrequency(frequency);
else if(timer == TIMER4A || timer == TIMER4B || timer == TIMER4C)
return Timer4_SetFrequency(frequency);
else if(timer == TIMER5A || timer == TIMER5B || timer == TIMER5C)
return Timer5_SetFrequency(frequency);
else
return false;
}
float GetPinResolution(uint8_t pin)
{
TimerData td = timer_to_pwm_data[digitalPinToTimer(pin)];
double baseTenRes = 0;
if(td.ChannelRegLoc)
{
//getting a base 10 resolution
td.Is16Bit? (baseTenRes = _SFR_MEM16(td.TimerTopRegLoc)) : (baseTenRes = _SFR_MEM8(td.TimerTopRegLoc));
//change the base and return
return toBaseTwo(baseTenRes);
}
else
{
return 0;
}
}
#endif
BTimerDefs.h
/*
Copyright (c) 2012 Sam Knight
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef BTIMERDEFS_H_
#define BTIMERDEFS_H_
#if defined(__AVR_ATmega48__) || defined(__AVR_ATmega88__) || defined(__AVR_ATmega88P__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__)
//physical memory locations used in pwmWrite()
#define OCR1A_MEM 0x88
#define OCR1B_MEM 0x8A
#define OCR1C_MEM 0x8C
#define OCR0A_MEM 0x47
#define OCR0B_MEM 0x48
#define OCR2A_MEM 0xB3
#define OCR2B_MEM 0xB4
#define TCCR1A_MEM 0x80
#define TCCR1B_MEM 0x81
#define TCCR1C_MEM 0x82
#define TCCR0A_MEM 0x44
#define TCCR0B_MEM 0x45
#define TCCR2A_MEM 0xB0
#define TCCR2B_MEM 0xB1
#define ICR1_MEM 0x86
//8 bit timers
#define TIMER0_OFFSET 0x00
#define TIMER2_OFFSET 0x6C
#define TCCRA_8(tmr_offset) _SFR_MEM8(0x44 + tmr_offset)
#define TCCRB_8(tmr_offset) _SFR_MEM8(0x45 + tmr_offset)
#define OCRA_8(tmr_offset) _SFR_MEM8(0x47 + tmr_offset)
static const uint16_t pscLst[] = { 0, 1, 8, 64, 256, 1024};
static const uint16_t pscLst_alt[] = {0, 1, 8, 32, 64, 128, 256, 1024};
enum prescaler
{
ps_1 = 1,
ps_8 = 2,
ps_64 = 3,
ps_256 = 4,
ps_1024 = 5
};
//certain 8 bit timers read the CSn register differently
enum prescaler_alt
{
psalt_1 = 1,
psalt_8 = 2,
psalt_32 = 3,
psalt_64 = 4,
psalt_128 = 5,
psalt_256 = 6,
psalt_1024 = 7
};
//macros for each timer 'object'
#define Timer0_GetFrequency() GetFrequency_8(TIMER0_OFFSET)
#define Timer0_SetFrequency(x) SetFrequency_8(TIMER0_OFFSET, x)
#define Timer0_GetPrescaler() GetPrescaler_8(TIMER0_OFFSET)
#define Timer0_SetPrescaler(x) SetPrescaler_8(TIMER0_OFFSET, x)
#define Timer0_GetTop() GetTop_8(TIMER0_OFFSET)
#define Timer0_SetTop(x) SetTop_8(TIMER0_OFFSET, x)
#define Timer0_Initialize() Initialize_8(TIMER0_OFFSET)
#define Timer0_GetResolution() GetResolution_8(TIMER0_OFFSET)
#define Timer1_GetFrequency() GetFrequency_16()
#define Timer1_SetFrequency(x) SetFrequency_16(x)
#define Timer1_GetPrescaler() GetPrescaler_16()
#define Timer1_SetPrescaler(x) SetPrescaler_16(x)
#define Timer1_GetTop() GetTop_16()
#define Timer1_SetTop(x) SetTop_16(x)
#define Timer1_Initialize() Initialize_16()
#define Timer1_GetResolution() GetResolution_16()
#define Timer2_GetFrequency() GetFrequency_8(TIMER2_OFFSET)
#define Timer2_SetFrequency(x) SetFrequency_8(TIMER2_OFFSET, x)
#define Timer2_GetPrescaler() GetPrescaler_8(TIMER2_OFFSET)
#define Timer2_SetPrescaler(x) SetPrescalerAlt_8(TIMER2_OFFSET, x)
#define Timer2_GetTop() GetTop_8(TIMER2_OFFSET)
#define Timer2_SetTop(x) SetTop_8(TIMER2_OFFSET, x)
#define Timer2_Initialize() Initialize_8(TIMER2_OFFSET)
#define Timer2_GetResolution() GetResolution_8(TIMER2_OFFSET)
#else
#error "BTimerDefs.h only supports ATMega48, ATMega88, ATMega168, ATMega328"
#endif
#endif /* BTIMERDEFS_H_ */
BTimerDefs.cpp
/*
Copyright (c) 2012 Sam Knight
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#if defined(__AVR_ATmega48__) || defined(__AVR_ATmega88__) || defined(__AVR_ATmega88P__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__)
#include "wiring_private.h"
#include "../PWM.h"
#define UINT16_MAX 65535
#define UINT8_MAX 255
//--------------------------------------------------------------------------------
// Helper Functions
//--------------------------------------------------------------------------------
static float toBaseTwo(double baseTenNum)
{
return log(baseTenNum + 1)/log(2);
}
//--------------------------------------------------------------------------------
// 16 Bit Timer Functions
//--------------------------------------------------------------------------------
uint32_t GetFrequency_16()
{
return (int32_t)(F_CPU/(2 * (int32_t)GetTop_16() * GetPrescaler_16()));
}
bool SetFrequency_16(uint32_t f)
{
if(f > 2000000 || f < 1)
return false;
//find the smallest usable multiplier
uint16_t multiplier = (int16_t)(F_CPU / (2 * f * UINT16_MAX));
uint8_t iterate = 0;
while(multiplier > pscLst[iterate++]);
multiplier = pscLst[iterate]; //multiplier holds the clock select value, and iterate holds the corresponding CS flag
//getting the timer top using the new multiplier
uint16_t timerTop = (uint16_t)(F_CPU/(2* f * multiplier));
SetTop_16(timerTop);
SetPrescaler_16((prescaler)iterate);
return true;
}
uint16_t GetPrescaler_16()
{
return pscLst[(TCCR1B & 7)];
}
void SetPrescaler_16(prescaler psc)
{
TCCR1B = (TCCR1B & ~7) | (psc & 7);
}
void SetTop_16(uint16_t top)
{
ICR1 = top;
}
uint16_t GetTop_16()
{
return ICR1;
}
void Initialize_16()
{
//setting the waveform generation mode
uint8_t wgm = 8;
TCCR1A = (TCCR1A & B11111100) | (wgm & 3);
TCCR1B = (TCCR1B & B11100111) | ((wgm & 12) << 1);
SetFrequency_16(500);
}
float GetResolution_16()
{
return toBaseTwo(ICR1);
}
//--------------------------------------------------------------------------------
// 8 Bit Timer Functions
//--------------------------------------------------------------------------------
uint32_t GetFrequency_8(const int16_t timerOffset)
{
return (uint32_t)(F_CPU/((uint32_t)2 * GetTop_8(timerOffset) * GetPrescaler_8(timerOffset)));
}
bool SetFrequency_8(const int16_t timerOffset, uint32_t f)
{
if(f > 2000000 || f < 31)
return false;
//find the smallest usable multiplier
uint16_t multiplier = (F_CPU / (2 * (uint32_t)f * UINT8_MAX));
uint8_t iterate = 0;
uint16_t timerTop;
do
{
if(TIMER2_OFFSET != timerOffset)
{
while(multiplier > pscLst[++iterate]);
multiplier = pscLst[iterate];
}
else
{
while(multiplier > pscLst_alt[++iterate]);
multiplier = pscLst_alt[iterate];
}
//getting the timer top using the new multiplier
timerTop = (F_CPU/(2* f * (uint32_t)multiplier));
} while (timerTop > UINT8_MAX);
SetTop_8(timerOffset, timerTop);
if(timerOffset != TIMER2_OFFSET)
SetPrescaler_8(timerOffset, (prescaler)iterate);
else
SetPrescalerAlt_8(timerOffset, (prescaler_alt)iterate);
return true;
}
uint16_t GetPrescaler_8(const int16_t timerOffset)
{
if(timerOffset != TIMER2_OFFSET)
return pscLst[(TCCRB_8(timerOffset) & 7)];
else
return pscLst_alt[(TCCRB_8(timerOffset) & 7)];
}
void SetPrescaler_8(const int16_t timerOffset, prescaler psc)
{
TCCRB_8(timerOffset) = (TCCRB_8(timerOffset) & ~7) | (psc & 7);
}
void SetPrescalerAlt_8(const int16_t timerOffset, prescaler_alt psc)
{
TCCRB_8(timerOffset) = (TCCRB_8(timerOffset) & ~7) | (psc & 7);
}
void SetTop_8(const int16_t timerOffset, uint8_t top)
{
OCRA_8(timerOffset) = top;
}
uint8_t GetTop_8(const int16_t timerOffset)
{
return OCRA_8(timerOffset);
}
void Initialize_8(const int16_t timerOffset)
{
//setting the waveform generation mode
uint8_t wgm = 5;
TCCRA_8(timerOffset) = (TCCRA_8(timerOffset) & B11111100) | (wgm & 3);
TCCRB_8(timerOffset) = (TCCRB_8(timerOffset) & B11110111) | ((wgm & 12) << 1);
//disable timer0's interrupt when initialization is called, otherwise handler will eat
//up processor cycles when PWM on timer0 is set to high frequencies. This will effectively disable
//Arduino's time keeping functions, which the user should be aware of before initializing timer0
if(timerOffset == 0)
{
TIMSK0 &= B11111110;
}
SetFrequency_8(timerOffset, 500);
}
float GetResolution_8(const int16_t timerOffset)
{
return toBaseTwo(OCRA_8(timerOffset));
}
//--------------------------------------------------------------------------------
// Timer Independent Functions
//--------------------------------------------------------------------------------
void pwmWrite(uint8_t pin, uint8_t val)
{
pinMode(pin, OUTPUT);
//casting "val" to be larger so that the final value (which is the partially
//the result of multiplying two potentially high value int16s) will not truncate
uint32_t tmp = val;
if (val == 0)
digitalWrite(pin, LOW);
else if (val == 255)
digitalWrite(pin, HIGH);
else
{
uint16_t regLoc16 = 0;
uint16_t regLoc8 = 0;
uint16_t top;
switch(digitalPinToTimer(pin))
{
case TIMER0B:
sbi(TCCR0A, COM0B1);
regLoc8 = OCR0B_MEM;
top = Timer0_GetTop();
break;
case TIMER1A:
sbi(TCCR1A, COM1A1);
regLoc16 = OCR1A_MEM;
top = Timer1_GetTop();
break;
case TIMER1B:
sbi(TCCR1A, COM1B1);
regLoc16 = OCR1B_MEM;
top = Timer1_GetTop();
break;
case TIMER2B:
sbi(TCCR2A, COM2B1);
regLoc8 = OCR2B_MEM;
top = Timer2_GetTop();
break;
case NOT_ON_TIMER:
default:
if (val < 128)
digitalWrite(pin, LOW);
else
digitalWrite(pin, HIGH);
return;
}
if(regLoc16)
_SFR_MEM16(regLoc16) = (tmp*top)/255;
else
_SFR_MEM8(regLoc8) = (tmp*top)/255;
}
}
//takes a 16 bit value instead of an 8 bit value for maximum resolution
void pwmWriteHR(uint8_t pin, uint16_t val)
{
pinMode(pin, OUTPUT);
uint32_t tmp = val;
if (val == 0)
digitalWrite(pin, LOW);
else if (val == 65535)
digitalWrite(pin, HIGH);
else
{
uint16_t regLoc16 = 0;
uint16_t regLoc8 = 0;
uint16_t top;
switch(digitalPinToTimer(pin))
{
case TIMER0B:
sbi(TCCR0A, COM0B1);
regLoc8 = OCR0B_MEM;
top = Timer0_GetTop();
break;
case TIMER1A:
sbi(TCCR1A, COM1A1);
regLoc16 = OCR1A_MEM;
top = Timer1_GetTop();
break;
case TIMER1B:
sbi(TCCR1A, COM1B1);
regLoc16 = OCR1B_MEM;
top = Timer1_GetTop();
break;
case TIMER2B:
sbi(TCCR2A, COM2B1);
regLoc8 = OCR2B_MEM;
top = Timer2_GetTop();
break;
case NOT_ON_TIMER:
default:
if (val < 128)
digitalWrite(pin, LOW);
else
digitalWrite(pin, HIGH);
return;
}
if(regLoc16)
_SFR_MEM16(regLoc16) = (tmp*top)/65535;
else
_SFR_MEM8(regLoc8) = (tmp*top)/65535;
}
}
void InitTimers()
{
Timer0_Initialize();
Timer1_Initialize();
Timer2_Initialize();
}
void InitTimersSafe()
{
Timer1_Initialize();
Timer2_Initialize();
}
extern bool SetPinFrequency( int8_t pin, uint32_t frequency )
{
uint8_t timer = digitalPinToTimer(pin);
if(timer == TIMER0B)
return Timer0_SetFrequency(frequency);
else if(timer == TIMER1A || timer == TIMER1B)
return Timer1_SetFrequency(frequency);
else if(timer == TIMER2B)
return Timer2_SetFrequency(frequency);
else
return false;
}
bool SetPinFrequencySafe(int8_t pin, uint32_t frequency)
{
uint8_t timer = digitalPinToTimer(pin);
if(timer == TIMER1A || timer == TIMER1B)
return Timer1_SetFrequency(frequency);
else if(timer == TIMER2B)
return Timer2_SetFrequency(frequency);
else
return false;
}
float GetPinResolution(uint8_t pin)
{
uint8_t timer = digitalPinToTimer(pin);
uint16_t top;
switch(timer)
{
case TIMER0B:
top = Timer0_GetTop();
break;
case TIMER1A:
top = Timer1_GetTop();
break;
case TIMER1B:
top = Timer1_GetTop();
break;
case TIMER2B:
top = Timer2_GetTop();
default:
return 0;
}
return toBaseTwo(top);
}
#endif