1
#define F_CPU 4800000UL
4
* =========================================================================
5
* Settings to modify per driver
8
#define VOLTAGE_MON // Comment out to disable - ramp down and eventual shutoff when battery is low
9
#define MODES 0,16,32,125,255 // Must be low to high, and must start with 0
10
#define TURBO // Comment out to disable - full output with a step down after n number of seconds
11
// If turbo is enabled, it will be where 255 is listed in the modes above
12
#define TURBO_TIMEOUT 5625 // How many WTD ticks before before dropping down (.016 sec each)
16
#define ADC_LOW 130 // When do we start ramping
17
#define ADC_CRIT 120 // When do we shut the light off
18
#define ADC_DELAY 188 // Delay in ticks between low-bat rampdowns (188 ~= 3s)
21
* =========================================================================
24
#include <avr/pgmspace.h>
26
#include <util/delay.h>
27
#include <avr/interrupt.h>
29
#include <avr/eeprom.h>
30
#include <avr/sleep.h>
31
//#include <avr/power.h>
35
#define SWITCH_PIN PB3 // what pin the switch is connected to, which is Star 4
37
#define VOLTAGE_PIN PB2
38
#define ADC_CHANNEL 0x01 // MUX 01 corresponds with PB2
39
#define ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2
40
#define ADC_PRSCL 0x06 // clk/64
42
#define PWM_LVL OCR0B // OCR0B is the output compare register for PB1
44
#define DB_REL_DUR 0b00001111 // time before we consider the switch released after
45
// each bit of 1 from the right equals 16ms, so 0x0f = 64ms
48
#define LONG_PRESS_DUR 32 // How many WDT ticks until we consider a press a long press
53
* =========================================================================
59
PROGMEM const uint8_t modes[] = { MODES };
60
volatile uint8_t mode_idx = 0;
61
volatile uint8_t press_duration = 0;
63
// Debounced switch press value
66
// Keep track of last switch values polled
67
static uint8_t buffer = 0x00;
68
// Shift over and tack on the latest value, 0 being low for pressed, 1 for pulled-up for released
69
buffer = (buffer << 1) | ((PINB & (1 << SWITCH_PIN)) == 0);
70
return (buffer & DB_REL_DUR);
73
inline void next_mode() {
74
if (++mode_idx >= sizeof(modes)) {
80
inline void prev_mode() {
83
mode_idx = sizeof(modes) - 1;
89
inline void PCINT_on() {
90
// Enable pin change interrupts
94
inline void PCINT_off() {
95
// Disable pin change interrupts
96
GIMSK &= ~(1 << PCIE);
99
// Need an interrupt for when pin change is enabled to ONLY wake us from sleep.
100
// All logic of what to do when we wake up will be handled in the main loop.
101
EMPTY_INTERRUPT(PCINT0_vect);
103
inline void WDT_on() {
104
// Setup watchdog timer to only interrupt, not reset, every 16ms.
105
cli(); // Disable interrupts
106
wdt_reset(); // Reset the WDT
107
WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence
108
WDTCR = (1<<WDTIE); // Enable interrupt every 16ms
109
sei(); // Enable interrupts
112
inline void WDT_off()
114
cli(); // Disable interrupts
115
wdt_reset(); // Reset the WDT
116
MCUSR &= ~(1<<WDRF); // Clear Watchdog reset flag
117
WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence
118
WDTCR = 0x00; // Disable WDT
119
sei(); // Enable interrupts
122
inline void ADC_on() {
123
ADMUX = (1 << REFS0) | (1 << ADLAR) | ADC_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2
124
DIDR0 |= (1 << ADC_DIDR); // disable digital input on ADC pin to reduce power consumption
125
ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL; // enable, start, prescale
128
inline void ADC_off() {
129
ADCSRA &= ~(1<<7); //ADC off
132
void sleep_until_switch_press()
134
// This routine takes up a lot of program memory :(
135
// Turn the WDT off so it doesn't wake us from sleep
136
// Will also ensure interrupts are on or we will never wake up
138
// Need to reset press duration since a button release wasn't recorded
140
// Enable a pin change interrupt to wake us up
141
// However, we have to make sure the switch is released otherwise we will wake when the user releases the switch
142
while (is_pressed()) {
146
// Enable sleep mode set to Power Down that will be triggered by the sleep_mode() command.
147
//set_sleep_mode(SLEEP_MODE_PWR_DOWN);
150
// Hey, someone must have pressed the switch!!
151
// Disable pin change interrupt because it's only used to wake us up
153
// Turn the WDT back on to check for switch presses
155
// Go back to main program
158
// The watchdog timer is called every 16ms
161
//static uint8_t press_duration = 0; // Pressed or not pressed
162
static uint16_t turbo_ticks = 0;
163
static uint8_t adc_ticks = ADC_DELAY;
164
static uint8_t lowbatt_cnt = 0;
167
if (press_duration < 255) {
171
if (press_duration == LONG_PRESS_DUR) {
172
// Long press, go to previous mode
175
// Just always reset turbo timer whenever the button is pressed
177
// Same with the ramp down delay
178
adc_ticks = ADC_DELAY;
181
if (press_duration > 0 && press_duration < LONG_PRESS_DUR) {
182
// Short press, go to next mode
185
// Only do turbo check when switch isn't pressed
187
if (pgm_read_byte(&modes[mode_idx]) == 255) {
189
if (turbo_ticks > TURBO_TIMEOUT) {
190
// Go to the previous mode
195
// Only do voltage monitoring when the switch isn't pressed
200
if (adc_ticks == 0) {
201
// See if conversion is done
202
if (ADCSRA & (1 << ADIF)) {
203
// See if voltage is lower than what we were looking for
204
if (ADCH < ((mode_idx == 1) ? ADC_CRIT : ADC_LOW)) {
211
// See if it's been low for a while
212
if (lowbatt_cnt >= 4) {
215
// If we reach 0 here, main loop will go into sleep mode
216
// Restart the counter to when we step down again
217
adc_ticks = ADC_DELAY;
220
// Make sure conversion is running for next time through
221
ADCSRA |= (1 << ADSC);
231
// Set all ports to input, and turn pull-up resistors on for the inputs we are using
233
PORTB = (1 << SWITCH_PIN) | (1 << STAR2_PIN) | (1 << STAR3_PIN);
235
// Set the switch as an interrupt for when we turn pin change interrupts on
236
PCMSK = (1 << SWITCH_PIN);
238
// Set PWM pin to output
239
DDRB = (1 << PWM_PIN);
241
// Set timer to do PWM for correct output pin and set prescaler timing
242
TCCR0A = 0x23; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23
243
TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
245
// Turn features on or off as needed
251
ACSR |= (1<<7); //AC off
253
// Enable sleep mode set to Power Down that will be triggered by the sleep_mode() command.
254
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
255
sleep_until_switch_press();
257
uint8_t last_mode_idx = 0;
260
// We will never leave this loop. The WDT will interrupt to check for switch presses and
261
// will change the mode if needed. If this loop detects that the mode has changed, run the
262
// logic for that mode while continuing to check for a mode change.
263
if (mode_idx != last_mode_idx) {
264
last_mode_idx = mode_idx;
265
// The WDT changed the mode.
266
PWM_LVL = pgm_read_byte(&modes[mode_idx]);
268
_delay_ms(1); // Need this here, maybe instructions for PWM output not getting executed before shutdown?
270
sleep_until_switch_press();
275
return 0; // Standard Return Code