Advertisement
gchart

Babka firmware by gChart

Oct 24th, 2016
292
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 14.79 KB | None | 0 0
  1. /*
  2.  
  3.  * ====================================================================
  4.  *  "Babka" firmware by gChart (Gabriel Hart)
  5.  *  Last updated: 2016-11-10
  6.  *
  7.  *  This firmware is based on "Biscotti" by ToyKeeper and is intended
  8.  *  for single-channel attiny13a drivers such as Qlite and nanjc105d.
  9.  * ====================================================================
  10.  *
  11.  * 12 or more fast presses > Configuration mode
  12.  *
  13.  * Short tap on first config option goes to Group Mode selection
  14.  * Short tap on second config option goes to Turbo Mode selection
  15.  *
  16.  * If hidden modes are enabled, perform a triple-click to enter the hidden modes.
  17.  * Tap once to advance through the hidden modes.  Once you advance past the last
  18.  * hidden mode, you will return to the first non-hidden mode.
  19.  *
  20.  * Battery Check will blink out between 0 and 8 blinks.  0 blinks means your battery
  21.  * is dead.  1 blink is 1-12% battery remaining.  2 blinks is 13-24% remaining...
  22.  * 8 blinks is 88-100% remaining.  Essentially, the number of blinks is how many
  23.  * eighths of the battery remain: 1/8, 2/8, 3/8... 8/8.  After blinking, it will
  24.  * pause for two seconds and then repeat itself.
  25.  *
  26.  * Configuration Menu
  27.  *   1: Mode Groups:
  28.  *     1: 100
  29.  *     2: 20, 100
  30.  *     3: 5, 35, 100
  31.  *     4: 1, 10, 35, 100
  32.  *   2: Memory Toggle
  33.  *   3: Hidden Strobe Modes Toggle (Strobe, Beacon, SOS)
  34.  *   4: Hidden Batt Check Toggle (if strobes are enabled, Batt Check appears at the end of them)
  35.  *   5: Turbo timer: disabled
  36.  *   6: Turbo timer: 2 minutes (turbo steps down to 50%)
  37.  *   7: Turbo timer: 5 minutes (turbo steps down to 50%)
  38.  *   8: Reset to default configuration
  39.  *  
  40.  * ====================================================================
  41.  *
  42.  * "Biscotti" firmware (attiny13a version of "Bistro")
  43.  * This code runs on a single-channel driver with attiny13a MCU.
  44.  * It is intended specifically for nanjg 105d drivers from Convoy.
  45.  *
  46.  * Copyright (C) 2015 Selene Scriven
  47.  *
  48.  * This program is free software: you can redistribute it and/or modify
  49.  * it under the terms of the GNU General Public License as published by
  50.  * the Free Software Foundation, either version 3 of the License, or
  51.  * (at your option) any later version.
  52.  *
  53.  * This program is distributed in the hope that it will be useful,
  54.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  55.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  56.  * GNU General Public License for more details.
  57.  *
  58.  * You should have received a copy of the GNU General Public License
  59.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  60.  *
  61.  *
  62.  * ATTINY13 Diagram
  63.  *         ----
  64.  *       -|1  8|- VCC
  65.  *       -|2  7|- Voltage ADC
  66.  *       -|3  6|-
  67.  *   GND -|4  5|- PWM (Nx7135)
  68.  *         ----
  69.  *
  70.  * FUSES
  71.  *    I use these fuse settings on attiny13
  72.  *    Low:  0x75
  73.  *    High: 0xff
  74.  *
  75.  * CALIBRATION
  76.  *
  77.  *   To find out what values to use, flash the driver with battcheck.hex
  78.  *   and hook the light up to each voltage you need a value for.  This is
  79.  *   much more reliable than attempting to calculate the values from a
  80.  *   theoretical formula.
  81.  *
  82.  *   Same for off-time capacitor values.  Measure, don't guess.
  83.  */
  84. // Choose your MCU here, or in the build script
  85. #define ATTINY 13
  86. #define NANJG_LAYOUT  // specify an I/O pin layout
  87. // Also, assign I/O pins in this file:
  88. #include "../tk-attiny.h"
  89.  
  90. //#define DEFAULTS 0b00000110  // 3 modes with memory, no blinkies, no battcheck, turbo timer disabled
  91. //#define DEFAULTS 0b00100110  // 3 modes with memory, no blinkies, no battcheck, 2 min turbo timer
  92. #define DEFAULTS 0b01000110  // 3 modes with memory, no blinkies, no battcheck, 5 min turbo timer
  93.  
  94. #define FAST  0xA3        // fast PWM both channels
  95. #define PHASE 0xA1        // phase-correct PWM both channels
  96. #define VOLTAGE_MON      // Comment out to disable LVP
  97. #define USE_BATTCHECK      // Enable battery check mode
  98. //#define BATTCHECK_4bars    // up to 4 blinks
  99. #define BATTCHECK_8bars  // up to 8 blinks
  100.  
  101. #define RAMP_SIZE  6
  102. // for the lowest value, a 4 wouldn't turn on an XP-L HI (but did ok with a 219C)... so let's kick it up to a 5
  103. //#define RAMP_PWM   4, 13, 26, 51, 89, 255  // 1, 5, 10, 20, 35, 100%
  104. #define RAMP_PWM   5, 13, 26, 51, 89, 255  // 1, 5, 10, 20, 35, 100%
  105.  
  106. #define BLINK_BRIGHTNESS    3 // output to use for blinks on battery check (and other modes)
  107. #define BLINK_SPEED      750 // ms per normal-speed blink
  108.  
  109. #define TICKS_PER_MINUTE 120 // used for Turbo Timer timing
  110. #define TURBO_LOWER 128  // the PWM level to use when stepping down
  111.  
  112. #define TURBO    RAMP_SIZE  // Convenience code for turbo mode
  113.  
  114. #define FIRST_SELECT_MODE 252
  115. #define GROUP_SELECT_MODE 252
  116. #define TURBO_SELECT_MODE 253
  117.  
  118. // These need to be in sequential order, no gaps.
  119. // Make sure to update FIRST_BLINKY and LAST_BLINKY as needed.
  120. // BATT_CHECK should be the last blinky, otherwise the non-blinky
  121. // mode groups will pick up any blinkies after BATT_CHECK
  122. #define FIRST_BLINKY 240
  123. #define STROBE  240
  124. #define BEACON 241
  125. #define SOS 242
  126. #define BATT_CHECK 243
  127. #define LAST_BLINKY 243
  128.  
  129. // Calibrate voltage and OTC in this file:
  130. #include "../tk-calibration.h"
  131.  
  132. /*
  133.  * =========================================================================
  134.  */
  135.  
  136. // Ignore a spurious warning, we did the cast on purpose
  137. #pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
  138.  
  139. #include <avr/pgmspace.h>
  140. #include <avr/interrupt.h>
  141. #include <avr/eeprom.h>
  142. #include <avr/sleep.h>
  143. #include <string.h>
  144.  
  145. #define OWN_DELAY          // Don't use stock delay functions.
  146. #define USE_DELAY_S      // Also use _delay_s(), not just _delay_ms()
  147. #include "../tk-delay.h"
  148.  
  149. #include "../tk-voltage.h"
  150.  
  151. uint8_t modegroup = 0; // which mode group (set above in #defines)
  152. uint8_t mode_idx = 0;     // current or last-used mode number
  153. uint8_t eepos = 0;
  154.  
  155. // (needs to be remembered while off, but only for up to half a second)
  156. uint8_t fast_presses __attribute__ ((section (".noinit")));
  157.  
  158. // number of regular modes in current mode group
  159. uint8_t solid_modes;
  160.  
  161. // Each group *must* be 4 values long
  162. #define NUM_MODEGROUPS 4
  163. //Group 1 should always have 1 mode, group 2 should have 2 modes, etc, otherwise count_modes will break
  164. PROGMEM const uint8_t modegroups[] = {
  165.     6, 0, 0, 0,
  166.     4, 6, 0, 0,
  167.     2, 5, 6, 0,
  168.     1, 3, 5, 6,
  169. };
  170. uint8_t modes[] = { 0,0,0,0, };  // make sure this is long enough...
  171.  
  172. // Modes (gets set when the light starts up based on saved config values)
  173. PROGMEM const uint8_t ramp_PWM[]  = { RAMP_PWM };
  174.  
  175. void save_mode() {  // save the current mode index (with wear leveling)
  176.     eeprom_write_byte((uint8_t *)(eepos), 0xff);     // erase old state
  177.     eepos = (eepos+1) & ((EEPSIZE/2)-1);  // wear leveling, use next cell
  178.     eeprom_write_byte((uint8_t *)(eepos), mode_idx);  // save current state
  179. }
  180.  
  181. #define OPT_modegroup (EEPSIZE-1)
  182. void save_state() {  // central method for writing complete state
  183.     save_mode();
  184.     eeprom_write_byte((uint8_t *)OPT_modegroup, modegroup);
  185. }
  186.  
  187. inline void reset_state() {
  188.     mode_idx = 0;
  189.     modegroup = DEFAULTS;  // 3 brightness levels with memory
  190.     save_state();
  191. }
  192.  
  193. void restore_state() {
  194.     uint8_t eep;
  195.  
  196.     uint8_t first = 1;
  197.     // find the mode index data
  198.     for(eepos=0; eepos<(EEPSIZE-6); eepos++) {
  199.         eep = eeprom_read_byte((const uint8_t *)eepos);
  200.         if (eep != 0xff) {
  201.             mode_idx = eep;
  202.             first = 0;
  203.             break;
  204.         }
  205.     }
  206.     // if no mode_idx was found, assume this is the first boot
  207.     if (first) {
  208.         reset_state();
  209.         return;
  210.     }
  211.  
  212.     // load other config values
  213.     modegroup = eeprom_read_byte((uint8_t *)OPT_modegroup);
  214. }
  215.  
  216. inline void next_mode() {
  217.     mode_idx += 1;
  218.    
  219.     if(fast_presses == 3 && mode_idx <= solid_modes) {  // triple-tap from a solid mode
  220.         if((modegroup >> 3) & 0x01) mode_idx = FIRST_BLINKY; // if blinkies enabled, go to first one
  221.         else if((modegroup >> 4) & 0x01) mode_idx = BATT_CHECK; // else if battcheck enabled, go to it
  222.     }
  223.    
  224.     // if we hit the end of the solid modes or the blinkies (or battcheck if disabled), go to first solid mode
  225.     if ( (mode_idx == solid_modes) || (mode_idx > LAST_BLINKY) || (mode_idx == BATT_CHECK && !((modegroup >> 4) & 0x01))) {
  226.         mode_idx = 0;
  227.     }
  228.    
  229. }
  230.  
  231. void count_modes() {
  232.     // Determine how many modes we have.
  233.     uint8_t my_modegroup = (modegroup) & 0x03;
  234.     uint8_t *dest;
  235.     const uint8_t *src = modegroups + (my_modegroup<<2);
  236.     dest = modes;
  237.     // Figure out how many modes are in this group
  238.     for(solid_modes = 0; solid_modes <= my_modegroup; solid_modes++, src++ )
  239.       { *dest++ = pgm_read_byte(src); }
  240.  
  241. }
  242.  
  243. inline void set_output(uint8_t pwm1) {
  244.     PWM_LVL = pwm1;
  245. }
  246.  
  247. void set_level(uint8_t level) {
  248.     if (level == 1) { TCCR0A = PHASE; } // phase corrected PWM is 0xA1 for PB1, fast-PWM is 0xA3
  249.     set_output(pgm_read_byte(ramp_PWM  + level - 1));
  250. }
  251.  
  252. void blink(uint8_t val, uint16_t speed)
  253. {
  254.     for (; val>0; val--)
  255.     {
  256.         set_level(BLINK_BRIGHTNESS);
  257.         _delay_ms(speed);
  258.         set_output(0);
  259.         _delay_ms(speed);
  260.         _delay_ms(speed);
  261.     }
  262. }
  263.  
  264. void toggle(uint8_t *var, uint8_t value, uint8_t num) {
  265.     blink(num, BLINK_SPEED/4);  // indicate which option number this is
  266.     uint8_t temp = *var;
  267.     *var = value;
  268.     save_state();
  269.     blink(32, 500/32); // "buzz" for a while to indicate the active toggle window
  270.    
  271.     // if the user didn't click, reset the value and return
  272.     *var = temp;
  273.     save_state();
  274.     _delay_s();
  275. }
  276.  
  277. int main(void)
  278. {
  279.  
  280.     DDRB |= (1 << PWM_PIN);  // Set PWM pin to output, enable main channel
  281.     TCCR0A = FAST; // Set timer to do PWM for correct output pin and set prescaler timing
  282.     TCCR0B = 0x01; // Set timer to do PWM for correct output pin and set prescaler timing
  283.  
  284.     restore_state(); // Read config values and saved state
  285.  
  286.     count_modes(); // Enable the current mode group
  287.  
  288.      // check button press time, unless we're in a selection mode (mode group or turbo timer selection)
  289.     if (mode_idx < FIRST_SELECT_MODE) {
  290.         if (fast_presses < 0x20) { // Short press, go to the next mode
  291.             fast_presses = (fast_presses+1) & 0x1f; // We don't care what the fast_presses value is as long as it's over 15
  292.             next_mode(); // Will handle wrap arounds
  293.         } else { // Long press, keep the same mode
  294.             fast_presses = 0;
  295.             if(!((modegroup >> 2) & 0x01)) {mode_idx = 0;}  // if memory is turned off, set mode_idx to 0
  296.         }
  297.     }
  298.     save_mode();
  299.  
  300.     // Turn features on or off as needed
  301.     #ifdef VOLTAGE_MON
  302.     ADC_on();
  303.     #else
  304.     ADC_off();
  305.     #endif
  306.  
  307.     uint8_t output;
  308.     uint8_t i = 0;
  309.     uint16_t ticks = 0;
  310.  
  311.     uint16_t turbo_ticks = 65535; // maxed out, effectively no turbo timer
  312.     //if(((modegroup >> 5) & 0x03) == 1) turbo_ticks = 2 * TICKS_PER_MINUTE; // 2 minutes
  313.     //if(((modegroup >> 5) & 0x03) == 2) turbo_ticks = 5 * TICKS_PER_MINUTE; // 5 minutes
  314.     // Using these (instead of the two lines above) saves 8 bytes, but they're sketchy... assumes we'll never have values > 2
  315.     if(modegroup & 0b00100000) turbo_ticks = 2  * TICKS_PER_MINUTE;  // assuming valid values are 0, 1, and 2... turbo timer = 1
  316.     if(modegroup & 0b01000000) turbo_ticks = 5  * TICKS_PER_MINUTE;  // assuming valid values are 0, 1, and 2... turbo timer = 1
  317.     //if((modegroup >> 5) & 0x01) turbo_ticks = 2  * TICKS_PER_MINUTE;  // assuming valid values are 0, 1, and 2... turbo timer = 1
  318.     //if((modegroup >> 6) & 0x01) turbo_ticks = 5  * TICKS_PER_MINUTE;  // assuming valid values are 0, 1, and 2... turbo timer = 2
  319.    
  320. #ifdef VOLTAGE_MON
  321.     uint8_t lowbatt_cnt = 0;
  322.     uint8_t voltage;
  323.     ADCSRA |= (1 << ADSC); // Make sure voltage reading is running for later
  324. #endif
  325.  
  326.     if(mode_idx > solid_modes) { output = mode_idx; }  // special modes, override output
  327.     else { output = modes[mode_idx]; }
  328.    
  329.     while(1) {
  330.         if (fast_presses >= 12) {  // Config mode if 12 or more fast presses
  331.             _delay_s();    // wait for user to stop fast-pressing button
  332.             fast_presses = 0; // exit this mode after one use
  333.  
  334.             toggle(&mode_idx, GROUP_SELECT_MODE, 1); // Enter the mode group selection mode?
  335.             toggle(&modegroup, (modegroup ^ 0b00000100), 2); // memory
  336.             toggle(&modegroup, (modegroup ^ 0b00001000), 3); // hidden blinkies
  337.             toggle(&modegroup, (modegroup ^ 0b00010000), 4); // hidden battcheck
  338.             toggle(&modegroup, (modegroup & 0b10011111), 5); // turbo timer = 0 (disabled)
  339.             toggle(&modegroup, (modegroup & 0b10011111) | (1 << 5), 6); // turbo timer = 1 (2 minutes)
  340.             toggle(&modegroup, (modegroup & 0b10011111) | (2 << 5), 7); // turbo timer = 2 (5 minutes)
  341.             toggle(&modegroup, DEFAULTS, 8); // reset to defaults
  342.         }
  343.         else if (output == STROBE) { // 10Hz tactical strobe
  344.             for(i=0;i<8;i++) {
  345.                 set_level(RAMP_SIZE);
  346.                 _delay_ms(33);
  347.                 set_output(0);
  348.                 _delay_ms(67);
  349.             }
  350.         }
  351.         else if (output == BEACON) {
  352.             set_level(RAMP_SIZE);
  353.             _delay_ms(10);
  354.             set_output(0);
  355.             _delay_s(); _delay_s();
  356.         }
  357.         else if (output == SOS) {  // dot = 1 unit, dash = 3 units, space betwen parts of a letter = 1 unit, space between letters = 3 units
  358.             #define SOS_SPEED 200  // these speeds aren't perfect, but they'll work for the [never] times they'll actually get used
  359.             blink(3, SOS_SPEED); // 200 on, 400 off, 200 on, 400 off, 200 on, 400 off
  360.             _delay_ms(SOS_SPEED);  // wait for 200
  361.             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)
  362.             blink(3, SOS_SPEED);  // 200 on, 400 off, 200 on, 400 off, 200 on, 400 off
  363.             _delay_s(); _delay_s();
  364.         }
  365.         else if (output == BATT_CHECK) {
  366.              blink(battcheck(), BLINK_SPEED/4);
  367.              _delay_s(); _delay_s();
  368.         }
  369.         else if (output == GROUP_SELECT_MODE) {
  370.             mode_idx = 0; // exit this mode after one use
  371.  
  372.             for(i=0; i<NUM_MODEGROUPS; i++) {
  373.                 toggle(&modegroup, ((modegroup & 0b11111100) | i), i+1);
  374.             }
  375.             _delay_s();
  376.         }
  377.         else {
  378.             set_level(output);
  379.  
  380.             ticks ++; // count ticks for turbo timer
  381.  
  382.             if ((output == TURBO) && (ticks > turbo_ticks)) set_output(TURBO_LOWER);
  383.  
  384.             _delay_ms(500);  // Otherwise, just sleep.
  385.  
  386.         }
  387.         fast_presses = 0;
  388. #ifdef VOLTAGE_MON
  389.         if (ADCSRA & (1 << ADIF)) {  // if a voltage reading is ready
  390.             voltage = ADCH;  // get the waiting value
  391.    
  392.             if (voltage < ADC_LOW) { // See if voltage is lower than what we were looking for
  393.                 lowbatt_cnt ++;
  394.             } else {
  395.                 lowbatt_cnt = 0;
  396.             }
  397.            
  398.             if (lowbatt_cnt >= 8) {  // See if it's been low for a while, and maybe step down
  399.                 //set_output(0);  _delay_ms(100); // blink on step-down:
  400.  
  401.                 if (output > RAMP_SIZE) {  // blinky modes
  402.                     output = RAMP_SIZE / 2; // step down from blinky modes to medium
  403.                 } else if (output > 1) {  // regular solid mode
  404.                     output = output - 1; // step down from solid modes somewhat gradually
  405.                 } else { // Already at the lowest mode
  406.                     set_output(0); // Turn off the light
  407.                     set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Power down as many components as possible
  408.                     sleep_mode();
  409.                 }
  410.                 set_level(output);
  411.                 lowbatt_cnt = 0;
  412.                 _delay_s(); // Wait before lowering the level again
  413.             }
  414.  
  415.             ADCSRA |= (1 << ADSC); // Make sure conversion is running for next time through
  416.         }
  417. #endif  // ifdef VOLTAGE_MON
  418.     }
  419. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement