Advertisement
gchart

Babka-25

Jan 15th, 2018
40
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 16.21 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.  *     5: 100, 35, 10, 1
  33.  *     6: 100, 35, 5
  34.  *     7: 100, 20
  35.  *     8: 100, Strobe
  36.  *     9: 100, 20, Strobe
  37.  *     10: 100, 35, 5, Strobe
  38.  *     11: 5, 35, 100, Strobe
  39.  *     12: 20, 100, Strobe
  40.  *   2: Memory Toggle
  41.  *   3: Hidden Strobe Modes Toggle (Strobe, Beacon, SOS)
  42.  *   4: Hidden Batt Check Toggle (if strobes are enabled, Batt Check appears at the end of them)
  43.  *   5: Turbo Timer Toggle (turbo steps down to 50%)
  44.  *   6: Reset to default configuration
  45.  *  
  46.  * ====================================================================
  47.  *
  48.  * "Biscotti" firmware (attiny13a version of "Bistro")
  49.  * This code runs on a single-channel driver with attiny13a MCU.
  50.  * It is intended specifically for nanjg 105d drivers from Convoy.
  51.  *
  52.  * Copyright (C) 2015 Selene Scriven
  53.  *
  54.  * This program is free software: you can redistribute it and/or modify
  55.  * it under the terms of the GNU General Public License as published by
  56.  * the Free Software Foundation, either version 3 of the License, or
  57.  * (at your option) any later version.
  58.  *
  59.  * This program is distributed in the hope that it will be useful,
  60.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  61.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  62.  * GNU General Public License for more details.
  63.  *
  64.  * You should have received a copy of the GNU General Public License
  65.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  66.  *
  67.  *
  68.  * ATTINY13 Diagram
  69.  *         ----
  70.  *       -|1  8|- VCC
  71.  *       -|2  7|- Voltage ADC
  72.  *       -|3  6|-
  73.  *   GND -|4  5|- PWM (Nx7135)
  74.  *         ----
  75.  *
  76.  * FUSES
  77.  *    I use these fuse settings on attiny13
  78.  *    Low:  0x75
  79.  *    High: 0xff
  80.  *
  81.  * CALIBRATION
  82.  *
  83.  *   To find out what values to use, flash the driver with battcheck.hex
  84.  *   and hook the light up to each voltage you need a value for.  This is
  85.  *   much more reliable than attempting to calculate the values from a
  86.  *   theoretical formula.
  87.  *
  88.  *   Same for off-time capacitor values.  Measure, don't guess.
  89.  */
  90. // Choose your MCU here, or in the build script
  91. #define ATTINY 25
  92. #define NANJG_LAYOUT  // specify an I/O pin layout
  93. // Also, assign I/O pins in this file:
  94. #include "../tk-attiny.h"
  95.  
  96. #define DEFAULTS 0b00010010  // 3 modes with memory, no blinkies, no battcheck
  97.  
  98. #define FAST  0xA3        // fast PWM both channels
  99. #define PHASE 0xA1        // phase-correct PWM both channels
  100. #define VOLTAGE_MON      // Comment out to disable LVP
  101. #define USE_BATTCHECK      // Enable battery check mode
  102. //#define BATTCHECK_4bars    // up to 4 blinks
  103. #define BATTCHECK_8bars  // up to 8 blinks
  104.  
  105. #define RAMP_SIZE  6
  106. #define RAMP_PWM   2, 13, 26, 51, 89, 255  // 1, 5, 10, 20, 35, 100%
  107.  
  108. #define BLINK_BRIGHTNESS    3 // output to use for blinks on battery check (and other modes)
  109. #define BLINK_SPEED      750 // ms per normal-speed blink
  110.  
  111. #define TURBO_LOWER 128  // the PWM level to use when stepping down
  112.  
  113. #define TURBO    RAMP_SIZE  // Convenience code for turbo mode
  114.  
  115. #define FIRST_SELECT_MODE 252
  116. #define GROUP_SELECT_MODE 252
  117. #define TEMP_CAL_MODE 253
  118.  
  119. // These need to be in sequential order, no gaps.
  120. // Make sure to update FIRST_BLINKY and LAST_BLINKY as needed.
  121. // BATT_CHECK should be the last blinky, otherwise the non-blinky
  122. // mode groups will pick up any blinkies after BATT_CHECK
  123. #define FIRST_BLINKY 240
  124. #define STROBE  240
  125. #define BEACON 241
  126. #define SOS 242
  127. #define BATT_CHECK 243
  128. #define LAST_BLINKY 243
  129.  
  130. // thermal step-down
  131. #define TEMPERATURE_MON
  132. //#define TEMP_CHANNEL 0b00001111  // this should be defined in tk-attiny.h, but typically isn't for the NANJG layout
  133.  
  134. // Calibrate voltage and OTC in this file:
  135. #include "../tk-calibration.h"
  136.  
  137. /*
  138.  * =========================================================================
  139.  */
  140.  
  141. // Ignore a spurious warning, we did the cast on purpose
  142. #pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
  143.  
  144. #include <avr/pgmspace.h>
  145. #include <avr/interrupt.h>
  146. #include <avr/eeprom.h>
  147. #include <avr/sleep.h>
  148. #include <string.h>
  149.  
  150. #define OWN_DELAY          // Don't use stock delay functions.
  151. #define USE_DELAY_S      // Also use _delay_s(), not just _delay_ms()
  152. #include "../tk-delay.h"
  153.  
  154. #include "../tk-voltage.h"
  155.  
  156. uint8_t modegroup = 0; // which mode group (set above in #defines)
  157. uint8_t mode_idx = 0;     // current or last-used mode number
  158. uint8_t eepos = 0;
  159.  
  160. #ifdef TEMPERATURE_MON
  161. uint8_t maxtemp = 79;      // temperature step-down threshold
  162. #endif
  163.  
  164. // (needs to be remembered while off, but only for up to half a second)
  165. uint8_t fast_presses __attribute__ ((section (".noinit")));
  166. // added "cold_start" as a separate variable so we have more bits to check
  167. // which means an exponentially lower chance of losing the "noinit lottery"
  168. uint8_t cold_start __attribute__ ((section (".noinit")));
  169.  
  170. // number of regular modes in current mode group
  171. uint8_t solid_modes;
  172.  
  173. // Each group *must* be 4 values long, can define up to 15 groups
  174. #define GROUP_SIZE 4
  175. #define NUM_MODEGROUPS 12
  176. PROGMEM const uint8_t modegroups[] = {
  177.     6, 0, 0, 0,
  178.     4, 6, 0, 0,
  179.     2, 5, 6, 0,
  180.     1, 3, 5, 6,
  181.     6, 5, 3, 1,
  182.     6, 5, 2, 0,
  183.     6, 4, 0, 0,
  184.     6, STROBE, 0, 0,
  185.     6, 4, STROBE, 0,
  186.   6, 5, 2, STROBE,
  187.     2, 5, 6, STROBE,
  188.     4, 6, STROBE, 0,
  189. };
  190. uint8_t modes[] = { 0,0,0,0, };  // make sure this is long enough...
  191.  
  192. // Modes (gets set when the light starts up based on saved config values)
  193. PROGMEM const uint8_t ramp_PWM[]  = { RAMP_PWM };
  194.  
  195. void save_mode() {  // save the current mode index (with wear leveling)
  196.     eeprom_write_byte((uint8_t *)(eepos), 0xff);     // erase old state
  197.     eepos = (eepos+1) & ((EEPSIZE/2)-1);  // wear leveling, use next cell
  198.     eeprom_write_byte((uint8_t *)(eepos), mode_idx);  // save current state
  199. }
  200.  
  201. #define OPT_modegroup (EEPSIZE-1)
  202. #define OPT_maxtemp (EEPSIZE-2)
  203. void save_state() {  // central method for writing complete state
  204.     save_mode();
  205.     eeprom_write_byte((uint8_t *)OPT_modegroup, modegroup);
  206. #ifdef TEMPERATURE_MON
  207.   eeprom_write_byte((uint8_t *)OPT_maxtemp, maxtemp);
  208. #endif
  209. }
  210.  
  211. inline void reset_state() {
  212.     mode_idx = 0;
  213.     modegroup = DEFAULTS;  // 3 brightness levels with memory
  214.     save_state();
  215. }
  216.  
  217. inline void restore_state() {
  218.     uint8_t eep;
  219.  
  220.     uint8_t first = 1;
  221.     // find the mode index data
  222.     for(eepos=0; eepos<(EEPSIZE-6); eepos++) {
  223.         eep = eeprom_read_byte((const uint8_t *)eepos);
  224.         if (eep != 0xff) {
  225.             mode_idx = eep;
  226.             first = 0;
  227.             break;
  228.         }
  229.     }
  230.     // if no mode_idx was found, assume this is the first boot
  231.     if (first) {
  232.         reset_state();
  233.         return;
  234.     }
  235.  
  236.     // load other config values
  237.     modegroup = eeprom_read_byte((uint8_t *)OPT_modegroup);
  238. #ifdef TEMPERATURE_MON
  239.   maxtemp   = eeprom_read_byte((uint8_t *)OPT_maxtemp);
  240. #endif
  241. }
  242.  
  243. inline void next_mode() {
  244.     mode_idx += 1;
  245.    
  246.     if(fast_presses == 3 && mode_idx <= solid_modes) {  // triple-tap from a solid mode
  247.         if((modegroup >> 5) & 0x01) mode_idx = FIRST_BLINKY; // if blinkies enabled, go to first one
  248.         else if((modegroup >> 6) & 0x01) mode_idx = BATT_CHECK; // else if battcheck enabled, go to it
  249.     }
  250.    
  251.     // if we hit the end of the solid modes or the blinkies (or battcheck if disabled), go to first solid mode
  252.     if ( (mode_idx == solid_modes) || (mode_idx > LAST_BLINKY) || (mode_idx == BATT_CHECK && !((modegroup >> 6) & 0x01))) {
  253.         mode_idx = 0;
  254.     }
  255.    
  256. }
  257.  
  258. inline void count_modes() {
  259.     // Determine how many modes we have.
  260.     uint8_t my_modegroup = (modegroup) & 0b00001111;
  261.     uint8_t *dest;
  262.     //const uint8_t *src = modegroups + (my_modegroup<<3); // use this for groups of 8
  263.     const uint8_t *src = modegroups + (my_modegroup<<2); // use this for groups of 4
  264.     dest = modes;
  265.     // Figure out how many modes are in this group
  266.     //for(solid_modes = 0; solid_modes <= my_modegroup; solid_modes++, src++ )
  267.     for(solid_modes=0; (solid_modes<GROUP_SIZE) && pgm_read_byte(src); solid_modes++, src++ )
  268.       { *dest++ = pgm_read_byte(src); }
  269.  
  270. }
  271.  
  272. inline void set_output(uint8_t pwm1) {
  273.     PWM_LVL = pwm1;
  274. }
  275.  
  276. void set_level(uint8_t level) {
  277.     //if (level == 1) { TCCR0A = PHASE; TCCR0B = 0x02; }
  278.     if (level <= 2) {
  279.         TCCR0A = PHASE;
  280.         if (level == 1) { TCCR0B = 0x02; }
  281.     }
  282.     set_output(pgm_read_byte(ramp_PWM  + level - 1));
  283. }
  284.  
  285. void blink(uint8_t val, uint16_t speed)
  286. {
  287.     for (; val>0; val--)
  288.     {
  289.         set_level(BLINK_BRIGHTNESS);
  290.         _delay_ms(speed);
  291.         set_output(0);
  292.         _delay_ms(speed);
  293.         _delay_ms(speed);
  294.     }
  295. }
  296.  
  297. void toggle(uint8_t *var, uint8_t value, uint8_t num) {
  298.     blink(num, BLINK_SPEED/4);  // indicate which option number this is
  299.     uint8_t temp = *var;
  300.     *var = value;
  301.     save_state();
  302.     blink(32, 500/32); // "buzz" for a while to indicate the active toggle window
  303.    
  304.     // if the user didn't click, reset the value and return
  305.     *var = temp;
  306.     save_state();
  307.     _delay_s();
  308. }
  309.  
  310. #ifdef TEMPERATURE_MON
  311. uint8_t get_temperature() {
  312.     ADC_on_temperature();
  313.     // average a few values; temperature is noisy
  314.     uint16_t temp = 0;
  315.     uint8_t i;
  316.     get_voltage();
  317.     for(i=0; i<16; i++) {
  318.         temp += get_voltage();
  319.         _delay_ms(5);
  320.     }
  321.     temp >>= 4;
  322.     return temp;
  323. }
  324. #endif  // TEMPERATURE_MON
  325.  
  326. int main(void)
  327. {
  328.  
  329.     DDRB |= (1 << PWM_PIN);  // Set PWM pin to output, enable main channel
  330.     TCCR0A = FAST; // Set timer to do PWM for correct output pin and set prescaler timing
  331.     TCCR0B = 0x01; // Set timer to do PWM for correct output pin and set prescaler timing
  332.  
  333.     restore_state(); // Read config values and saved state
  334.  
  335.     count_modes(); // Enable the current mode group
  336.  
  337.      // check button press time, unless we're in a selection mode (mode group or turbo timer selection)
  338.     if (mode_idx < FIRST_SELECT_MODE) {
  339.         if ( !cold_start ) { // ram hasn't decayed yet (it's still all zeroes), must have been a short press
  340.             fast_presses++;
  341.             next_mode(); // Will handle wrap arounds
  342.         } else { // Long press, keep the same mode
  343.             fast_presses = 0;
  344.             cold_start = 0;
  345.             if(!((modegroup >> 4) & 0x01)) {mode_idx = 0;}  // if memory is turned off, set mode_idx to 0
  346.         }
  347.     }
  348.     save_mode();
  349.  
  350.     // Turn features on or off as needed
  351.     #ifdef VOLTAGE_MON
  352.     ADC_on();
  353.     #else
  354.     ADC_off();
  355.     #endif
  356.    
  357.     #ifdef TEMPERATURE_MON
  358.   uint8_t overheat_count = 0;
  359.     #endif
  360.  
  361.     uint8_t output;
  362.     uint8_t desired_output;
  363.     uint8_t i = 0;
  364.     //uint16_t ticks = 0;
  365.    
  366. #ifdef VOLTAGE_MON
  367.     uint8_t lowbatt_cnt = 0;
  368.     uint8_t voltage;
  369.     ADCSRA |= (1 << ADSC); // Make sure voltage reading is running for later
  370. #endif
  371.  
  372.     if(mode_idx > solid_modes) { output = mode_idx; }  // special modes, override output
  373.     else { output = modes[mode_idx]; }
  374.     desired_output = output;
  375.    
  376.     while(1) {
  377.         if (fast_presses >= 12) {  // Config mode if 12 or more fast presses
  378.             _delay_s();    // wait for user to stop fast-pressing button
  379.             fast_presses = 0; // exit this mode after one use
  380.  
  381.             toggle(&mode_idx, GROUP_SELECT_MODE, 1); // Enter the mode group selection mode?
  382.             toggle(&modegroup, (modegroup ^ 0b00010000), 2); // memory
  383.             toggle(&modegroup, (modegroup ^ 0b00100000), 3); // hidden blinkies
  384.             toggle(&modegroup, (modegroup ^ 0b01000000), 4); // hidden battcheck
  385. #ifdef TEMPERATURE_MON
  386.       toggle(&mode_idx, TEMP_CAL_MODE, 5); // Enter temperature calibration mode?
  387. #endif
  388.             toggle(&modegroup, DEFAULTS, 6); // reset to defaults
  389.         }
  390.         else if (output == STROBE) { // 10Hz tactical strobe
  391.             for(i=0;i<8;i++) {
  392.                 set_level(RAMP_SIZE);
  393.                 _delay_ms(33);
  394.                 set_output(0);
  395.                 _delay_ms(67);
  396.             }
  397.         }
  398.         else if (output == BEACON) {
  399.             set_level(RAMP_SIZE);
  400.             _delay_ms(10);
  401.             set_output(0);
  402.             _delay_s(); _delay_s();
  403.         }
  404.         else if (output == SOS) {  // dot = 1 unit, dash = 3 units, space betwen parts of a letter = 1 unit, space between letters = 3 units
  405.             #define SOS_SPEED 200  // these speeds aren't perfect, but they'll work for the [never] times they'll actually get used
  406.             blink(3, SOS_SPEED); // 200 on, 400 off, 200 on, 400 off, 200 on, 400 off
  407.             _delay_ms(SOS_SPEED);  // wait for 200
  408.             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)
  409.             blink(3, SOS_SPEED);  // 200 on, 400 off, 200 on, 400 off, 200 on, 400 off
  410.             _delay_s(); _delay_s();
  411.         }
  412.         else if (output == BATT_CHECK) {
  413.              blink(battcheck(), BLINK_SPEED/4);
  414.              _delay_s(); _delay_s();
  415.         }
  416.         else if (output == GROUP_SELECT_MODE) {
  417.             mode_idx = 0; // exit this mode after one use
  418.  
  419.             for(i=0; i<NUM_MODEGROUPS; i++) {
  420.                 toggle(&modegroup, ((modegroup & 0b11110000) | i), i+1);
  421.             }
  422.             _delay_s();
  423.         }
  424. #ifdef TEMP_CAL_MODE
  425.     else if (output == TEMP_CAL_MODE) {
  426.             mode_idx = 0; // exit this mode after one use
  427.  
  428.             // Allow the user to turn off thermal regulation if they want
  429.             maxtemp = 255;
  430.             save_state();
  431.             set_level(RAMP_SIZE/4);  // start somewhat dim during turn-off-regulation mode
  432.             _delay_s(); _delay_s();
  433.  
  434.             // run at highest output level, to generate heat
  435.             set_level(RAMP_SIZE);
  436.  
  437.             // measure, save, wait...  repeat
  438.             while(1) {
  439.                     maxtemp = get_temperature();
  440.                     save_state();
  441.                     _delay_s(); _delay_s();
  442.             }
  443.     }
  444. #endif  // TEMP_CAL_MODE
  445.         else {
  446.             set_level(output);
  447.  
  448.             //ticks ++; // count ticks for turbo timer
  449.             //uint8_t turbo_enabled = ((modegroup >> 8) & 0x01);
  450.             //if ((output == TURBO) && (turbo_enabled) && (ticks > (TURBO_MINUTES * TICKS_PER_MINUTE))) set_output(TURBO_LOWER);
  451. #ifdef TEMPERATURE_MON
  452.             uint8_t temp = get_temperature();
  453.  
  454.             // step down? (or step back up?)
  455.             if (temp >= maxtemp) {
  456.                     overheat_count++;
  457.                     // reduce noise, and limit the lowest step-down level
  458.                     if ((overheat_count > 15) && (output > 2)) {
  459.                             output--;
  460.                             //_delay_ms(5000);  // don't ramp down too fast
  461.                             overheat_count = 0;  // don't ramp down too fast
  462.                     }
  463.             } else {
  464.                     // if we're not overheated, ramp up to the user-requested level
  465.                     overheat_count = 0;
  466.                     if ((temp < maxtemp - 2) && (output < desired_output)) {
  467.                             output++;
  468.                     }
  469.             }
  470.             set_level(output);
  471.  
  472.             ADC_on();  // return to voltage mode
  473. #endif
  474.  
  475.             _delay_ms(500);  // Otherwise, just sleep.
  476.  
  477.         }
  478.         fast_presses = 0;
  479.         cold_start = 0;
  480. #ifdef VOLTAGE_MON
  481.         if (ADCSRA & (1 << ADIF)) {  // if a voltage reading is ready
  482.             voltage = ADCH;  // get the waiting value
  483.    
  484.             if (voltage < ADC_LOW) { // See if voltage is lower than what we were looking for
  485.                 lowbatt_cnt ++;
  486.             } else {
  487.                 lowbatt_cnt = 0;
  488.             }
  489.            
  490.             if (lowbatt_cnt >= 8) {  // See if it's been low for a while, and maybe step down
  491.                 //set_output(0);  _delay_ms(100); // blink on step-down:
  492.  
  493.                 if (output > RAMP_SIZE) {  // blinky modes
  494.                     output = RAMP_SIZE / 2; // step down from blinky modes to medium
  495.                 } else if (output > 1) {  // regular solid mode
  496.                     output = output - 1; // step down from solid modes somewhat gradually
  497.                 } else { // Already at the lowest mode
  498.                     set_output(0); // Turn off the light
  499.                     set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Power down as many components as possible
  500.                     sleep_mode();
  501.                 }
  502.                 set_level(output);
  503.                 lowbatt_cnt = 0;
  504.                 _delay_s(); // Wait before lowering the level again
  505.             }
  506.  
  507.             ADCSRA |= (1 << ADSC); // Make sure conversion is running for next time through
  508.         }
  509. #endif  // ifdef VOLTAGE_MON
  510.     }
  511. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement