| /* STAR_momentary version 1.6 | | /* MTN_momentary_temp version 1.0 |
| * | | * |
| * Changelog | | * Changelog |
| * | | * |
| * 1.0 Initial version | | * 1.0 First stab at this based on STAR_momentary v1.6 |
| * 1.1 Added ability for star 2 to change mode order | | |
| * 1.2 Fix for newer version of AVR Studio | | |
| * 1.3 Added support for dual PWM outputs and selection of PWM mode per output level | | |
| * 1.4 Added ability to switch to a momentary mode | | |
| * 1.5 Added ability to set mode to 0 but not have it turn off | | |
| * 1.6 Added ability to debounce the press | | |
| * | | * |
| */ | | */ |
| | | |
| /* | | /* |
| * NANJG 105C Diagram | | * NANJG 105C Diagram |
| * --- | | * --- |
| * -| |- VCC | | * -| |- VCC |
| * Star 4 -| |- Voltage ADC | | * Star 4 -| |- Voltage ADC |
| * Star 3 -| |- PWM | | * Star 3 -| |- PWM |
| * GND -| |- Star 2 | | * GND -| |- Star 2 |
| * --- | | * --- |
| * | | * |
| * FUSES | | * FUSES |
| * I use these fuse settings | | * I use these fuse settings |
| * Low: 0x75 (4.8MHz CPU without 8x divider, 9.4kHz phase-correct PWM or 18.75kHz fast-PWM) | | * Low: 0x75 (4.8MHz CPU without 8x divider, 9.4kHz phase-correct PWM or 18.75kHz fast-PWM) |
| * High: 0xff | | * High: 0xff |
| * | | * |
| * For more details on these settings, visit http://github.com/JCapSolutions/blf-firmware/wiki/PWM-Frequency | | * For more details on these settings, visit http://github.com/JCapSolutions/blf-firmware/wiki/PWM-Frequency |
| * | | * |
| * STARS | | * STARS |
| * Star 2 = Not used | | * Star 2 = Alt PWM output |
| * Star 3 = H-L if connected, L-H if not | | * Star 3 = Charger on? input |
| * Star 4 = Switch input | | * Star 4 = Switch input |
| * | | * |
| * VOLTAGE | | * VOLTAGE |
| * Resistor values for voltage divider (reference BLF-VLD README for more info) | | * Resistor values for voltage divider (reference BLF-VLD README for more info) |
| * Reference voltage can be anywhere from 1.0 to 1.2, so this cannot be all that accurate | | * Reference voltage can be anywhere from 1.0 to 1.2, so this cannot be all that accurate |
| * | | * |
| * VCC | | * VCC |
| * | | | * | |
| * Vd (~.25 v drop from protection diode) | | * Vd (~.25 v drop from protection diode) |
| * | | | * | |
| * 1912 (R1 19,100 ohms) | | * 1912 (R1 19,100 ohms) |
| * | | | * | |
| * |---- PB2 from MCU | | * |---- PB2 from MCU |
| * | | | * | |
| * 4701 (R2 4,700 ohms) | | * 4701 (R2 4,700 ohms) |
| * | | | * | |
| * GND | | * GND |
| * | | * |
| * ADC = ((V_bat - V_diode) * R2 * 255) / ((R1 + R2 ) * V_ref) | | * ADC = ((V_bat - V_diode) * R2 * 255) / ((R1 + R2 ) * V_ref) |
| * 125 = ((3.0 - .25 ) * 4700 * 255) / ((19100 + 4700) * 1.1 ) | | * 125 = ((3.0 - .25 ) * 4700 * 255) / ((19100 + 4700) * 1.1 ) |
| * 121 = ((2.9 - .25 ) * 4700 * 255) / ((19100 + 4700) * 1.1 ) | | * 121 = ((2.9 - .25 ) * 4700 * 255) / ((19100 + 4700) * 1.1 ) |
| * | | * |
| * Well 125 and 121 were too close, so it shut off right after lowering to low mode, so I went with | | * Well 125 and 121 were too close, so it shut off right after lowering to low mode, so I went with |
| * 130 and 120 | | * 130 and 120 |
| * | | * |
| * To find out what value to use, plug in the target voltage (V) to this equation | | * To find out what value to use, plug in the target voltage (V) to this equation |
| * value = (V * 4700 * 255) / (23800 * 1.1) | | * value = (V * 4700 * 255) / (23800 * 1.1) |
| * | | * |
| */ | | */ |
| #define F_CPU 4800000UL | | #define F_CPU 4800000UL |
| | | |
| // PWM Mode | | // PWM Mode |
| #define PHASE 0b00000001 | | #define PHASE 0b00000001 |
| #define FAST 0b00000011 | | #define FAST 0b00000011 |
| | | |
| /* | | /* |
| * ========================================================================= | | * ========================================================================= |
| * Settings to modify per driver | | * Settings to modify per driver |
| */ | | */ |
| | | |
| #define VOLTAGE_MON // Comment out to disable - ramp down and eventual shutoff when battery is low | | //#define VOLTAGE_MON // Comment out to disable - ramp down and eventual shutoff when battery is low |
| #define MODES 0,3,14,39,125,255 // Must be low to high, and must start with 0 | | #define CHARGER_MON // Comment out to disable - is charging cable connected |
| #define ALT_MODES 0,3,14,39,125,255 // Must be low to high, and must start with 0, the defines the level for the secondary output. Comment out if no secondary output | | #define MODES 0,3,32,125,255 // Must be low to high, and must start with 0 |
| #define MODE_PWM 0,PHASE,FAST,FAST,FAST,PHASE // Define one per mode above. 0 tells the light to go to sleep | | //#define ALT_MODES 0,3,32,125,255 // Must be low to high, and must start with 0, the defines the level for the secondary output. Comment out if no secondary output |
| #define TURBO // Comment out to disable - full output with a step down after n number of seconds | | #define MODE_PWM 0,PHASE,FAST,FAST,FAST // Define one per mode above. 0 tells the light to go to sleep |
| | | //#define TURBO // Comment out to disable - full output with a step down after n number of seconds |
| // If turbo is enabled, it will be where 255 is listed in the modes above | | // If turbo is enabled, it will be where 255 is listed in the modes above |
| #define TURBO_TIMEOUT 5625 // How many WTD ticks before before dropping down (.016 sec each) | | //#define TURBO_TIMEOUT 5625 // How many WTD ticks before before dropping down (.016 sec each) |
| // 90 = 5625 | | // 90 = 5625 |
| // 120 = 7500 | | // 120 = 7500 |
| | | |
| #define ADC_LOW 130 // When do we start ramping | | #define BATT_LOW 145 // When do we start ramping |
| #define ADC_CRIT 120 // When do we shut the light off | | #define BATT_CRIT 135 // When do we shut the light off |
| #define ADC_DELAY 188 // Delay in ticks between low-bat rampdowns (188 ~= 3s) | | #define CHARGER_HIGH 120 // When we should lower the output |
| | | #define CHARGER_LOW 100 // When we should raise the output |
| | | #define BATT_ADC_DELAY 188 // Delay in ticks between low-bat rampdowns (188 ~= 3s) |
| | | #define CHARGER_ADC_DELAY 15 // Delay in ticks before checking for charging cable (188 ~= 3s) |
| | | |
| //#define MOM_ENTER_DUR 128 // .16ms each. Comment out to disable this feature | | //#define MOM_ENTER_DUR 128 // 16ms each. Comment out to disable this feature |
| #define MOM_EXIT_DUR 128 // .16ms each | | //#define MOM_EXIT_DUR 128 // 16ms each |
| | | |
| #define MOM_MODE_IDX 4 // The index of the mode to use in MODES above if momementary feature enabled, starting at index of 0 | | //#define MOM_MODE_IDX 4 // The index of the mode to use in MODES above, starting at index of 0 |
| | | |
| /* | | /* |
| * ========================================================================= | | * ========================================================================= |
| */ | | */ |
| | | |
| #include <avr/pgmspace.h> | | #include <avr/pgmspace.h> |
| #include <avr/io.h> | | #include <avr/io.h> |
| #include <util/delay.h> | | #include <util/delay.h> |
| #include <avr/interrupt.h> | | #include <avr/interrupt.h> |
| #include <avr/wdt.h> | | #include <avr/wdt.h> |
| #include <avr/eeprom.h> | | #include <avr/eeprom.h> |
| #include <avr/sleep.h> | | #include <avr/sleep.h> |
| //#include <avr/power.h> | | //#include <avr/power.h> |
| | | |
| #define STAR3_PIN PB4 // If not connected, will cycle L-H. Connected, H-L | | |
| #define SWITCH_PIN PB3 // what pin the switch is connected to, which is Star 4 | | #define SWITCH_PIN PB3 // what pin the switch is connected to, which is Star 4 |
| #define PWM_PIN PB1 | | #define PWM_PIN PB1 |
| #define ALT_PWM_PIN PB0 | | #define ALT_PWM_PIN PB0 |
| #define VOLTAGE_PIN PB2 | | |
| #define ADC_CHANNEL 0x01 // MUX 01 corresponds with PB2 | | |
| #define ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2 | | #define ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2 |
| #define ADC_PRSCL 0x06 // clk/64 | | #define ADC_PRSCL 0x06 // clk/64 |
| | | |
| #define PWM_LVL OCR0B // OCR0B is the output compare register for PB1 | | #define PWM_LVL OCR0B // OCR0B is the output compare register for PB1 |
| #define ALT_PWM_LVL OCR0A // OCR0A is the output compare register for PB0 | | #define ALT_PWM_LVL OCR0A // OCR0A is the output compare register for PB0 |
| | | |
| //#define DEBOUNCE_BOTH // Comment out if you don't want to debounce the PRESS along with the RELEASE | | //#define DEBOUNCE_BOTH // Comment out if you don't want to debounce the PRESS along with the RELEASE |
| // PRESS debounce is only needed in special cases where the switch can experience errant signals | | // PRESS debounce is only needed in special cases where the switch can experience errant signals |
| #define DB_PRES_DUR 0b00000001 // time before we consider the switch pressed (after first realizing it was pressed) | | #define DB_PRES_DUR 0b00000001 // time before we consider the switch pressed (after first realizing it was pressed) |
| #define DB_REL_DUR 0b00001111 // time before we consider the switch released | | #define DB_REL_DUR 0b00001111 // time before we consider the switch released |
| // each bit of 1 from the right equals 16ms, so 0x0f = 64ms | | // each bit of 1 from the right equals 16ms, so 0x0f = 64ms |
| | | |
| // Switch handling | | // Switch handling |
| #define LONG_PRESS_DUR 32 // How many WDT ticks until we consider a press a long press | | #define LONG_PRESS_DUR 32 // How many WDT ticks until we consider a press a long press |
| // 32 is roughly .5 s | | // 32 is roughly .5 s |
| | | #define BEACON |
| | | #define BEACON_DUR 70 // How many WDT ticks until we enter the hold-to-BEACON mode |
| | | |
| /* | | /* |
| * The actual program | | * The actual program |
| * ========================================================================= | | * ========================================================================= |
| */ | | */ |
| | | |
| /* | | /* |
| * global variables | | * global variables |
| */ | | */ |
| const uint8_t modes[] = { MODES }; | | const uint8_t modes[] = { MODES }; |
| #ifdef ALT_MODES | | #ifdef ALT_MODES |
| const uint8_t alt_modes[] = { ALT_MODES }; | | const uint8_t alt_modes[] = { ALT_MODES }; |
| #endif | | #endif |
| const uint8_t mode_pwm[] = { MODE_PWM }; | | const uint8_t mode_pwm[] = { MODE_PWM }; |
| volatile uint8_t mode_idx = 0; | | volatile uint8_t mode_idx = 0; |
| volatile uint8_t press_duration = 0; | | volatile uint8_t press_duration = 0; |
| volatile uint8_t low_to_high = 0; | | |
| volatile uint8_t in_momentary = 0; | | volatile uint8_t in_momentary = 0; |
| | | #ifdef VOLTAGE_MON |
| | | volatile uint8_t adc_channel = 1; // MUX 01 corresponds with PB2, 02 for PB4. Will switch back and forth |
| | | #else |
| | | volatile uint8_t adc_channel = 2; // MUX 01 corresponds with PB2, 02 for PB4. Will switch back and forth |
| | | #endif |
| | | |
| // Debounce switch press value | | // Debounce switch press value |
| #ifdef DEBOUNCE_BOTH | | #ifdef DEBOUNCE_BOTH |
| int is_pressed() | | int is_pressed() |
| { | | { |
| static uint8_t pressed = 0; | | static uint8_t pressed = 0; |
| // Keep track of last switch values polled | | // Keep track of last switch values polled |
| static uint8_t buffer = 0x00; | | static uint8_t buffer = 0x00; |
| // Shift over and tack on the latest value, 0 being low for pressed, 1 for pulled-up for released | | // Shift over and tack on the latest value, 0 being low for pressed, 1 for pulled-up for released |
| buffer = (buffer << 1) | ((PINB & (1 << SWITCH_PIN)) == 0); | | buffer = (buffer << 1) | ((PINB & (1 << SWITCH_PIN)) == 0); |
| | | |
| if (pressed) { | | if (pressed) { |
| // Need to look for a release indicator by seeing if the last switch status has been 0 for n number of polls | | // Need to look for a release indicator by seeing if the last switch status has been 0 for n number of polls |
| pressed = (buffer & DB_REL_DUR); | | pressed = (buffer & DB_REL_DUR); |
| } else { | | } else { |
| // Need to look for pressed indicator by seeing if the last switch status was 1 for n number of polls | | // Need to look for pressed indicator by seeing if the last switch status was 1 for n number of polls |
| pressed = ((buffer & DB_PRES_DUR) == DB_PRES_DUR); | | pressed = ((buffer & DB_PRES_DUR) == DB_PRES_DUR); |
| } | | } |
| | | |
| return pressed; | | return pressed; |
| } | | } |
| #else | | #else |
| int is_pressed() | | int is_pressed() |
| { | | { |
| // Keep track of last switch values polled | | // Keep track of last switch values polled |
| static uint8_t buffer = 0x00; | | static uint8_t buffer = 0x00; |
| // Shift over and tack on the latest value, 0 being low for pressed, 1 for pulled-up for released | | // Shift over and tack on the latest value, 0 being low for pressed, 1 for pulled-up for released |
| buffer = (buffer << 1) | ((PINB & (1 << SWITCH_PIN)) == 0); | | buffer = (buffer << 1) | ((PINB & (1 << SWITCH_PIN)) == 0); |
| | | |
| return (buffer & DB_REL_DUR); | | return (buffer & DB_REL_DUR); |
| } | | } |
| #endif | | #endif |
| | | |
| inline void next_mode() { | | void next_mode() { |
| if (++mode_idx >= sizeof(modes)) { | | if (++mode_idx >= sizeof(modes)) { |
| // Wrap around | | // Wrap around |
| mode_idx = 0; | | mode_idx = 1; |
| } | | } |
| } | | } |
| | | |
| inline void prev_mode() { | | void prev_mode() { |
| if (mode_idx == 0) { | | if (mode_idx == 1) { |
| // Wrap around | | // Wrap around |
| mode_idx = sizeof(modes) - 1; | | mode_idx = sizeof(modes) - 1; |
| } else { | | } else { |
| --mode_idx; | | --mode_idx; |
| } | | } |
| } | | } |
| | | |
| inline void PCINT_on() { | | inline void PCINT_on() { |
| // Enable pin change interrupts | | // Enable pin change interrupts |
| GIMSK |= (1 << PCIE); | | GIMSK |= (1 << PCIE); |
| } | | } |
| | | |
| inline void PCINT_off() { | | inline void PCINT_off() { |
| // Disable pin change interrupts | | // Disable pin change interrupts |
| GIMSK &= ~(1 << PCIE); | | GIMSK &= ~(1 << PCIE); |
| } | | } |
| | | |
| // Need an interrupt for when pin change is enabled to ONLY wake us from sleep. | | // Need an interrupt for when pin change is enabled to ONLY wake us from sleep. |
| // All logic of what to do when we wake up will be handled in the main loop. | | // All logic of what to do when we wake up will be handled in the main loop. |
| EMPTY_INTERRUPT(PCINT0_vect); | | EMPTY_INTERRUPT(PCINT0_vect); |
| | | |
| inline void WDT_on() { | | inline void WDT_on() { |
| // Setup watchdog timer to only interrupt, not reset, every 16ms. | | // Setup watchdog timer to only interrupt, not reset, every 16ms. |
| cli(); // Disable interrupts | | cli(); // Disable interrupts |
| wdt_reset(); // Reset the WDT | | wdt_reset(); // Reset the WDT |
| WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence | | WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence |
| WDTCR = (1<<WDTIE); // Enable interrupt every 16ms | | WDTCR = (1<<WDTIE); // Enable interrupt every 16ms |
| sei(); // Enable interrupts | | sei(); // Enable interrupts |
| } | | } |
| | | |
| inline void WDT_off() | | inline void WDT_off() |
| { | | { |
| cli(); // Disable interrupts | | cli(); // Disable interrupts |
| wdt_reset(); // Reset the WDT | | wdt_reset(); // Reset the WDT |
| MCUSR &= ~(1<<WDRF); // Clear Watchdog reset flag | | MCUSR &= ~(1<<WDRF); // Clear Watchdog reset flag |
| WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence | | WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence |
| WDTCR = 0x00; // Disable WDT | | WDTCR = 0x00; // Disable WDT |
| sei(); // Enable interrupts | | sei(); // Enable interrupts |
| } | | } |
| | | |
| inline void ADC_on() { | | void ADC_on() { |
| ADMUX = (1 << REFS0) | (1 << ADLAR) | ADC_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2 | | ADMUX = (1 << REFS0) | (1 << ADLAR) | adc_channel; // 1.1v reference, left-adjust, ADC1/PB2 or ADC2/PB3 |
| DIDR0 |= (1 << ADC_DIDR); // disable digital input on ADC pin to reduce power consumption | | DIDR0 |= (1 << ADC_DIDR); // disable digital input on ADC pin to reduce power consumption |
| ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL; // enable, start, prescale | | ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL; // enable, start, prescale, Single Conversion mode |
| } | | } |
| | | |
| inline void ADC_off() { | | void ADC_off() { |
| ADCSRA &= ~(1<<7); //ADC off | | ADCSRA &= ~(1 << ADSC); //ADC off |
| } | | } |
| | | |
| void sleep_until_switch_press() | | void sleep_until_switch_press() |
| { | | { |
| // This routine takes up a lot of program memory :( | | // This routine takes up a lot of program memory :( |
| // Turn the WDT off so it doesn't wake us from sleep | | // Turn the WDT off so it doesn't wake us from sleep |
| // Will also ensure interrupts are on or we will never wake up | | // Will also ensure interrupts are on or we will never wake up |
| WDT_off(); | | WDT_off(); |
| // Need to reset press duration since a button release wasn't recorded | | // Need to reset press duration since a button release wasn't recorded |
| press_duration = 0; | | press_duration = 0; |
| // Enable a pin change interrupt to wake us up | | // Enable a pin change interrupt to wake us up |
| // However, we have to make sure the switch is released otherwise we will wake when the user releases the switch | | // However, we have to make sure the switch is released otherwise we will wake when the user releases the switch |
| while (is_pressed()) { | | while (is_pressed()) { |
| _delay_ms(16); | | _delay_ms(16); |
| } | | } |
| PCINT_on(); | | PCINT_on(); |
| // Enable sleep mode set to Power Down that will be triggered by the sleep_mode() command. | | // Enable sleep mode set to Power Down that will be triggered by the sleep_mode() command. |
| //set_sleep_mode(SLEEP_MODE_PWR_DOWN); | | //set_sleep_mode(SLEEP_MODE_PWR_DOWN); |
| // Now go to sleep | | // Now go to sleep |
| sleep_mode(); | | sleep_mode(); |
| // Hey, someone must have pressed the switch!! | | // Hey, someone must have pressed the switch!! |
| // Disable pin change interrupt because it's only used to wake us up | | // Disable pin change interrupt because it's only used to wake us up |
| PCINT_off(); | | PCINT_off(); |
| // Turn the WDT back on to check for switch presses | | // Turn the WDT back on to check for switch presses |
| WDT_on(); | | WDT_on(); |
| // Go back to main program | | // Go back to main program |
| } | | } |
| | | |
| // The watchdog timer is called every 16ms | | // The watchdog timer is called every 16ms |
| ISR(WDT_vect) { | | ISR(WDT_vect) { |
| | | |
| //static uint8_t press_duration = 0; // Pressed or not pressed | | //static uint8_t press_duration = 0; // Pressed or not pressed |
| static uint16_t turbo_ticks = 0; | | static uint16_t turbo_ticks = 0; |
| static uint8_t adc_ticks = ADC_DELAY; | | static uint16_t batt_adc_ticks = BATT_ADC_DELAY; |
| | | static uint16_t charger_adc_ticks = CHARGER_ADC_DELAY; |
| static uint8_t lowbatt_cnt = 0; | | static uint8_t lowbatt_cnt = 0; |
| | | static uint8_t high_charger_cnt = 0; |
| | | static uint8_t low_charger_cnt = 0; |
| | | static uint8_t highest_mode_idx = 255; |
| | | |
| if (is_pressed()) { | | if (is_pressed()) { |
| if (press_duration < 255) { | | if (press_duration < 255) { |
| press_duration++; | | press_duration++; |
| } | | } |
| | | |
| #ifdef MOM_ENTER_DUR | | #ifdef MOM_ENTER_DUR |
| if (in_momentary) { | | if (in_momentary) { |
| // Turn on full output | | // Turn on full output |
| mode_idx = MOM_MODE_IDX; | | mode_idx = MOM_MODE_IDX; |
| if (press_duration == MOM_EXIT_DUR) { | | if (press_duration == MOM_EXIT_DUR) { |
| // Turn light off and disable momentary | | // Turn light off and disable momentary |
| mode_idx = 0; | | mode_idx = 0; |
| in_momentary = 0; | | in_momentary = 0; |
| } | | } |
| return; | | return; |
| } | | } |
| #endif | | #endif |
| | | |
| if (press_duration == LONG_PRESS_DUR) { | | if (press_duration == LONG_PRESS_DUR) { |
| // Long press | | // Long press |
| if (low_to_high) { | | |
| prev_mode(); | | prev_mode(); |
| } else { | | highest_mode_idx = mode_idx; |
| next_mode(); | | |
| } | | |
| } | | } |
| | | #ifdef BEACON //Is BEACON turned on |
| | | if (press_duration == BEACON_DUR) { |
| | | PWM_LVL = 0; |
| | | while(is_pressed()){ |
| | | //Exit loop once switch is let go |
| | | PWM_LVL = 255; |
| | | _delay_ms(500); |
| | | PWM_LVL=0; |
| | | _delay_ms(5000); |
| | | //Can alter values to change BEACON duration |
| | | } |
| | | } |
| | | #endif |
| | | |
| #ifdef MOM_ENTER_DUR | | #ifdef MOM_ENTER_DUR |
| if (press_duration == MOM_ENTER_DUR) { | | if (press_duration == MOM_ENTER_DUR) { |
| in_momentary = 1; | | in_momentary = 1; |
| press_duration = 0; | | press_duration = 0; |
| } | | } |
| #endif | | #endif |
| // Just always reset turbo timer whenever the button is pressed | | // Just always reset turbo timer whenever the button is pressed |
| turbo_ticks = 0; | | turbo_ticks = 0; |
| // Same with the ramp down delay | | // Same with the ramp down delay |
| adc_ticks = ADC_DELAY; | | batt_adc_ticks = BATT_ADC_DELAY; |
| | | charger_adc_ticks = CHARGER_ADC_DELAY; |
| | | |
| } else { | | } else { |
| | | |
| #ifdef MOM_ENTER_DUR | | #ifdef MOM_ENTER_DUR |
| if (in_momentary) { | | if (in_momentary) { |
| // Turn off the light | | // Turn off the light |
| mode_idx = 0; | | mode_idx = 0; |
| return; | | return; |
| } | | } |
| #endif | | #endif |
| | | |
| // Not pressed | | // Not pressed |
| if (press_duration > 0 && press_duration < LONG_PRESS_DUR) { | | if (press_duration > 0 && press_duration < LONG_PRESS_DUR) { |
| // Short press | | // Short press |
| if (low_to_high) { | | |
| next_mode(); | | next_mode(); |
| } else { | | highest_mode_idx = mode_idx; |
| prev_mode(); | | |
| } | | |
| } else { | | } else { |
| // Only do turbo check when switch isn't pressed | | // Only do turbo check when switch isn't pressed |
| #ifdef TURBO | | #ifdef TURBO |
| if (modes[mode_idx] == 255) { | | if (modes[mode_idx] == 255) { |
| turbo_ticks++; | | turbo_ticks++; |
| if (turbo_ticks > TURBO_TIMEOUT) { | | if (turbo_ticks > TURBO_TIMEOUT) { |
| // Go to the previous mode | | // Go to the previous mode |
| prev_mode(); | | prev_mode(); |
| } | | } |
| } | | } |
| #endif | | #endif |
| // Only do voltage monitoring when the switch isn't pressed | | // Only do voltage monitoring when the switch isn't pressed |
| #ifdef VOLTAGE_MON | | // See if conversion is done. We moved this up here because we want to stay on |
| if (adc_ticks > 0) { | | // the current ADC input until the conversion is done, and then switch to the new |
| --adc_ticks; | | // input, start the monitoring |
| | | if (batt_adc_ticks > 0) { |
| | | --batt_adc_ticks; |
| } | | } |
| if (adc_ticks == 0) { | | if (charger_adc_ticks > 0) { |
| // See if conversion is done | | --charger_adc_ticks; |
| if (ADCSRA & (1 << ADIF)) { | | } |
| // See if voltage is lower than what we were looking for | | if (ADCSRA & (1 << ADIF)) { |
| if (ADCH < ((mode_idx == 1) ? ADC_CRIT : ADC_LOW)) { | | if (adc_channel == 0x01) { |
| ++lowbatt_cnt; | | |
| } else { | | if (batt_adc_ticks == 0) { |
| lowbatt_cnt = 0; | | // See if voltage is lower than what we were looking for |
| | | if (ADCH < ((mode_idx == 1) ? BATT_CRIT : BATT_LOW)) { |
| | | ++lowbatt_cnt; |
| | | } else { |
| | | lowbatt_cnt = 0; |
| | | } |
| | | |
| | | // See if it's been low for a while |
| | | if (lowbatt_cnt >= 4) { |
| | | prev_mode(); |
| | | highest_mode_idx = mode_idx; |
| | | lowbatt_cnt = 0; |
| | | // If we reach 0 here, main loop will go into sleep mode |
| | | // Restart the counter to when we step down again |
| | | batt_adc_ticks = BATT_ADC_DELAY; |
| | | charger_adc_ticks = CHARGER_ADC_DELAY; |
| | | } |
| } | | } |
| } | | // Switch ADC to temp monitoring |
| | | adc_channel = 0x02; |
| | | ADMUX = ((ADMUX & 0b11111100) | adc_channel); |
| | | } else if (adc_channel == 0x02) { |
| | | |
| | | if (charger_adc_ticks == 0) { |
| | | // See if temp is higher than the high threshold |
| | | if (ADCH > ((mode_idx == 1) ? 255 : CHARGER_HIGH)) { |
| | | ++high_charger_cnt; |
| | | // See if it's lower that the low threshold, but not at or above the batt-adjusted mode |
| | | } else { |
| | | high_charger_cnt = 0; |
| | | low_charger_cnt = 0; |
| | | } |
| | | |
| // See if it's been low for a while | | // See if it's been low for a while |
| if (lowbatt_cnt >= 4) { | | if (high_charger_cnt >= 1) { |
| prev_mode(); | | // TODO - step down half |
| lowbatt_cnt = 0; | | mode_idx = 0; |
| // If we reach 0 here, main loop will go into sleep mode | | high_charger_cnt = 0; |
| // Restart the counter to when we step down again | | low_charger_cnt = 0; |
| adc_ticks = ADC_DELAY; | | // If we reach 0 here, main loop will go into sleep mode |
| | | // Restart the counter to when we step down again |
| | | charger_adc_ticks = CHARGER_ADC_DELAY; |
| | | } else { |
| | | // TODO - step up half |
| | | mode_idx; |
| | | high_charger_cnt = 0; |
| | | low_charger_cnt = 0; |
| | | // TODO - CHARGER_ADC_DELAY?? Might jump up and cause it to overheat waiting too long |
| | | charger_adc_ticks = CHARGER_ADC_DELAY; |
| | | } |
| | | } |
| | | #ifdef VOLTAGE_MON |
| | | // Switch ADC to battery monitoring |
| | | adc_channel = 0x01; |
| | | ADMUX = ((ADMUX & 0b11111100) | adc_channel); |
| | | #endif; |
| } | | } |
| | | |
| // Make sure conversion is running for next time through | | |
| ADCSRA |= (1 << ADSC); | | |
| } | | } |
| #endif | | // Start conversion for next time through |
| | | ADCSRA |= (1 << ADSC); |
| } | | } |
| press_duration = 0; | | press_duration = 0; |
| } | | } |
| } | | } |
| | | |
| int main(void) | | int main(void) |
| { | | { |
| // Set all ports to input, and turn pull-up resistors on for the inputs we are using | | // Set all ports to input, and turn pull-up resistors on for the inputs we are using |
| DDRB = 0x00; | | DDRB = 0x00; |
| PORTB = (1 << SWITCH_PIN) | (1 << STAR3_PIN); | | PORTB = (1 << SWITCH_PIN); |
| | | |
| // Set the switch as an interrupt for when we turn pin change interrupts on | | // Set the switch as an interrupt for when we turn pin change interrupts on |
| PCMSK = (1 << SWITCH_PIN); | | PCMSK = (1 << SWITCH_PIN); |
| | | |
| // Set PWM pin to output | | // Set PWM pin to output |
| #ifdef ALT_MODES | | #ifdef ALT_MODES |
| DDRB = (1 << PWM_PIN) | (1 << ALT_PWM_PIN); | | DDRB = (1 << PWM_PIN) | (1 << ALT_PWM_PIN); |
| #else | | #else |
| DDRB = (1 << PWM_PIN); | | DDRB = (1 << PWM_PIN); |
| #endif | | #endif |
| | | |
| // Set timer to do PWM for correct output pin and set prescaler timing | | // Set timer to do PWM for correct output pin and set prescaler timing |
| //TCCR0A = 0x23; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23 | | //TCCR0A = 0x23; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23 |
| TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...) | | TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...) |
| | | |
| // Turn features on or off as needed | | // Turn features on or off as needed |
| #ifdef VOLTAGE_MON | | |
| ADC_on(); | | ADC_on(); |
| #else | | |
| ADC_off(); | | |
| #endif | | |
| ACSR |= (1<<7); //AC off | | ACSR |= (1<<7); //AC off |
| | | |
| // Determine if we are going L-H, or H-L based on Star 3 | | |
| if ((PINB & (1 << STAR3_PIN)) == 0) { | | |
| // High to Low | | |
| low_to_high = 0; | | |
| } else { | | |
| low_to_high = 1; | | |
| } | | |
| | | |
| // Enable sleep mode set to Power Down that will be triggered by the sleep_mode() command. | | // Enable sleep mode set to Power Down that will be triggered by the sleep_mode() command. |
| set_sleep_mode(SLEEP_MODE_PWR_DOWN); | | set_sleep_mode(SLEEP_MODE_PWR_DOWN); |
| sleep_until_switch_press(); | | sleep_until_switch_press(); |
| | | |
| uint8_t last_mode_idx = 0; | | uint8_t last_mode_idx = 0; |
| | | |
| while(1) { | | while(1) { |
| // We will never leave this loop. The WDT will interrupt to check for switch presses and | | // We will never leave this loop. The WDT will interrupt to check for switch presses and |
| // will change the mode if needed. If this loop detects that the mode has changed, run the | | // will change the mode if needed. If this loop detects that the mode has changed, run the |
| // logic for that mode while continuing to check for a mode change. | | // logic for that mode while continuing to check for a mode change. |
| if (mode_idx != last_mode_idx) { | | if (mode_idx != last_mode_idx) { |
| // The WDT changed the mode. | | // The WDT changed the mode. |
| if (mode_idx > 0) { | | if (mode_idx > 0) { |
| // No need to change the mode if we are just turning the light off | | // No need to change the mode if we are just turning the light off |
| // Check if the PWM mode is different | | // Check if the PWM mode is different |
| if (mode_pwm[last_mode_idx] != mode_pwm[mode_idx]) { | | if (mode_pwm[last_mode_idx] != mode_pwm[mode_idx]) { |
| #ifdef ALT_MODES | | #ifdef ALT_MODES |
| TCCR0A = mode_pwm[mode_idx] | 0b10100000; // Use both outputs | | TCCR0A = mode_pwm[mode_idx] | 0b10100000; // Use both outputs |
| #else | | #else |
| TCCR0A = mode_pwm[mode_idx] | 0b00100000; // Only use the normal output | | TCCR0A = mode_pwm[mode_idx] | 0b00100000; // Only use the normal output |
| #endif | | #endif |
| } | | } |
| } | | } |
| PWM_LVL = modes[mode_idx]; | | PWM_LVL = modes[mode_idx]; |
| #ifdef ALT_MODES | | #ifdef ALT_MODES |
| ALT_PWM_LVL = alt_modes[mode_idx]; | | ALT_PWM_LVL = alt_modes[mode_idx]; |
| #endif | | #endif |
| last_mode_idx = mode_idx; | | last_mode_idx = mode_idx; |
| if (mode_pwm[mode_idx] == 0) { | | if (mode_pwm[mode_idx] == 0) { |
| _delay_ms(1); // Need this here, maybe instructions for PWM output not getting executed before shutdown? | | _delay_ms(1); // Need this here, maybe instructions for PWM output not getting executed before shutdown? |
| // Go to sleep | | // Go to sleep |
| sleep_until_switch_press(); | | sleep_until_switch_press(); |
| } | | } |
| } | | } |
| } | | } |
| | | |
| return 0; // Standard Return Code | | return 0; // Standard Return Code |
| } | | } |