|
|
@ -0,0 +1,354 @@ |
|
|
|
/*************************************************************************** |
|
|
|
* Copyright (C) 01/2019 by Olaf Rempel * |
|
|
|
* razzor@kopf-tisch.de * |
|
|
|
* * |
|
|
|
* This program is free software; you can redistribute it and/or modify * |
|
|
|
* it under the terms of the GNU General Public License as published by * |
|
|
|
* the Free Software Foundation; version 2 of the License, * |
|
|
|
* * |
|
|
|
* This program is distributed in the hope that it will be useful, * |
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of * |
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * |
|
|
|
* GNU General Public License for more details. * |
|
|
|
* * |
|
|
|
* You should have received a copy of the GNU General Public License * |
|
|
|
* along with this program; if not, write to the * |
|
|
|
* Free Software Foundation, Inc., * |
|
|
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * |
|
|
|
***************************************************************************/ |
|
|
|
|
|
|
|
#include <avr/interrupt.h> |
|
|
|
#include <avr/pgmspace.h> |
|
|
|
|
|
|
|
#include "pwm.h" |
|
|
|
#include "timer.h" |
|
|
|
|
|
|
|
|
|
|
|
/* *********************************************************************** */ |
|
|
|
|
|
|
|
#define PWM_TIM_16BIT 1 |
|
|
|
|
|
|
|
#define PWM_TIM_INIT() { \ |
|
|
|
/* enable output for OC1A, OC1B, OC3A */ \ |
|
|
|
DDRB |= (1<<PORTB5) | (1<<PORTB6); \ |
|
|
|
DDRC |= (1<<PORTC6); \ |
|
|
|
/* reset and hold prescaler */ \ |
|
|
|
GTCCR = (1<<TSM) | (1<<PSRSYNC); \ |
|
|
|
/* FastModePWM, ICRn is TOP */ \ |
|
|
|
TCCR1A = (1<<WGM11); \ |
|
|
|
TCCR1B = (1<<WGM13) | (1<<WGM12); \ |
|
|
|
ICR1 = 0xFFFF; \ |
|
|
|
TCNT1 = 0x0000; \ |
|
|
|
TCCR3A = (1<<WGM31); \ |
|
|
|
TCCR3B = (1<<WGM33) | (1<<WGM32); \ |
|
|
|
ICR3 = 0xFFFF; \ |
|
|
|
TCNT3 = 0x8000; \ |
|
|
|
/* release prescaler */ \ |
|
|
|
GTCCR = (1<<PSRSYNC); \ |
|
|
|
} |
|
|
|
|
|
|
|
#define PWM_TIM1_ENABLE() { TCCR1B |= (1<<CS10); } |
|
|
|
#define PWM_TIM1_DISABLE() { TCCR1B &= ~(1<<CS10); } |
|
|
|
#define PWM_TIM1_RUNNING() (TCCR1B & ((1<<CS10) | (1<<CS11) | (1<<CS12))) |
|
|
|
#define PWM_TIM1_CHECK() (TCCR1A & ((1<<COM1A1) | (1<<COM1B1))) |
|
|
|
|
|
|
|
#define PWM_TIM3_ENABLE() { TCCR3B |= (1<<CS30); } |
|
|
|
#define PWM_TIM3_DISABLE() { TCCR3B &= ~(1<<CS30); } |
|
|
|
#define PWM_TIM3_RUNNING() (TCCR3B & ((1<<CS30) | (1<<CS31) | (1<<CS32))) |
|
|
|
#define PWM_TIM3_CHECK() (TCCR3A & ((1<<COM3A1) | (1<<COM3B1))) |
|
|
|
|
|
|
|
#define PWM_TIMER_FADE_STEP_MS 10 |
|
|
|
|
|
|
|
#define PWM_CH0_PORT PORTB |
|
|
|
#define PWM_CH0_NUM 5 |
|
|
|
#define PWM_CH0_OFF() { PWM_CH0_PORT &= ~(1<<PWM_CH0_NUM); TCCR1A &= ~(1<<COM1A1); } |
|
|
|
#define PWM_CH0_ON() { PWM_CH0_PORT |= (1<<PWM_CH0_NUM); TCCR1A &= ~(1<<COM1A1); } |
|
|
|
#define PWM_CH0_PWM(x) { PWM_CH0_PORT &= ~(1<<PWM_CH0_NUM); TCCR1A |= (1<<COM1A1); OCR1A = x; } |
|
|
|
|
|
|
|
#define PWM_CH1_PORT PORTB |
|
|
|
#define PWM_CH1_NUM 6 |
|
|
|
#define PWM_CH1_OFF() { PWM_CH1_PORT &= ~(1<<PWM_CH1_NUM); TCCR1A &= ~(1<<COM1B1); } |
|
|
|
#define PWM_CH1_ON() { PWM_CH1_PORT |= (1<<PWM_CH1_NUM); TCCR1A &= ~(1<<COM1B1); } |
|
|
|
#define PWM_CH1_PWM(x) { PWM_CH1_PORT &= ~(1<<PWM_CH1_NUM); TCCR1A |= (1<<COM1B1); OCR1B = x; } |
|
|
|
|
|
|
|
#define PWM_CH2_PORT PORTC |
|
|
|
#define PWM_CH2_NUM 6 |
|
|
|
#define PWM_CH2_OFF() { PWM_CH2_PORT &= ~(1<<PWM_CH2_NUM); TCCR3A &= ~(1<<COM3A1); } |
|
|
|
#define PWM_CH2_ON() { PWM_CH2_PORT |= (1<<PWM_CH2_NUM); TCCR3A &= ~(1<<COM3A1); } |
|
|
|
#define PWM_CH2_PWM(x) { PWM_CH2_PORT &= ~(1<<PWM_CH2_NUM); TCCR3A |= (1<<COM3A1); OCR3A = x; } |
|
|
|
|
|
|
|
#if (PWM_TIM_16BIT) |
|
|
|
#define PWM_TABLE_SIZE 128 |
|
|
|
#define PWM_TABLE_GET(x) pgm_read_word(&pwmtable16[x]) |
|
|
|
const uint16_t pwmtable16[PWM_TABLE_SIZE] PROGMEM = |
|
|
|
{ |
|
|
|
0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, |
|
|
|
6, 6, 7, 8, 9, 9, 10, 11, 12, 14, 15, 16, 18, 20, 22, 24, 26, |
|
|
|
28, 31, 34, 37, 40, 44, 48, 53, 58, 63, 69, 75, 82, 90, 98, |
|
|
|
107, 116, 127, 139, 151, 165, 180, 196, 214, 234, 255, 278, |
|
|
|
303, 331, 361, 394, 430, 469, 511, 557, 608, 663, 723, 789, |
|
|
|
860, 938, 1023, 1116, 1217, 1327, 1447, 1578, 1721, 1877, |
|
|
|
2047, 2232, 2434, 2655, 2895, 3157, 3443, 3755, 4095, 4466, |
|
|
|
4870, 5311, 5792, 6316, 6888, 7511, 8191, 8932, 9741, 10623, |
|
|
|
11584, 12633, 13776, 15023, 16383, 17866, 19483, 21246, 23169, |
|
|
|
25267, 27553, 30047, 32767, 35733, 38967, 42494, 46340, 50534, |
|
|
|
55108, 60096, 65535 |
|
|
|
}; |
|
|
|
#else |
|
|
|
#define PWM_TABLE_SIZE 32 |
|
|
|
#define PWM_TABLE_GET(x) pgm_read_byte(&pwmtable8[x]) |
|
|
|
const uint8_t pwmtable8[PWM_TABLE_SIZE] PROGMEM = |
|
|
|
{ |
|
|
|
0, 0, 1, 1, 1, 2, 2, 3, |
|
|
|
4, 5, 6, 7, 9, 10, 12, 15, |
|
|
|
18, 22, 26, 31, 37, 44, 53, 63, |
|
|
|
75, 90, 107, 127, 151, 180, 214, 255 |
|
|
|
}; |
|
|
|
#endif |
|
|
|
|
|
|
|
static uint8_t pwm_index[3]; |
|
|
|
static uint8_t pwm_setpoint[3]; |
|
|
|
static uint8_t pwm_setpoint_save[3]; |
|
|
|
static uint8_t pwm_fade_timer_running; |
|
|
|
|
|
|
|
/* *********************************************************************** */ |
|
|
|
|
|
|
|
static void pwm_update(uint8_t channel) |
|
|
|
{ |
|
|
|
uint8_t check_setpoint = 0; |
|
|
|
|
|
|
|
if (pwm_setpoint[channel] >= PWM_TABLE_SIZE) |
|
|
|
{ |
|
|
|
pwm_setpoint[channel] = (PWM_TABLE_SIZE - 1); |
|
|
|
} |
|
|
|
|
|
|
|
if ((pwm_setpoint[channel] > pwm_index[channel]) && |
|
|
|
(pwm_index[channel] < (PWM_TABLE_SIZE - 1)) |
|
|
|
) |
|
|
|
{ |
|
|
|
pwm_index[channel]++; |
|
|
|
check_setpoint = 1; |
|
|
|
} |
|
|
|
else if ((pwm_setpoint[channel] < pwm_index[channel]) && |
|
|
|
(pwm_index[channel] > 0) |
|
|
|
) |
|
|
|
{ |
|
|
|
pwm_index[channel]--; |
|
|
|
check_setpoint = 1; |
|
|
|
} |
|
|
|
|
|
|
|
/* setpoint reached, notify others */ |
|
|
|
if (check_setpoint) |
|
|
|
{ |
|
|
|
if (pwm_index[channel] == pwm_setpoint[channel]) |
|
|
|
{ |
|
|
|
event_queue(EVENT_TYPE_PWM_STATUS, |
|
|
|
channel, |
|
|
|
pwm_setpoint[channel]); |
|
|
|
} |
|
|
|
else if (!pwm_fade_timer_running) |
|
|
|
{ |
|
|
|
event_queue(EVENT_TYPE_TIMER_SET, |
|
|
|
EVENT_NUM_TIMER_PWM, |
|
|
|
PWM_TIMER_FADE_STEP_MS); |
|
|
|
|
|
|
|
pwm_fade_timer_running = 1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* if PWM is zero, disable output */ |
|
|
|
if (pwm_index[channel] == 0) |
|
|
|
{ |
|
|
|
switch (channel) |
|
|
|
{ |
|
|
|
case 0: |
|
|
|
PWM_CH0_OFF(); |
|
|
|
break; |
|
|
|
|
|
|
|
case 1: |
|
|
|
PWM_CH1_OFF(); |
|
|
|
break; |
|
|
|
|
|
|
|
case 2: |
|
|
|
PWM_CH2_OFF(); |
|
|
|
break; |
|
|
|
|
|
|
|
default: |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
/* if PWM is max, enable output */ |
|
|
|
else if (pwm_index[channel] == (PWM_TABLE_SIZE - 1)) |
|
|
|
{ |
|
|
|
switch (channel) |
|
|
|
{ |
|
|
|
case 0: |
|
|
|
PWM_CH0_ON(); |
|
|
|
break; |
|
|
|
|
|
|
|
case 1: |
|
|
|
PWM_CH1_ON(); |
|
|
|
break; |
|
|
|
|
|
|
|
case 2: |
|
|
|
PWM_CH2_ON(); |
|
|
|
break; |
|
|
|
|
|
|
|
default: |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
/* else load new PWM into timer */ |
|
|
|
else |
|
|
|
{ |
|
|
|
switch (channel) |
|
|
|
{ |
|
|
|
case 0: |
|
|
|
PWM_CH0_PWM(PWM_TABLE_GET(pwm_index[0])); |
|
|
|
break; |
|
|
|
|
|
|
|
case 1: |
|
|
|
PWM_CH1_PWM(PWM_TABLE_GET(pwm_index[1])); |
|
|
|
break; |
|
|
|
|
|
|
|
case 2: |
|
|
|
PWM_CH2_PWM(PWM_TABLE_GET(pwm_index[2])); |
|
|
|
break; |
|
|
|
|
|
|
|
default: |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
switch (channel) |
|
|
|
{ |
|
|
|
case 0: |
|
|
|
case 1: |
|
|
|
/* disable timer if all channels are ON or OFF */ |
|
|
|
if (PWM_TIM1_CHECK()) |
|
|
|
{ |
|
|
|
PWM_TIM1_ENABLE(); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
PWM_TIM1_DISABLE(); |
|
|
|
} |
|
|
|
break; |
|
|
|
|
|
|
|
case 2: |
|
|
|
/* disable timer if all channels are ON or OFF */ |
|
|
|
if (PWM_TIM3_CHECK()) |
|
|
|
{ |
|
|
|
PWM_TIM3_ENABLE(); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
PWM_TIM3_DISABLE(); |
|
|
|
} |
|
|
|
break; |
|
|
|
} |
|
|
|
} /* pwm_update */ |
|
|
|
|
|
|
|
|
|
|
|
void pwm_event_handler(event_entry_t * p_event) |
|
|
|
{ |
|
|
|
if (p_event->type == EVENT_TYPE_PWM_COMMAND) |
|
|
|
{ |
|
|
|
uint8_t channel = p_event->num; |
|
|
|
|
|
|
|
switch (p_event->value) |
|
|
|
{ |
|
|
|
case EVENT_VALUE_PWM_KEEP: |
|
|
|
pwm_setpoint[channel] = pwm_index[channel]; |
|
|
|
break; |
|
|
|
|
|
|
|
/* increment PWM */ |
|
|
|
case EVENT_VALUE_PWM_INC: |
|
|
|
if (pwm_setpoint[channel] < (PWM_TABLE_SIZE - 1)) |
|
|
|
{ |
|
|
|
pwm_setpoint[channel]++; |
|
|
|
} |
|
|
|
break; |
|
|
|
|
|
|
|
/* decrement PWM */ |
|
|
|
case EVENT_VALUE_PWM_DEC: |
|
|
|
if (pwm_setpoint[channel] > 0) |
|
|
|
{ |
|
|
|
pwm_setpoint[channel]--; |
|
|
|
} |
|
|
|
break; |
|
|
|
|
|
|
|
/* fade to min */ |
|
|
|
case EVENT_VALUE_PWM_FADE_MIN: |
|
|
|
pwm_setpoint[channel] = 0; |
|
|
|
break; |
|
|
|
|
|
|
|
/* fade to max */ |
|
|
|
case EVENT_VALUE_PWM_FADE_MAX: |
|
|
|
pwm_setpoint[channel] = (PWM_TABLE_SIZE - 1); |
|
|
|
break; |
|
|
|
|
|
|
|
/* toggle between 0 and stored value */ |
|
|
|
case EVENT_VALUE_PWM_TOGGLE: |
|
|
|
if (pwm_setpoint[channel] > 5) |
|
|
|
{ |
|
|
|
pwm_setpoint_save[channel] = pwm_setpoint[channel]; |
|
|
|
pwm_setpoint[channel] = 0; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
pwm_setpoint[channel] = pwm_setpoint_save[channel]; |
|
|
|
} |
|
|
|
break; |
|
|
|
|
|
|
|
default: |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
pwm_update(channel); |
|
|
|
} |
|
|
|
else if (p_event->type == EVENT_TYPE_PWM_VALUE) |
|
|
|
{ |
|
|
|
uint8_t channel = p_event->num; |
|
|
|
|
|
|
|
if (p_event->value < PWM_TABLE_SIZE) |
|
|
|
{ |
|
|
|
pwm_setpoint[channel] = p_event->value; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
pwm_setpoint[channel] = (PWM_TABLE_SIZE - 1); |
|
|
|
} |
|
|
|
|
|
|
|
pwm_update(channel); |
|
|
|
} |
|
|
|
else if ((p_event->type == EVENT_TYPE_TIMER_ELAPSED) && |
|
|
|
(p_event->num == EVENT_NUM_TIMER_PWM) |
|
|
|
) |
|
|
|
{ |
|
|
|
pwm_fade_timer_running = 0; |
|
|
|
|
|
|
|
pwm_update(0); |
|
|
|
pwm_update(1); |
|
|
|
pwm_update(2); |
|
|
|
} |
|
|
|
} /* pwm_event_handler */ |
|
|
|
|
|
|
|
|
|
|
|
uint8_t pwm_get_sleep_mode(void) |
|
|
|
{ |
|
|
|
if (PWM_TIM1_RUNNING() || PWM_TIM3_RUNNING()) |
|
|
|
{ |
|
|
|
return SLEEP_MODE_IDLE; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
return SLEEP_MODE_PWR_DOWN; |
|
|
|
} |
|
|
|
} /* pwm_get_sleep_mode */ |
|
|
|
|
|
|
|
|
|
|
|
void pwm_init(void) |
|
|
|
{ |
|
|
|
PWM_TIM_INIT(); |
|
|
|
} /* pwm_init */ |