Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /* STAR version 1.2 - MODIFIED
- // Made for Convoy S3 flashlight.
- // Has 3 modes (low, medium and maximum) which can be changed below.
- // No stars. No memory. 3 seconds for saving mode. Always starts up in lowest mode.
- // To use medium, click 2 times within 3 seconds. To use the max mode, click the button 3 times within the same time period (3 seconds).
- // Light has a fade effect when turning on a mode. Its not possible to fade out.
- // Low voltage code was not tested after the changes.
- // Ticks are every 32 ms
- // Lowest mode for Convoy S3 is: 15 (but flickers slightly). Next mode 16 is brighter and does not flicker
- *
- * Change log
- *
- * 1.0 Initial version
- * 1.1 Cleaned up the code
- * 1.2 Added support for dual PWM outputs and selection of PWM mode per output level
- * 1.3 Added ability to have turbo ramp down gradually instead of step down
- *
- */
- /*
- * NANJG 105C Diagram
- * ---
- * -| |- VCC
- * Star 4 -| |- Voltage ADC
- * Star 3 -| |- PWM
- * GND -| |- Star 2
- * ---
- *
- * FUSES
- * I use these fuse settings
- * Low: 0x75 (4.8MHz CPU without 8x divider, 9.4kHz phase-correct PWM or 18.75kHz fast-PWM)
- * High: 0xff
- *
- * For more details on these settings, visit http://github.com/JCapSolutions/blf-firmware/wiki/PWM-Frequency
- *
- * STARS
- * Star 2 = Moon if connected and alternate PWM output not used
- * Star 3 = H-L if connected, L-H if not
- * Star 4 = Memory if not connected
- *
- * VOLTAGE
- * 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
- *
- * VCC
- * |
- * Vd (~.25 v drop from protection diode)
- * |
- * 1912 (R1 19,100 ohms)
- * |
- * |---- PB2 from MCU
- * |
- * 4701 (R2 4,700 ohms)
- * |
- * GND
- *
- * ADC = ((V_bat - V_diode) * R2 * 255) / ((R1 + R2 ) * V_ref)
- * 125 = ((3.0 - .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
- * 130 and 120
- *
- * To find out what value to use, plug in the target voltage (V) to this equation
- * value = (V * 4700 * 255) / (23800 * 1.1)
- *
- */
- #define F_CPU 4800000UL
- /*
- * =========================================================================
- * Settings to modify per driver
- */
- #define VOLTAGE_MON // Comment out to disable
- //#define TICKS_250MS // If enabled, ticks are every 250 ms. If disabled, ticks are every 500 ms
- // Affects mode saving and turbo timeout/rampdown timing
- //#define MODE_MOON 15 // Can comment out to remove mode, but should be set through soldering stars
- #define MODE_LOW 45 // Can comment out to remove mode
- #define MODE_MED 120 // Can comment out to remove mode
- //#define MODE_HIGH 16 // Can comment out to remove mode
- #define MODE_TURBO 255 // Can comment out to remove mode
- #define MODE_TURBO_LOW 140 // Level turbo ramps down to if turbo enabled
- #define TURBO_TIMEOUT 3750 // .. MODED. How many WTD ticks before before dropping down. If ticks set for 500 ms, then 240 x .5 = 120 seconds. Max value of 255 unless you change "ticks"
- // variable to uint8_t
- #define TURBO_RAMP_DOWN // By default we will start to gradually ramp down, once TURBO_TIMEOUT ticks are reached, 1 PWM_LVL each tick until reaching MODE_TURBO_LOW PWM_LVL
- // If commented out, we will step down to MODE_TURBO_LOW once TURBO_TIMEOUT ticks are reached
- #define FAST_PWM_START 1 // Above what output level should we switch from phase correct to fast-PWM?
- //#define DUAL_PWM_START 8 // Above what output level should we switch from the alternate PWM output to both PWM outputs? Comment out to disable alternate PWM output
- #define WDT_TIMEOUT 93 // Number of WTD ticks before mode is saved (about X or 3 seconds). val = X * 1000 / 32
- #define ADC_LOW 130 // When do we start ramping
- #define ADC_CRIT 120 // When do we shut the light off
- /*
- * =========================================================================
- */
- //#include <avr/pgmspace.h>
- #include <avr/io.h>
- #include <util/delay.h>
- #include <avr/interrupt.h>
- #include <avr/wdt.h>
- #include <avr/eeprom.h>
- #include <avr/sleep.h>
- //#include <avr/power.h>
- #define STAR2_PIN PB0
- #define STAR3_PIN PB4
- #define STAR4_PIN PB3
- #define PWM_PIN PB1
- #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_PRSCL 0x06 // clk/64
- #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
- /*
- * global variables
- */
- // Mode storage
- uint8_t eepos = 0;
- uint8_t eep[32];
- volatile uint8_t fade_effect = 0;
- volatile uint8_t current_mode = 0;
- // Modes (gets set when the light starts up based on stars)
- static uint8_t modes[7]; // Don't need 10, but keeping it high enough to handle all
- volatile uint8_t mode_idx = 0;
- //int mode_dir = 0; // 1 or -1. Determined when checking stars. Do we increase or decrease the idx when moving up to a higher mode.
- uint8_t mode_cnt = 0;
- uint8_t lowbatt_cnt = 0;
- void store_mode_idx(uint8_t lvl) { //central method for writing (with wear leveling)
- uint8_t oldpos=eepos;
- eepos=(eepos+1)&31; //wear leveling, use next cell
- // Write the current mode
- EEARL=eepos; EEDR=lvl; EECR=32+4; EECR=32+4+2; //WRITE //32:write only (no erase) 4:enable 2:go
- while(EECR & 2); //wait for completion
- // Erase the last mode
- EEARL=oldpos; EECR=16+4; EECR=16+4+2; //ERASE //16:erase only (no write) 4:enable 2:go
- }
- inline void read_mode_idx() {
- eeprom_read_block(&eep, 0, 32);
- while((eep[eepos] == 0xff) && (eepos < 32)) eepos++;
- if (eepos < 32) mode_idx = eep[eepos];//&0x10; What the?
- else eepos=0;
- }
- inline void next_mode() {
- mode_idx ++;
- if (mode_idx > (mode_cnt - 1)) {
- // Wrap around
- mode_idx = 0;
- }
- }
- inline void WDT_on() {
- // Setup watchdog timer to only interrupt, not reset
- cli(); // Disable interrupts
- wdt_reset(); // Reset the WDT
- WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence
- #ifdef TICKS_250MS
- WDTCR = (1<<WDTIE) | (1<<WDP2); // Enable interrupt every 250ms
- #else
- WDTCR = (1<<WDTIE) | (1<<WDP0); // Enable interrupt every 32ms **
- #endif
- sei(); // Enable interrupts
- }
- inline void WDT_off()
- {
- cli(); // Disable interrupts
- wdt_reset(); // Reset the WDT
- MCUSR &= ~(1<<WDRF); // Clear Watchdog reset flag
- WDTCR |= (1<<WDCE) | (1<<WDE); // Start time d sequence
- WDTCR = 0x00; // Disable WDT
- sei(); // Enable interrupts
- }
- inline void ADC_on() {
- ADMUX = (1 << REFS0) | (1 << ADLAR) | ADC_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2
- DIDR0 |= (1 << ADC_DIDR); // disable digital input on ADC pin to reduce power consumption
- ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL; // enable, start, prescale
- }
- inline void ADC_off() {
- ADCSRA &= ~(1<<7); //ADC off
- }
- #ifdef VOLTAGE_MON
- uint8_t low_voltage(uint8_t voltage_val) {
- // Start conversion
- ADCSRA |= (1 << ADSC);
- // Wait for completion
- while (ADCSRA & (1 << ADSC));
- // See if voltage is lower than what we were looking for
- if (ADCH < voltage_val) {
- // See if it's been low for a while
- if (++lowbatt_cnt > 8) {
- lowbatt_cnt = 0;
- return 1;
- }
- } else {
- lowbatt_cnt = 0;
- }
- return 0;
- }
- #endif
- void set_output(uint8_t pwm_lvl) {
- fade_effect = 1;
- current_mode = pwm_lvl;
- //PWM_LVL = pwm_lvl;
- // Always set alternate PWM value even if not compiled for dual output as we will use this value
- // throughout the code when trying to see what the current output level is. Setting this wont affect
- // the output when alternate output is disabled.
- ALT_PWM_LVL = pwm_lvl;
- }
- ISR(WDT_vect) {
- static uint16_t ticks = 0;
- if (ticks < 31767) ticks++;
- //PWM_LVL = PWM_LVL +1;
- // modes[mode_cnt++]
- // set_output(modes[mode_idx]);
- // current_mode = modes[mode_idx];
- if (fade_effect == 1) {
- //PWM_LVL = PWM_LVL +ceil((current_mode - PWM_LVL) / 32);
- if (modes[mode_idx] == MODE_LOW) {
- PWM_LVL = PWM_LVL +1;
- }
- else {
- PWM_LVL = PWM_LVL +3;
- }
- // PWM_LVL = PWM_LVL +3;
- if (PWM_LVL > 255) PWM_LVL = 255;
- if (PWM_LVL >= current_mode) {
- fade_effect = 0;
- }
- }
- if (ticks >= WDT_TIMEOUT) {
- // Reset the mode to the start for next time
- store_mode_idx(0);
- }
- #ifdef MODE_TURBO
- //if (ticks == TURBO_TIMEOUT && modes[mode_idx] == MODE_TURBO) { // Doesn't make any sense why this doesn't work
- if (ticks >= TURBO_TIMEOUT && mode_idx == (mode_cnt - 1) && PWM_LVL > MODE_TURBO_LOW) {
- #ifdef TURBO_RAMP_DOWN
- set_output(PWM_LVL - 1);
- #else
- // Turbo mode is always at end
- set_output(MODE_TURBO_LOW);
- //store_mode_idx(mode_idx);
- #endif
- }
- #endif
- }
- int main(void)
- {
- // All ports default to input, but turn pull-up resistors on for the stars (not the ADC input! Made that mistake already)
- PORTB = (1 << STAR2_PIN) | (1 << STAR3_PIN) | (1 << STAR4_PIN);
- // Set PWM pin to output
- DDRB = (1 << PWM_PIN);
- // Turn features on or off as needed
- #ifdef VOLTAGE_MON
- ADC_on();
- #else
- ADC_off();
- #endif
- ACSR |= (1<<7); //AC off
- // Load up the modes based on stars
- // Always load up the modes array in order of lowest to highest mode
- // 0 being low for soldered, 1 for pulled-up for not soldered
- // Moon
- // mode_cnt is increment after array value is assigned
- #ifdef MODE_MOON
- modes[mode_cnt++] = MODE_MOON;
- #endif
- #ifdef MODE_LOW
- modes[mode_cnt++] = MODE_LOW;
- #endif
- #ifdef MODE_MED
- modes[mode_cnt++] = MODE_MED;
- #endif
- #ifdef MODE_HIGH
- modes[mode_cnt++] = MODE_HIGH;
- #endif
- #ifdef MODE_TURBO
- modes[mode_cnt++] = MODE_TURBO;
- #endif
- // Not soldered (1) should enable memory
- // memory = ((PINB & (1 << STAR4_PIN)) > 0) ? 1 : 0;
- // memory = 0; // dont need memory
- // Enable sleep mode set to Idle that will be triggered by the sleep_mode() command.
- // Will allow us to go idle between WDT interrupts
- set_sleep_mode(SLEEP_MODE_IDLE);
- // Determine what mode we should fire up
- // Read the last mode that was saved
- read_mode_idx();
- if (mode_idx&0x10) {
- // Indicates we did a short press last time, go to the next mode
- // Remove short press indicator first
- mode_idx &= 0x0f;
- next_mode(); // Will handle wrap arounds
- fade_effect=1;
- } else {
- // Didn't have a short press, keep the same mode
- }
- // Store mode with short press indicator
- store_mode_idx(mode_idx|0x10);
- WDT_on();
- // Now just fire up the mode
- // Set timer to do PWM for correct output pin and set prescaler timing
- if (modes[mode_idx] > FAST_PWM_START) {
- #ifdef DUAL_PWM_START
- TCCR0A = 0b10100011; // fast-PWM both outputs
- #else
- TCCR0A = 0b00100011; // fast-PWM normal output
- #endif
- } else {
- #ifdef DUAL_PWM_START
- TCCR0A = 0b10100001; // phase corrected PWM both outputs
- #else
- TCCR0A = 0b00100001; // phase corrected PWM normal output
- #endif
- }
- TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
- // Start with minimum mode to speed up fade effects
- PWM_LVL = 15;
- set_output(modes[mode_idx]);
- // current_mode = modes[mode_idx];
- uint8_t i = 0;
- uint8_t hold_pwm;
- while(1) {
- #ifdef VOLTAGE_MON
- if (low_voltage(ADC_LOW)) {
- // We need to go to a lower level
- if (mode_idx == 0 && ALT_PWM_LVL <= modes[mode_idx]) {
- // Can't go any lower than the lowest mode
- // Wait until we hit the critical level before flashing 10 times and turning off
- while (!low_voltage(ADC_CRIT));
- i = 0;
- while (i++<10) {
- set_output(0);
- _delay_ms(250);
- set_output(modes[0]);
- _delay_ms(500);
- }
- // Turn off the light
- set_output(0);
- // Disable WDT so it doesn't wake us up
- WDT_off();
- // Power down as many components as possible
- set_sleep_mode(SLEEP_MODE_PWR_DOWN);
- sleep_mode();
- } else {
- // Flash 3 times before lowering
- hold_pwm = ALT_PWM_LVL;
- i = 0;
- while (i++<3) {
- set_output(0);
- _delay_ms(250);
- set_output(hold_pwm);
- _delay_ms(500);
- }
- // Lower the mode by half, but don't go below lowest level
- if ((ALT_PWM_LVL >> 1) < modes[0]) {
- set_output(modes[0]);
- mode_idx = 0;
- } else {
- set_output(ALT_PWM_LVL >> 1);
- }
- // See if we should change the current mode level if we've gone under the current mode.
- if (ALT_PWM_LVL < modes[mode_idx]) {
- // Lower our recorded mode
- mode_idx--;
- }
- }
- // Wait 3 seconds before lowering the level again
- _delay_ms(3000);
- }
- #endif
- sleep_mode();
- }
- return 0; // Standard Return Code
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement