5
* Star 4 -| |- Voltage ADC
10
* CPU speed is 4.8Mhz without the 8x divider when low fuse is 0x75
12
* define F_CPU 4800000 CPU: 4.8MHz PWM: 9.4kHz ####### use low fuse: 0x75 #######
13
* /8 PWM: 1.176kHz ####### use low fuse: 0x65 #######
14
* define F_CPU 9600000 CPU: 9.6MHz PWM: 19kHz ####### use low fuse: 0x7a #######
15
* /8 PWM: 2.4kHz ####### use low fuse: 0x6a #######
17
* Above PWM speeds are for phase-correct PWM. This program uses Fast-PWM, which when the CPU is 4.8MHz will be 18.75 kHz
20
* I use these fuse settings
25
* Star 2 = H-L if connected, L-H if not
26
* Star 3 = Memory if not connected
27
* Star 4 = Input for switch
30
* Resistor values for voltage divider (reference BLF-VLD README for more info)
31
* Reference voltage can be anywhere from 1.0 to 1.2, so this cannot be all that accurate
35
* Vd (~.25 v drop from protection diode)
37
* 1912 (R1 19,100 ohms)
41
* 4701 (R2 4,700 ohms)
45
* ADC = ((V_bat - V_diode) * R2 * 255) / ((R1 + R2 ) * V_ref)
46
* 125 = ((3.0 - .25 ) * 4700 * 255) / ((19100 + 4700) * 1.1 )
47
* 121 = ((2.9 - .25 ) * 4700 * 255) / ((19100 + 4700) * 1.1 )
49
* Well 125 and 121 were too close, so it shut off right after lowering to low mode, so I went with
52
* To find out what value to use, plug in the target voltage (V) to this equation
53
* value = (V * 4700 * 255) / (23800 * 1.1)
56
* v1.0 - Initial try, there's bound to be bugs
58
#define F_CPU 4800000UL
61
* =========================================================================
62
* Settings to modify per driver
65
#define VOLTAGE_MON // Comment out to disable - ramp down and eventual shutoff when battery is low
66
#define MODES 16,32,125,255 // Must be low to high, star determines which way we cycle through them
67
#define TURBO // Comment out to disable - full output with a step down after n number of seconds
68
// If turbo is enabled, it will be where 255 is listed in the modes above
69
#define TURBO_TIMEOUT 5625 // How many WTD ticks before before dropping down (.016 sec each)
73
#define ADC_LOW 130 // When do we start ramping
74
#define ADC_CRIT 120 // When do we shut the light off
75
#define ADC_DELAY 188 // Delay in ticks between low-bat rampdowns (188 ~= 3s)
78
* =========================================================================
81
#include <avr/pgmspace.h>
83
#include <util/delay.h>
84
#include <avr/interrupt.h>
86
#include <avr/eeprom.h>
87
#include <avr/sleep.h>
88
//#include <avr/power.h>
90
#define STAR2_PIN PB0 // If not connected, will cycle L-H. Connected, H-L
91
#define STAR3_PIN PB4 // If not connected, will enable memory
92
#define SWITCH_PIN PB3 // what pin the switch is connected to, which is Star 4
94
#define VOLTAGE_PIN PB2
95
#define ADC_CHANNEL 0x01 // MUX 01 corresponds with PB2
96
#define ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2
97
#define ADC_PRSCL 0x06 // clk/64
99
#define PWM_LVL OCR0B // OCR0B is the output compare register for PB1
101
#define DB_REL_DUR 0b00001111 // time before we consider the switch released after
102
// each bit of 1 from the right equals 16ms, so 0x0f = 64ms
105
#define LONG_PRESS_DUR 32 // How many WDT ticks until we consider a press a long press
106
// 32 is roughly .5 s
110
* =========================================================================
116
PROGMEM uint8_t modes[] = { MODES };
117
volatile uint8_t mode_idx = 0;
118
volatile uint8_t press_duration = 0;
119
volatile uint8_t low_to_high = 0;
126
void store_mode_idx(uint8_t lvl) { //central method for writing (with wear leveling)
127
uint8_t oldpos=eepos;
128
eepos=(eepos+1)&31; //wear leveling, use next cell
129
// Write the current mode
130
EEARL=eepos; EEDR=lvl; EECR=32+4; EECR=32+4+2; //WRITE //32:write only (no erase) 4:enable 2:go
131
while(EECR & 2); //wait for completion
132
// Erase the last mode
133
EEARL=oldpos; EECR=16+4; EECR=16+4+2; //ERASE //16:erase only (no write) 4:enable 2:go
135
inline void read_mode_idx() {
136
eeprom_read_block(&eep, 0, 32);
137
while((eep[eepos] == 0xff) && (eepos < 32)) eepos++;
138
if (eepos < 32) mode_idx = eep[eepos];//&0x10; What the?
142
// Debounced switch press value
145
// Keep track of last switch values polled
146
static uint8_t buffer = 0x00;
147
// Shift over and tack on the latest value, 0 being low for pressed, 1 for pulled-up for released
148
buffer = (buffer << 1) | ((PINB & (1 << SWITCH_PIN)) == 0);
149
return (buffer & DB_REL_DUR);
152
void next_mode(uint8_t save) {
153
if (++mode_idx >= sizeof(modes)) {
157
if (memory && save) {
158
store_mode_idx(mode_idx);
162
void prev_mode(uint8_t save) {
165
mode_idx = sizeof(modes) - 1;
169
if (memory && save) {
170
store_mode_idx(mode_idx);
174
inline void PCINT_on() {
175
// Enable pin change interrupts
176
GIMSK |= (1 << PCIE);
179
inline void PCINT_off() {
180
// Disable pin change interrupts
181
GIMSK &= ~(1 << PCIE);
185
// Need an interrupt for when pin change is enabled to ONLY wake us from sleep.
186
// All logic of what to do when we wake up will be handled in the main loop.
187
EMPTY_INTERRUPT(PCINT0_vect);
189
inline void WDT_on() {
190
// Setup watchdog timer to only interrupt, not reset, every 16ms.
191
cli(); // Disable interrupts
192
wdt_reset(); // Reset the WDT
193
WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence
194
WDTCR = (1<<WDTIE); // Enable interrupt every 16ms
195
sei(); // Enable interrupts
198
inline void WDT_off()
200
cli(); // Disable interrupts
201
wdt_reset(); // Reset the WDT
202
MCUSR &= ~(1<<WDRF); // Clear Watchdog reset flag
203
WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence
204
WDTCR = 0x00; // Disable WDT
205
sei(); // Enable interrupts
209
inline void ADC_on() {
210
ADMUX = (1 << REFS0) | (1 << ADLAR) | ADC_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2
211
DIDR0 |= (1 << ADC_DIDR); // disable digital input on ADC pin to reduce power consumption
212
ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL; // enable, start, prescale
215
inline void ADC_off() {
216
ADCSRA &= ~(1<<7); //ADC off
220
void sleep_until_switch_press()
222
// This routine takes up a lot of program memory :(
223
// Turn the WDT off so it doesn't wake us from sleep
224
// Will also ensure interrupts are on or we will never wake up
226
// Need to reset press duration since a button release wasn't recorded
228
// Enable a pin change interrupt to wake us up
229
// However, we have to make sure the switch is released otherwise we will wake when the user releases the switch
230
while (is_pressed()) {
234
// Enable sleep mode set to Power Down that will be triggered by the sleep_mode() command.
235
//set_sleep_mode(SLEEP_MODE_PWR_DOWN);
238
// Hey, someone must have pressed the switch!!
239
// Disable pin change interrupt because it's only used to wake us up
241
// Turn the WDT back on to check for switch presses
243
// Go back to main program
247
// The watchdog timer is called every 16ms
250
//static uint8_t press_duration = 0; // Pressed or not pressed
251
static uint16_t turbo_ticks = 0;
252
static uint8_t adc_ticks = ADC_DELAY;
253
static uint8_t lowbatt_cnt = 0;
256
if (press_duration < 255) {
260
if (press_duration == LONG_PRESS_DUR) {
268
// Just always reset turbo timer whenever the button is pressed
270
// Same with the ramp down delay
271
adc_ticks = ADC_DELAY;
274
if (press_duration > 0 && press_duration < LONG_PRESS_DUR) {
282
// Only do turbo check when switch isn't pressed
284
if (pgm_read_byte(&modes[mode_idx]) == 255) {
286
if (turbo_ticks > TURBO_TIMEOUT) {
287
// Go to the previous mode
292
// Only do voltage monitoring when the switch isn't pressed and we aren't at the lowest level
298
if (adc_ticks == 0) {
299
// See if conversion is done
300
if (ADCSRA & (1 << ADIF)) {
301
// See if voltage is lower than what we were looking for
302
if (ADCH < ((mode_idx == 1) ? ADC_CRIT : ADC_LOW)) {
309
// See if it's been low for a while
310
if (lowbatt_cnt >= 4) {
313
// Restart the counter to when we step down again
314
adc_ticks = ADC_DELAY;
317
// Make sure conversion is running for next time through
318
ADCSRA |= (1 << ADSC);
329
// Set all ports to input, and turn pull-up resistors on for the inputs we are using
331
PORTB = (1 << SWITCH_PIN) | (1 << STAR2_PIN) | (1 << STAR3_PIN);
333
// Set the switch as an interrupt for when we turn pin change interrupts on
334
PCMSK = (1 << SWITCH_PIN);
336
// Set PWM pin to output
337
DDRB = (1 << PWM_PIN);
339
// Set timer to do PWM for correct output pin and set prescaler timing
340
TCCR0A = 0x23; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23
341
TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
343
// Turn features on or off as needed
349
ACSR |= (1<<7); //AC off
351
// Determine if we are going L-H, or H-L based on STAR 2
352
if ((PINB & (1 << STAR2_PIN)) == 0) {
358
// Not soldered (1) should enable memory
359
memory = ((PINB & (1 << STAR3_PIN)) > 0) ? 1 : 0;
361
// Don't think we want to ever go to sleep
362
// Enable sleep mode set to Power Down that will be triggered by the sleep_mode() command.
363
//set_sleep_mode(SLEEP_MODE_PWR_DOWN);
364
//sleep_until_switch_press();
368
// Determine what mode we should fire up
369
// Read the last mode that was saved
376
mode_idx = sizeof(modes);
380
PWM_LVL = pgm_read_byte(&modes[mode_idx]);
382
uint8_t last_mode_idx = mode_idx;
385
// We will never leave this loop. The WDT will interrupt to check for switch presses and
386
// will change the mode if needed. If this loop detects that the mode has changed, run the
387
// logic for that mode while continuing to check for a mode change.
388
if (mode_idx != last_mode_idx) {
390
last_mode_idx = mode_idx;
391
// The WDT changed the mode.
392
PWM_LVL = pgm_read_byte(&modes[mode_idx]);
396
return 0; // Standard Return Code
b'\\ No newline at end of file'