2014-12-19 08:45:50 -08:00
|
|
|
// a Tiny optimized Software PWM Manager (all pins must be part of the same port)
|
|
|
|
// Only resources RAM/Program Memory of used pins are declared in the code at compilation time.
|
|
|
|
// based largely on Atmel's AVR136: Low-Jitter Multi-Channel Software PWM Application Note:
|
|
|
|
// http://www.atmel.com/dyn/resources/prod_documents/doc8020.pdf
|
2015-01-14 18:08:45 -08:00
|
|
|
// RC Navy 2013-2015
|
2014-12-19 08:45:50 -08:00
|
|
|
// http://p.loussouarn.free.fr
|
2015-01-14 18:08:45 -08:00
|
|
|
// 11/01/2015: Multi port support added for ATtiny167
|
2014-12-19 08:45:50 -08:00
|
|
|
|
|
|
|
#include <TinySoftPwm.h>
|
|
|
|
|
2015-01-14 18:08:45 -08:00
|
|
|
#if (TINY_SOFT_PWM_CH_MAX == 0)
|
|
|
|
#error At least one PWM pin shall be declared in TinySoftPwm.h
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(TINY_SOFT_PWM_DDR1) && defined(TINY_SOFT_PWM_DDR0)
|
|
|
|
#define TINY_SOFT_PWM_DECLARE_PIN(Pin) do{ \
|
|
|
|
if(digitalPinToPortIdx(Pin)) \
|
|
|
|
{ \
|
|
|
|
TINY_SOFT_PWM_DDR1 |= (1 << digitalPinToPortBit(Pin)); \
|
|
|
|
Port1_PwmMask |= (1 << digitalPinToPortBit(Pin)); \
|
|
|
|
} \
|
|
|
|
else \
|
|
|
|
{ \
|
|
|
|
TINY_SOFT_PWM_DDR0 |= (1 << digitalPinToPortBit(Pin)); \
|
|
|
|
Port0_PwmMask |= (1 << digitalPinToPortBit(Pin)); \
|
|
|
|
} \
|
|
|
|
}while(0)
|
|
|
|
|
|
|
|
#define TINY_SOFT_PWM_CLEAR_PIN(RamIdx) GET_PWM_PIN_PORT(RamIdx) \
|
|
|
|
? (Port1_PwmTo1 &= GET_PWM_INV_MSK(RamIdx)) \
|
|
|
|
: (Port0_PwmTo1 &= GET_PWM_INV_MSK(RamIdx))
|
|
|
|
#else
|
|
|
|
#if defined(TINY_SOFT_PWM_DDR1)
|
|
|
|
#define TINY_SOFT_PWM_DECLARE_PIN(Pin) TINY_SOFT_PWM_DDR1 |= (1 << digitalPinToPortBit(Pin)); Port1_PwmMask |= (1 << digitalPinToPortBit(Pin))
|
|
|
|
#define TINY_SOFT_PWM_CLEAR_PIN(RamIdx) Port1_PwmTo1 &= GET_PWM_INV_MSK(RamIdx)
|
|
|
|
#else
|
|
|
|
#define TINY_SOFT_PWM_DECLARE_PIN(Pin) TINY_SOFT_PWM_DDR0 |= (1 << digitalPinToPortBit(Pin)); Port0_PwmMask |= (1 << digitalPinToPortBit(Pin))
|
|
|
|
#define TINY_SOFT_PWM_CLEAR_PIN(RamIdx) Port0_PwmTo1 &= GET_PWM_INV_MSK(RamIdx)
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define GET_PWM_PIN_ID(RamIdx) ((uint8_t)pgm_read_byte(&PwmPin[(RamIdx)].Id))
|
|
|
|
#define GET_PWM_PIN_PORT(RamIdx) ((uint8_t)pgm_read_byte(&PwmPin[(RamIdx)].Port))
|
|
|
|
#define GET_PWM_INV_MSK(RamIdx) ((uint8_t)pgm_read_byte(&PwmPin[(RamIdx)].InvMsk))
|
2014-12-19 08:45:50 -08:00
|
|
|
|
|
|
|
|
2015-01-14 18:08:45 -08:00
|
|
|
typedef struct {
|
|
|
|
uint8_t Id;
|
|
|
|
uint8_t Port;
|
|
|
|
uint8_t InvMsk;
|
|
|
|
}SoftPwmPinSt_t;
|
2014-12-19 08:45:50 -08:00
|
|
|
|
2015-01-14 18:08:45 -08:00
|
|
|
const SoftPwmPinSt_t PwmPin[] PROGMEM ={
|
|
|
|
#if (TINY_SOFT_PWM_USES_PIN0 == 1)
|
|
|
|
{0, digitalPinToPortIdx(0), ~(1 << digitalPinToPortBit(0))},
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_USES_PIN1 == 1)
|
|
|
|
{1, digitalPinToPortIdx(1), ~(1 << digitalPinToPortBit(1))},
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_USES_PIN2 == 1)
|
|
|
|
{2, digitalPinToPortIdx(2), ~(1 << digitalPinToPortBit(2))},
|
2014-12-19 08:45:50 -08:00
|
|
|
#endif
|
2015-01-14 18:08:45 -08:00
|
|
|
#if (TINY_SOFT_PWM_USES_PIN3 == 1)
|
|
|
|
{3, digitalPinToPortIdx(3), ~(1 << digitalPinToPortBit(3))},
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_USES_PIN4 == 1)
|
|
|
|
{4, digitalPinToPortIdx(4), ~(1 << digitalPinToPortBit(4))},
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_USES_PIN5 == 1)
|
|
|
|
{5, digitalPinToPortIdx(5), ~(1 << digitalPinToPortBit(5))},
|
|
|
|
#endif
|
|
|
|
#if defined (__AVR_ATtiny167__)
|
|
|
|
#if (TINY_SOFT_PWM_USES_PIN6 == 1)
|
|
|
|
{6, digitalPinToPortIdx(6), ~(1 << digitalPinToPortBit(6))},
|
2014-12-19 08:45:50 -08:00
|
|
|
#endif
|
2015-01-14 18:08:45 -08:00
|
|
|
#if (TINY_SOFT_PWM_USES_PIN7 == 1)
|
|
|
|
{7, digitalPinToPortIdx(7), ~(1 << digitalPinToPortBit(7))},
|
2014-12-19 08:45:50 -08:00
|
|
|
#endif
|
2015-01-14 18:08:45 -08:00
|
|
|
#if (TINY_SOFT_PWM_USES_PIN8 == 1)
|
|
|
|
{8, digitalPinToPortIdx(8), ~(1 << digitalPinToPortBit(8))},
|
2014-12-19 08:45:50 -08:00
|
|
|
#endif
|
2015-01-14 18:08:45 -08:00
|
|
|
#if (TINY_SOFT_PWM_USES_PIN9 == 1)
|
|
|
|
{9, digitalPinToPortIdx(9), ~(1 << digitalPinToPortBit(9))},
|
2014-12-19 08:45:50 -08:00
|
|
|
#endif
|
2015-01-14 18:08:45 -08:00
|
|
|
#if (TINY_SOFT_PWM_USES_PIN10 == 1)
|
|
|
|
{10, digitalPinToPortIdx(10), ~(1 << digitalPinToPortBit(10))},
|
2014-12-19 08:45:50 -08:00
|
|
|
#endif
|
2015-01-14 18:08:45 -08:00
|
|
|
#if (TINY_SOFT_PWM_USES_PIN11 == 1)
|
|
|
|
{11, digitalPinToPortIdx(11), ~(1 << digitalPinToPortBit(11))},
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_USES_PIN12 == 1)
|
|
|
|
{12, digitalPinToPortIdx(12), ~(1 << digitalPinToPortBit(12))},
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
2014-12-19 08:45:50 -08:00
|
|
|
static uint8_t Compare[TINY_SOFT_PWM_CH_MAX];
|
|
|
|
volatile uint8_t PwmOrder[TINY_SOFT_PWM_CH_MAX];
|
2015-01-14 18:08:45 -08:00
|
|
|
#ifdef TINY_SOFT_PWM_PORT0
|
|
|
|
static uint8_t Port0_PwmMask = 0;
|
|
|
|
volatile uint8_t Port0_PwmTo1 = 0x00;
|
|
|
|
volatile uint8_t Port0_PwmTo0 = 0xFF;
|
|
|
|
#endif
|
|
|
|
#ifdef TINY_SOFT_PWM_PORT1
|
|
|
|
static uint8_t Port1_PwmMask = 0;
|
|
|
|
volatile uint8_t Port1_PwmTo1 = 0x00;
|
|
|
|
volatile uint8_t Port1_PwmTo0 = 0xFF;
|
|
|
|
#endif
|
|
|
|
static uint8_t _TickMax = 255;
|
2014-12-19 08:45:50 -08:00
|
|
|
|
|
|
|
static uint8_t PwmToPwmMax(uint8_t Pwm);
|
|
|
|
|
|
|
|
void TinySoftPwm_begin(uint8_t TickMax, uint8_t PwmInit)
|
|
|
|
{
|
|
|
|
uint8_t oldSREG = SREG;
|
|
|
|
cli();
|
|
|
|
// set the direction of the used ports and update PortPwmMask
|
2015-01-14 18:08:45 -08:00
|
|
|
#if (TINY_SOFT_PWM_USES_PIN0 == 1)
|
|
|
|
TINY_SOFT_PWM_DECLARE_PIN(0);
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_USES_PIN1 == 1)
|
|
|
|
TINY_SOFT_PWM_DECLARE_PIN(1);
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_USES_PIN2 == 1)
|
|
|
|
TINY_SOFT_PWM_DECLARE_PIN(2);
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_USES_PIN3 == 1)
|
|
|
|
TINY_SOFT_PWM_DECLARE_PIN(3);
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_USES_PIN4 == 1)
|
|
|
|
TINY_SOFT_PWM_DECLARE_PIN(4);
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_USES_PIN5 == 1)
|
|
|
|
TINY_SOFT_PWM_DECLARE_PIN(5);
|
2014-12-19 08:45:50 -08:00
|
|
|
#endif
|
2015-01-14 18:08:45 -08:00
|
|
|
#if (TINY_SOFT_PWM_USES_PIN6 == 1)
|
|
|
|
TINY_SOFT_PWM_DECLARE_PIN(6);
|
2014-12-19 08:45:50 -08:00
|
|
|
#endif
|
2015-01-14 18:08:45 -08:00
|
|
|
#if (TINY_SOFT_PWM_USES_PIN7 == 1)
|
|
|
|
TINY_SOFT_PWM_DECLARE_PIN(7);
|
2014-12-19 08:45:50 -08:00
|
|
|
#endif
|
2015-01-14 18:08:45 -08:00
|
|
|
#if (TINY_SOFT_PWM_USES_PIN8 == 1)
|
|
|
|
TINY_SOFT_PWM_DECLARE_PIN(8);
|
2014-12-19 08:45:50 -08:00
|
|
|
#endif
|
2015-01-14 18:08:45 -08:00
|
|
|
#if (TINY_SOFT_PWM_USES_PIN9 == 1)
|
|
|
|
TINY_SOFT_PWM_DECLARE_PIN(9);
|
2014-12-19 08:45:50 -08:00
|
|
|
#endif
|
2015-01-14 18:08:45 -08:00
|
|
|
#if (TINY_SOFT_PWM_USES_PIN10 == 1)
|
|
|
|
TINY_SOFT_PWM_DECLARE_PIN(10);
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_USES_PIN11 == 1)
|
|
|
|
TINY_SOFT_PWM_DECLARE_PIN(11);
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_USES_PIN12 == 1)
|
|
|
|
TINY_SOFT_PWM_DECLARE_PIN(12);
|
|
|
|
#endif
|
|
|
|
_TickMax = TickMax;
|
|
|
|
#ifdef TINY_SOFT_PWM_PORT0
|
|
|
|
Port0_PwmTo1 = Port0_PwmMask;
|
|
|
|
#endif
|
|
|
|
#ifdef TINY_SOFT_PWM_PORT1
|
|
|
|
Port1_PwmTo1 = Port1_PwmMask;
|
2014-12-19 08:45:50 -08:00
|
|
|
#endif
|
|
|
|
// initialise all channels
|
2015-01-14 18:08:45 -08:00
|
|
|
for(uint8_t i = 0; i < TINY_SOFT_PWM_CH_MAX; i++)
|
2014-12-19 08:45:50 -08:00
|
|
|
{
|
2015-01-14 18:08:45 -08:00
|
|
|
Compare[i] = PwmToPwmMax(PwmInit); // set default PWM values
|
2014-12-19 08:45:50 -08:00
|
|
|
PwmOrder[i] = Compare[i]; // set default PWM values
|
|
|
|
}
|
|
|
|
SREG = oldSREG;
|
|
|
|
}
|
|
|
|
|
2015-01-14 18:08:45 -08:00
|
|
|
void TinySoftPwm_analogWrite(uint8_t Pin, uint8_t Pwm)
|
2014-12-19 08:45:50 -08:00
|
|
|
{
|
|
|
|
uint8_t RamIdx;
|
|
|
|
|
2015-01-14 18:08:45 -08:00
|
|
|
for(RamIdx = 0; RamIdx < TINY_SOFT_PWM_CH_MAX; RamIdx++)
|
2014-12-19 08:45:50 -08:00
|
|
|
{
|
2015-01-14 18:08:45 -08:00
|
|
|
if(GET_PWM_PIN_ID(RamIdx) == Pin) break;
|
2014-12-19 08:45:50 -08:00
|
|
|
}
|
2015-01-14 18:08:45 -08:00
|
|
|
if(RamIdx < TINY_SOFT_PWM_CH_MAX)
|
2014-12-19 08:45:50 -08:00
|
|
|
{
|
|
|
|
PwmOrder[RamIdx] = PwmToPwmMax(Pwm);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t PwmToPwmMax(uint8_t Pwm)
|
|
|
|
{
|
2015-01-14 18:08:45 -08:00
|
|
|
uint16_t Pwm16;
|
|
|
|
Pwm16 = map(Pwm, 0, 255, 0, _TickMax);
|
|
|
|
Pwm16 = constrain(Pwm16, 0, _TickMax);
|
|
|
|
return((uint8_t)Pwm16);
|
2014-12-19 08:45:50 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void TinySoftPwm_process(void)
|
|
|
|
{
|
|
|
|
static uint8_t OvfCount=0xFF;
|
|
|
|
|
2015-01-14 18:08:45 -08:00
|
|
|
#ifdef TINY_SOFT_PWM_PORT0
|
|
|
|
Port0_PwmTo0 = (~Port0_PwmTo1) ^ Port0_PwmMask;
|
|
|
|
TINY_SOFT_PWM_PORT0 |= Port0_PwmTo1; // update ONLY used outputs to 1 without disturbing the others
|
|
|
|
TINY_SOFT_PWM_PORT0 &= Port0_PwmTo0; // update ONLY used outputs to 0 without disturbing the others
|
|
|
|
#endif
|
|
|
|
#ifdef TINY_SOFT_PWM_PORT1
|
|
|
|
Port1_PwmTo0 = (~Port1_PwmTo1) ^ Port1_PwmMask;
|
|
|
|
TINY_SOFT_PWM_PORT1 |= Port1_PwmTo1; // update ONLY used outputs to 1 without disturbing the others
|
|
|
|
TINY_SOFT_PWM_PORT1 &= Port1_PwmTo0; // update ONLY used outputs to 0 without disturbing the others
|
|
|
|
#endif
|
2014-12-19 08:45:50 -08:00
|
|
|
if(++OvfCount == _TickMax)
|
|
|
|
{ // increment modulo 256 counter and update
|
|
|
|
// the compare values only when counter = 0.
|
|
|
|
OvfCount=0;
|
|
|
|
#if (TINY_SOFT_PWM_CH_MAX >= 1)
|
|
|
|
Compare[0] = PwmOrder[0]; // verbose code for speed
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_CH_MAX >= 2)
|
|
|
|
Compare[1] = PwmOrder[1];
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_CH_MAX >= 3)
|
|
|
|
Compare[2] = PwmOrder[2];
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_CH_MAX >= 4)
|
|
|
|
Compare[3] = PwmOrder[3];
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_CH_MAX >= 5)
|
|
|
|
Compare[4] = PwmOrder[4];
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_CH_MAX >= 6)
|
|
|
|
Compare[5] = PwmOrder[5];
|
|
|
|
#endif
|
2015-01-14 18:08:45 -08:00
|
|
|
#if (TINY_SOFT_PWM_CH_MAX >= 7)
|
|
|
|
Compare[6] = PwmOrder[6]; // verbose code for speed
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_CH_MAX >= 8)
|
|
|
|
Compare[7] = PwmOrder[7];
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_CH_MAX >= 9)
|
|
|
|
Compare[8] = PwmOrder[8];
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_CH_MAX >= 10)
|
|
|
|
Compare[9] = PwmOrder[9];
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_CH_MAX >= 11)
|
|
|
|
Compare[10] = PwmOrder[10];
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_CH_MAX >= 12)
|
|
|
|
Compare[11] = PwmOrder[11];
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_CH_MAX >= 13)
|
|
|
|
Compare[12] = PwmOrder[12];
|
|
|
|
#endif
|
|
|
|
#ifdef TINY_SOFT_PWM_PORT0
|
|
|
|
Port0_PwmTo1 = Port0_PwmMask; // set all port used pins high
|
|
|
|
#endif
|
|
|
|
#ifdef TINY_SOFT_PWM_PORT1
|
|
|
|
Port1_PwmTo1 = Port1_PwmMask; // set all port used pins high
|
|
|
|
#endif
|
2014-12-19 08:45:50 -08:00
|
|
|
}
|
|
|
|
// clear port pin on compare match (executed on next interrupt)
|
|
|
|
#if (TINY_SOFT_PWM_CH_MAX >= 1)
|
|
|
|
if(Compare[0] == OvfCount) TINY_SOFT_PWM_CLEAR_PIN(0);
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_CH_MAX >= 2)
|
|
|
|
if(Compare[1] == OvfCount) TINY_SOFT_PWM_CLEAR_PIN(1);
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_CH_MAX >= 3)
|
|
|
|
if(Compare[2] == OvfCount) TINY_SOFT_PWM_CLEAR_PIN(2);
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_CH_MAX >= 4)
|
|
|
|
if(Compare[3] == OvfCount) TINY_SOFT_PWM_CLEAR_PIN(3);
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_CH_MAX >= 5)
|
|
|
|
if(Compare[4] == OvfCount) TINY_SOFT_PWM_CLEAR_PIN(4);
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_CH_MAX >= 6)
|
|
|
|
if(Compare[5] == OvfCount) TINY_SOFT_PWM_CLEAR_PIN(5);
|
|
|
|
#endif
|
2015-01-14 18:08:45 -08:00
|
|
|
#if (TINY_SOFT_PWM_CH_MAX >= 7)
|
|
|
|
if(Compare[6] == OvfCount) TINY_SOFT_PWM_CLEAR_PIN(6);
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_CH_MAX >= 8)
|
|
|
|
if(Compare[7] == OvfCount) TINY_SOFT_PWM_CLEAR_PIN(7);
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_CH_MAX >= 9)
|
|
|
|
if(Compare[8] == OvfCount) TINY_SOFT_PWM_CLEAR_PIN(8);
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_CH_MAX >= 10)
|
|
|
|
if(Compare[9] == OvfCount) TINY_SOFT_PWM_CLEAR_PIN(9);
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_CH_MAX >= 11)
|
|
|
|
if(Compare[10] == OvfCount) TINY_SOFT_PWM_CLEAR_PIN(10);
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_CH_MAX >= 12)
|
|
|
|
if(Compare[11] == OvfCount) TINY_SOFT_PWM_CLEAR_PIN(11);
|
|
|
|
#endif
|
|
|
|
#if (TINY_SOFT_PWM_CH_MAX >= 13)
|
|
|
|
if(Compare[12] == OvfCount) TINY_SOFT_PWM_CLEAR_PIN(12);
|
|
|
|
#endif
|
2014-12-19 08:45:50 -08:00
|
|
|
}
|