void next_rnd_mode (mpc_fun_param_t *params) /* cycle through all valid rounding modes and finish with an invalid one */ { int last = params->nbout + params->nbin - 1; int index = params->nbout + params->nbin - params->nbrnd; int carry = 1; while (carry && index <= last) { next_mode (params, index); if (!is_valid_mode (params, index) && index < last) first_mode (params, index); else carry = 0; index++; } }
int main(void) { uint8_t short_click = 0; // 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); // Start up ADC for capacitor pin DIDR0 |= (1 << CAP_DIDR); // disable digital input on ADC pin to reduce power consumption ADMUX = (1 << REFS0) | (1 << ADLAR) | CAP_CHANNEL; // 1.1v reference, left-adjust, ADC3/PB3 ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL; // enable, start, prescale // Wait for completion while (ADCSRA & (1 << ADSC)) ; // Start again as datasheet says first result is unreliable ADCSRA |= (1 << ADSC); // Wait for completion while (ADCSRA & (1 << ADSC)) ; if(ADCH > CAP_THRESHOLD) short_click = 1; // Turn off ADC ADC_off(); // Charge up the capacitor by setting CAP_PIN to output DDRB |= (1 << CAP_PIN); // Output PORTB |= (1 << CAP_PIN); // High // Set PWM pin to output DDRB = (1 << PWM_PIN); // 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 TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...) // Turn features on or off as needed #ifdef ENABLE_VOLTAGE_MONITORING ADC_on(); #else ADC_off(); #endif ACSR |= (1<<7); //AC off #ifdef ENABLE_TURNING_OFF_TURBO_TIMER turbo_timer_enabled = ((PINB & (1 << STAR2_PIN)) > 0) ? 1 : 0; #endif #ifdef ENABLE_SINGLE_MODE if((PINB & (1 << STAR2_PIN)) == 0) { modes[0] = modes[1] = #ifdef FOUR_MODES modes[2] = #endif MODE_TURBO; // Last basic mode is already MODE_TURBO - no need to waste bytes changing that } #endif #ifdef ENABLE_HIGH_TO_LOW #ifdef HIGH_TO_LOW_ON_BY_DEFAULT if((PINB & (1 << STAR3_PIN)) > 0) #else if((PINB & (1 << STAR3_PIN)) == 0) #endif revert_modes(); #endif #ifdef ENABLE_TACTICAL_MODE #ifdef TACTICAL_MODE_ON_BY_DEFAULT if((PINB & (1 << STAR2_PIN)) > 0) #else if((PINB & (1 << STAR2_PIN)) == 0) #endif { modes[1] = MODE_TURBO; if(modes[0] == MODE_TURBO) // Single mode { modes[2] = #ifdef FOUR_MODES modes[3] = #endif MODE_TURBO; } else { modes[0] = MODE_STROBE; modes[2] = MODE_LOW; #ifdef FOUR_MODES modes[3] = MODE_LOWLOW; #endif } } #endif // 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); // Mode memory handling block { uint8_t n_short_clicks = 0; // Determine what mode we should fire up // Read the last mode that was saved mode_idx = read_stored_idx(); // Handle short press counter n_short_clicks = (mode_idx & 0xf0); mode_idx &= 0x0f; if(short_click) // This click was short { if(n_short_clicks == MODE_INDEX_TURBO_RAMPDOWN) // TODO: Test if this logic works in practice, or do we need to use always double tap from turbo? n_short_clicks = 0; // Keep turbo, reset counter else next_mode(n_short_clicks); // Will handle wrap arounds store_mode_idx(mode_idx | ((n_short_clicks < HIDDEN_MODE_THRESHOLD) ? n_short_clicks+0x10 : n_short_clicks)); } else // Didn't have a short press, keep the same mode, stored without short click counter { if((PINB & (1 << STAR2_PIN)) == 0) // Tactical or Single or No memory, reset to 1st mode mode_idx = 0; store_mode_idx(mode_idx); } } // Start watchdog timer (used for storing the mode after delay, turbo timer, and voltage monitoring) WDT_on(); // Now just fire up the mode selected_pwm = modes[mode_idx]; // Note: Actual PWM can be less than selected PWM (e.g. in case of low voltage) if(selected_pwm < PWM_MIN) // Hidden blinky modes adjusted_pwm = PWM_MAX; // All blinky modes initially with full power else adjusted_pwm = selected_pwm; // block for main loop { uint8_t ii = 0; // Loop counter, used by multiple branches #ifdef ENABLE_BEACONS uint8_t beacon_background = PWM_OFF; #endif while(1) { #ifdef ENABLE_VOLTAGE_MONITORING if(adjusted_pwm == PWM_OFF) // Voltage monitoring signaled us to turn off the light -> break out of the main loop break; #endif PWM_LVL = adjusted_pwm; // must be set inside loop, is volatile & might have changed because of voltage monitoring switch(selected_pwm) { case MODE_STROBE: // Disorienting alternating strobe #ifdef NORMAL_STROBE // 51ms = ~19.5Hz, ~60% DC _delay_ms(25); PWM_LVL = 0; _delay_ms(26); #endif #ifdef ALTERNATING_STROBE _delay_ms(31); PWM_LVL = 0; if(ii < 19) // 51ms = ~19.5Hz, ~60% DC _delay_ms(20); else if(ii < 32) // 77ms = ~13Hz, ~40% DC _delay_ms(46); else ii = 255; #endif #ifdef RANDOM_STROBE { // 77ms = 13Hz, 51ms = 19.5Hz / 40-60% DC ii = (5 * ii) + 128; _delay_ms(31); PWM_LVL = 0; _delay_ms(ii > 127 ? 46 : 20); } #endif break; case MODE_MOTION_STOPPING_STROBE: // 8Hz, 1.6% DC _delay_ms(2); PWM_LVL = 0; _delay_ms(123); break; #ifdef ENABLE_SOS case MODE_SOS: if(ii / 3 == 1) _delay_ms(600); // Dash for 'O' (3xDot) else _delay_ms(200); // Dot for 'S' PWM_LVL = 0; switch(ii) { default: _delay_ms(200); // Pause inside a letter (1xDot) break; case 2: case 5: _delay_ms(600); // Pause between letters (3xDot) break; case 8: _delay_ms(2500); // Pause between "words" (should be 7xDot, but I like it longer) ii = 255; break; } break; #endif #ifdef ENABLE_BEACONS case MODE_BEACON_WITH_BACKGROUND: beacon_background = PWM_BEACON_BACKGROUND; goto beacon_common; case MODE_SLOW_BEACON_WITH_BACKGROUND: beacon_background = PWM_SLOW_BEACON_BACKGROUND; // no break - fall through to beacon code case MODE_BEACON: case MODE_ALPINE_DISTRESS_BEACON: beacon_common: _delay_ms(50); PWM_LVL = beacon_background; _delay_ms(950); if(selected_pwm == MODE_ALPINE_DISTRESS_BEACON) { if(ii > 5) { _delay_ms(59000); ii = 255; } else _delay_ms(9000); } else if(selected_pwm == MODE_SLOW_BEACON_WITH_BACKGROUND) _delay_ms(1500); #endif break; default: sleep_mode(); break; } ii++; // Loop counter, used by multiple branches } } #ifdef ENABLE_VOLTAGE_MONITORING // // Critically low voltage -> Turn off the light // // Disable WDT so it doesn't wake us up from sleep WDT_off(); // Would be nice to blink a couple of times with lowest brightness to notify the user, but not implemented due space restrictions // Turn the light off PWM_LVL = 0; // Disable ADC so it doesn't consume power ADC_off(); // Power down as many components as possible set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Deep sleep until powered off - consumes ~110uA during the deep sleep while(1) sleep_mode(); #endif return 0; // Standard Return Code -> would return to idle loop with interrupts disabled. }
int main(void) { DDRB |= (1 << PWM_PIN); // Set PWM pin to output, enable main channel TCCR0A = FAST; // Set timer to do PWM for correct output pin and set prescaler timing TCCR0B = 0x01; // Set timer to do PWM for correct output pin and set prescaler timing restore_state(); // Read config values and saved state count_modes(); // Enable the current mode group // check button press time, unless we're in group selection mode if (mode_idx != GROUP_SELECT_MODE) { if ( we_did_a_fast_press() ) { // sram hasn't decayed yet, must have been a short press increment_fast_presses(); next_mode(); // Will handle wrap arounds } else { // Long press, keep the same mode reset_fast_presses(); if( !memory_is_enabled() ) {mode_idx = 0;} // if memory is turned off, set mode_idx to 0 } } save_mode(); // Turn features on or off as needed #ifdef VOLTAGE_MON ADC_on(); #else ADC_off(); #endif uint8_t output; uint8_t i = 0; uint16_t ticks = 0; #ifdef TURBO_RAMP_DOWN uint8_t adj_output = 255; #endif #ifdef VOLTAGE_MON uint8_t lowbatt_cnt = 0; uint8_t voltage; ADCSRA |= (1 << ADSC); // Make sure voltage reading is running for later #endif if(mode_idx > num_modes) { output = mode_idx; } // special modes, override output else { output = modes[mode_idx]; } while(1) { if (fast_presses[0] >= 12) { // Config mode if 12 or more fast presses _delay_s(); // wait for user to stop fast-pressing button reset_fast_presses(); // exit this mode after one use toggle_mode(GROUP_SELECT_MODE, 1); // Enter the mode group selection mode? toggle_options((options ^ 0b00010000), 2); // memory toggle_options((options ^ 0b00100000), 3); // hidden blinkies toggle_options((options ^ 0b01000000), 4); // hidden battcheck toggle_options((options ^ 0b10000000), 5); // turbo timer toggle_options(DEFAULTS, 6); // reset to defaults } else if (output == STROBE) { // 10Hz tactical strobe for(i=0;i<8;i++) { set_level(RAMP_SIZE); _delay_ms(33); set_output(0); _delay_ms(67); } } else if (output == BEACON) { set_level(RAMP_SIZE); _delay_ms(10); set_output(0); _delay_s(); _delay_s(); } else if (output == SOS) { // dot = 1 unit, dash = 3 units, space betwen parts of a letter = 1 unit, space between letters = 3 units #define SOS_SPEED 200 // these speeds aren't perfect, but they'll work for the [never] times they'll actually get used blink(3, SOS_SPEED); // 200 on, 400 off, 200 on, 400 off, 200 on, 400 off _delay_ms(SOS_SPEED); // wait for 200 blink(3, SOS_SPEED*5/2); // 500 on, 1000 off, 500 on, 1000 off, 500 on, 1000 off (techically too long on that last off beat, but oh well) blink(3, SOS_SPEED); // 200 on, 400 off, 200 on, 400 off, 200 on, 400 off _delay_s(); _delay_s(); } else if (output == BATT_CHECK) { blink(battcheck(), BLINK_SPEED/4); _delay_s(); _delay_s(); } else if (output == GROUP_SELECT_MODE) { mode_idx = 0; // exit this mode after one use for(i=0; i<NUM_MODEGROUPS; i++) { toggle_options(((options & 0b11110000) | i), i+1); } _delay_s(); } else { if ((output == TURBO) && ( ttimer_is_enabled() ) && (ticks > (TURBO_MINUTES * TICKS_PER_MINUTE))) { #ifdef TURBO_RAMP_DOWN if (adj_output > TURBO_LOWER) { adj_output = adj_output - 2; } set_output(adj_output); #else set_output(TURBO_LOWER); #endif } else { ticks ++; // count ticks for turbo timer set_level(output); } _delay_ms(500); // Otherwise, just sleep. } reset_fast_presses(); #ifdef VOLTAGE_MON if (ADCSRA & (1 << ADIF)) { // if a voltage reading is ready voltage = ADCH; // get the waiting value if (voltage < ADC_LOW) { // See if voltage is lower than what we were looking for lowbatt_cnt ++; } else { lowbatt_cnt = 0; } if (lowbatt_cnt >= 8) { // See if it's been low for a while, and maybe step down //set_output(0); _delay_ms(100); // blink on step-down: if (output > RAMP_SIZE) { // blinky modes output = RAMP_SIZE / 2; // step down from blinky modes to medium } else if (output > 1) { // regular solid mode output = output - 1; // step down from solid modes somewhat gradually } else { // Already at the lowest mode set_output(0); // Turn off the light set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Power down as many components as possible sleep_mode(); } set_level(output); lowbatt_cnt = 0; _delay_s(); // Wait before lowering the level again } ADCSRA |= (1 << ADSC); // Make sure conversion is running for next time through } #endif // ifdef VOLTAGE_MON } }
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) // (stars not used) //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 // Enable sleep mode set to Idle that will be triggered by the sleep_mode() command. // Will allow us to go idle between WDT interrupts (which we're not using anyway) set_sleep_mode(SLEEP_MODE_IDLE); // Determine what mode we should fire up // Read the last mode that was saved if (noinit_decay) // not short press, forget mode { noinit_mode = 0; mode_idx = 0; } else { // short press, advance to next mode mode_idx = noinit_mode; next_mode(); noinit_mode = mode_idx; } // set noinit data for next boot noinit_decay = 0; // set PWM mode if (modes[mode_idx] < FAST_PWM_START) { // Set timer to do PWM for correct output pin and set prescaler timing TCCR0A = 0x21; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23 } else { // 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 } TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...) // Now just fire up the mode PWM_LVL = modes[mode_idx]; uint8_t i = 0; uint8_t j = 0; uint8_t strobe_len = 0; #ifdef VOLTAGE_MON uint8_t lowbatt_cnt = 0; uint8_t voltage; #endif while(1) { if(mode_idx < SOLID_MODES) { // Just stay on at a given brightness sleep_mode(); } else if (mode_idx < BATT_CHECK_MODE) { PWM_LVL = 0; get_voltage(); _delay_ms(200); // the first reading is junk #ifdef BATTCHECK_VpT uint8_t result = battcheck(); blink(result >> 5, BLINK_SPEED/8); _delay_ms(BLINK_SPEED); blink(1,5); _delay_ms(BLINK_SPEED*3/2); blink(result & 0b00011111, BLINK_SPEED/8); #else blink(battcheck()); #endif // BATTCHECK_VpT _delay_ms(2000); // wait at least 2 seconds between readouts } else if (mode_idx < SINGLE_BEACON_MODES) { // heartbeat flasher
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); // 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 TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...) // Turn features on or off as needed #ifdef ENABLE_VOLTAGE_MONITORING ADC_on(); #else ADC_off(); #endif ACSR |= (1<<7); //AC off #ifdef ENABLE_TURNING_OFF_MEMORY // Soldering Star 4 disables memory #ifdef MEMORY_ON_BY_DEFAULT memory_enabled = ((PINB & (1 << STAR4_PIN)) > 0) ? 1 : 0; #else memory_enabled = ((PINB & (1 << STAR4_PIN)) == 0) ? 1 : 0; #endif #endif #ifdef ENABLE_TURNING_OFF_TURBO_TIMER turbo_timer_enabled = ((PINB & (1 << STAR2_PIN)) > 0) ? 1 : 0; #endif #ifdef ENABLE_SINGLE_MODE if((PINB & (1 << STAR2_PIN)) == 0) { modes[0] = modes[1] = #ifdef FOUR_MODES modes[2] = #endif MODE_TURBO; // Last basic mode is already MODE_TURBO - no need to waste bytes changing that } #endif #ifdef ENABLE_TACTICAL_MODE #ifdef TACTICAL_MODE_ON_BY_DEFAULT if((PINB & (1 << STAR2_PIN)) > 0) #else if((PINB & (1 << STAR2_PIN)) == 0) #endif { #ifdef ENABLE_TURNING_OFF_MEMORY if(memory_enabled) // Single mode { modes[0] = modes[1] = #ifdef FOUR_MODES modes[2] = #endif MODE_TURBO; // Last basic mode is already MODE_TURBO - no need to waste bytes changing that } else #endif { modes[0] = MODE_STROBE; modes[1] = MODE_TURBO; modes[2] = MODE_LOW; #ifdef FOUR_MODES modes[3] = MODE_LOWLOW; #endif wdt_timeout = 1; } } #endif #ifdef ENABLE_HIGH_TO_LOW #ifdef HIGH_TO_LOW_ON_BY_DEFAULT if((PINB & (1 << STAR3_PIN)) > 0) #else if((PINB & (1 << STAR3_PIN)) == 0) #endif revert_modes(); #endif // 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); // Mode memory handling block { uint8_t short_clicks = 0; // Determine what mode we should fire up // Read the last mode that was saved mode_idx = read_stored_idx(); // Handle short press indicator short_clicks = (mode_idx & 0xf0); mode_idx &= 0x0f; if(short_clicks) // One or more short clicks { // Indicates we did a short press last time, go to the next mode next_mode(short_clicks); // Will handle wrap arounds } // else: Didn't have a short press, keep the same mode, nothing to do // Store mode with short press indicator store_mode_idx(mode_idx | ((short_clicks < HIDDEN_MODE_THRESHOLD) ? short_clicks+0x10 : short_clicks)); } // Start watchdog timer (used for storing the mode after delay, turbo timer, and voltage monitoring) WDT_on(); // Now just fire up the mode selected_pwm = modes[mode_idx]; // Note: Actual PWM can be less than selected PWM (e.g. in case of low voltage) if(selected_pwm < PWM_MIN) // Hidden blinky modes adjusted_pwm = PWM_MAX; // All blinky modes initially with full power else adjusted_pwm = selected_pwm; // block for main loop { uint8_t ii = 0; // Loop counter, used by multiple branches #ifdef ENABLE_BEACONS uint8_t beacon_background = PWM_OFF; #endif while(1) { #ifdef ENABLE_VOLTAGE_MONITORING if(adjusted_pwm == PWM_OFF) // Voltage monitoring signaled us to turn off the light -> break out of the main loop break; #endif PWM_LVL = adjusted_pwm; // must be set inside loop, is volatile & might have changed because of voltage monitoring switch(selected_pwm) { case MODE_STROBE: // Disorienting alternating strobe #ifdef NORMAL_STROBE _delay_ms(20); PWM_LVL = 0; _delay_ms(40); #endif #ifdef ALTERNATING_STROBE _delay_ms(20); PWM_LVL = 0; if(ii < 22) // 60ms = ~16.7Hz, ~33% DC _delay_ms(40); else if(ii < 36) // 111ms = ~9Hz, ~18% DC _delay_ms(90); else ii = 255; #endif #ifdef RANDOM_STROBE { // 77ms = 13Hz, 51ms = 19.5Hz / 40-60% DC ii = (5 * ii) + 128; _delay_ms(31); PWM_LVL = 0; _delay_ms(ii > 127 ? 46 : 20); } #endif break; case MODE_MOTION_STOPPING_STROBE: // 10Hz, 2% DC _delay_ms(2); PWM_LVL = 0; _delay_ms(98); break; #ifdef ENABLE_SOS case MODE_SOS: if(ii / 3 == 1) _delay_ms(600); // Dash for 'O' (3xDot) else _delay_ms(200); // Dot for 'S' PWM_LVL = 0; switch(ii) { default: _delay_ms(200); // Pause inside a letter (1xDot) break; case 2: case 5: _delay_ms(600); // Pause between letters (3xDot) break; case 8: _delay_ms(2500); // Pause between "words" (should be 7xDot, but I like it longer) ii = 255; break; } break; #endif #ifdef ENABLE_BEACONS case MODE_BEACON_WITH_BACKGROUND: case MODE_SLOW_BEACON_WITH_BACKGROUND: #ifdef FOUR_MODES beacon_background = PWM_SLOW_BEACON_BACKGROUND; #else switch(selected_pwm) { case MODE_BEACON_WITH_BACKGROUND: beacon_background = PWM_BEACON_BACKGROUND; break; case MODE_SLOW_BEACON_WITH_BACKGROUND: beacon_background = PWM_SLOW_BEACON_BACKGROUND; break; } #endif // no break - fall through to beacon code case MODE_BEACON: case MODE_ALPINE_DISTRESS_BEACON: _delay_ms(50); PWM_LVL = beacon_background; _delay_ms(950); if(selected_pwm == MODE_ALPINE_DISTRESS_BEACON) { if(ii > 5) { _delay_ms(59000); ii = 255; } else _delay_ms(9000); } else if(selected_pwm == MODE_SLOW_BEACON_WITH_BACKGROUND) _delay_ms(1500); #endif break; default: sleep_mode(); break; } ii++; // Loop counter, used by multiple branches } } #ifdef ENABLE_VOLTAGE_MONITORING // // Critically low voltage -> Turn off the light // // Disable WDT so it doesn't wake us up from sleep WDT_off(); // Would be nice to blink a couple of times with lowest brightness to notify the user, but not implemented due space restrictions // Turn the light off PWM_LVL = 0; // Disable ADC so it doesn't consume power ADC_off(); // Power down as many components as possible set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Deep sleep until powered off - consumes ~110uA during the deep sleep while(1) sleep_mode(); #endif return 0; // Standard Return Code -> would return to idle loop with interrupts disabled. }