Advertisement
gchart

Babka V3

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