aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbin/build.sh2
-rwxr-xr-xbin/flash-25.sh4
-rwxr-xr-xbin/level_calc.py220
-rw-r--r--tk-attiny.h35
4 files changed, 162 insertions, 99 deletions
diff --git a/bin/build.sh b/bin/build.sh
index 0d53b57..68f981d 100755
--- a/bin/build.sh
+++ b/bin/build.sh
@@ -8,7 +8,7 @@ export ATTINY=13
export MCU=attiny$ATTINY
export CC=avr-gcc
export OBJCOPY=avr-objcopy
-export CFLAGS="-Wall -g -Os -mmcu=$MCU -c -std=gnu99 -DATTINY=$ATTINY -I.."
+export CFLAGS="-Wall -g -Os -mmcu=$MCU -c -std=gnu99 -DATTINY=$ATTINY -I.. -I../.. -I../../.."
export OFLAGS="-Wall -g -Os -mmcu=$MCU"
export LDFLAGS=
export OBJCOPYFLAGS='--set-section-flags=.eeprom=alloc,load --change-section-lma .eeprom=0 --no-change-warnings -O ihex'
diff --git a/bin/flash-25.sh b/bin/flash-25.sh
index bb5dd34..0b24bb2 100755
--- a/bin/flash-25.sh
+++ b/bin/flash-25.sh
@@ -1,4 +1,4 @@
#/bin/sh
FIRMWARE=$1
-avrdude -c usbasp -p t25 -u -Uflash:w:$FIRMWARE -U lfuse:w:0xd2:m -U hfuse:w:0xde:m -U efuse:w:0xff:m
-# http://www.engbedded.com/cgi-bin/fcx.cgi?P_PREV=ATtiny25&P=ATtiny25&M_LOW_0x3F=0x12&M_HIGH_0x07=0x06&M_HIGH_0x20=0x00&B_SPIEN=P&B_SUT0=P&B_CKSEL3=P&B_CKSEL2=P&B_CKSEL0=P&B_BODLEVEL0=P&V_LOW=E2&V_HIGH=DE&V_EXTENDED=FF
+avrdude -c usbasp -p t25 -u -U lfuse:w:0xd2:m -U hfuse:w:0xdf:m -U efuse:w:0xff:m -Uflash:w:$FIRMWARE
+# http://www.engbedded.com/cgi-bin/fcx.cgi?P_PREV=ATtiny25&P=ATtiny25&M_LOW_0x3F=0x22&M_HIGH_0x07=0x07&M_HIGH_0x20=0x00&B_SUT1=P&B_SPIEN=P&B_CKSEL3=P&B_CKSEL2=P&B_CKSEL0=P&V_LOW=D2&V_HIGH=DF&V_EXTENDED=FF
diff --git a/bin/level_calc.py b/bin/level_calc.py
index 63926ba..de52998 100755
--- a/bin/level_calc.py
+++ b/bin/level_calc.py
@@ -4,115 +4,142 @@ import math
interactive = False
+
def main(args):
"""Calculates PWM levels for visually-linear steps.
"""
# Get parameters from the user
- v = dict(pwm_max=255, pwm2_max=255)
- questions = [
+ questions_main = [
+ (int, 'num_channels', 1, 'How many power channels?'),
(int, 'num_levels', 4, 'How many total levels do you want?'),
- (int, 'pwm_min', 6, 'Lowest visible PWM level, for moon mode:'),
- (float, 'lm_min', 0.25, 'How bright is moon mode, in lumens?'),
+ ]
+ questions_per_channel = [
+ (str, 'type', '7135', 'Type of channel - 7135 or FET:'),
+ (int, 'pwm_min', 6, 'Lowest visible PWM level:'),
+ (float, 'lm_min', 0.25, 'How bright is the lowest level, in lumens?'),
#(int, 'pwm_max', 255, 'Highest PWM level:'),
(float, 'lm_max', 1000, 'How bright is the highest level, in lumens?'),
- (str, 'dual_pwm', 'n', 'Use dual PWM? [y/n]'),
- (float, 'pwm2_min', 6, 'Second channel, lowest visible PWM level:'),
- (float, 'lm2_min', 0.25, 'Second channel, how bright is the lowest mode, in lumens?'),
- #(float, 'pwm2_max', 255, 'Second channel, highest PWM level:'),
- (float, 'lm2_max', 140, 'Second channel, how bright is maximum, in lumens?'),
]
- for typ, name, default, text in questions:
- value = get_value(text, default, args)
- if not value:
- value = default
- else:
- value = typ(value)
- v[name] = value
- if (name == 'dual_pwm' and value == 'n'):
- # skip remaining questions if not using dual PWM
- break
-
- if v['dual_pwm'] == 'y':
- dual_pwm(v)
- else:
- single_pwm(v)
+
+ def ask(questions, ans):
+ for typ, name, default, text in questions:
+ value = get_value(text, default, args)
+ if not value:
+ value = default
+ else:
+ value = typ(value)
+ setattr(ans, name, value)
+
+ answers = Empty()
+ ask(questions_main, answers)
+ channels = []
+ if not args:
+ print('Describe the channels in order of lowest to highest power.')
+ for chan_num in range(answers.num_channels):
+ if not args:
+ print('===== Channel %s =====' % (chan_num+1))
+ chan = Empty()
+ chan.pwm_max = 255
+ ask(questions_per_channel, chan)
+ channels.append(chan)
+
+ # calculate total output of all previous channels
+ for i, channel in enumerate(channels):
+ channel.prev_lm = 0.0
+ for j in range(i):
+ if channels[j].type == '7135':
+ channel.prev_lm += channels[j].lm_max
+
+ # figure out the desired PWM values
+ multi_pwm(answers, channels)
if interactive: # Wait on exit, in case user invoked us by clicking an icon
print 'Press Enter to exit:'
raw_input()
-def single_pwm(v):
- """Estimate the PWM levels for a one-channel driver."""
- visual_min = invpower(v['lm_min'])
- visual_max = invpower(v['lm_max'])
- step_size = (visual_max - visual_min) / (v['num_levels']-1)
- modes = []
- goal = visual_min
- for i in range(v['num_levels']):
- goal_lm = power(goal)
- #pwm_float = ((goal_lm / v['lm_max']) * (256-v['pwm_min'])) + v['pwm_min'] - 1
- pwm_float = (((goal_lm-v['lm_min']) / (v['lm_max']-v['lm_min'])) \
- * (255-v['pwm_min'])) \
- + v['pwm_min']
- pwm = int(round(pwm_float))
- pwm = max(min(pwm,v['pwm_max']),v['pwm_min'])
- modes.append(pwm)
- print '%i: visually %.2f (%.2f lm): %.2f/255' % (i+1, goal, goal_lm, pwm_float)
- goal += step_size
-
- print 'PWM values:', ','.join([str(i) for i in modes])
-
-def dual_pwm(v):
- """Estimate the PWM levels for a two-channel driver.
- Assume the first channel is the brighter one, and second will be used for moon/low modes.
- """
- #visual_min = math.pow(v['lm2_min'], 1.0/power)
- #visual_max = math.pow(v['lm_max'], 1.0/power)
- visual_min = invpower(v['lm2_min'])
- visual_max = invpower(v['lm_max'])
- step_size = (visual_max - visual_min) / (v['num_levels']-1)
- modes = []
- goal = visual_min
- for i in range(v['num_levels']):
- goal_lm = power(goal)
- # Up to the second channel's limit, calculate things just like a
- # single-channel driver (first channel will be zero)
- if goal_lm <= v['lm2_max']:
- pwm1_float = 0.0
- #pwm2_float = ((goal_lm / v['lm2_max']) * (256-v['pwm2_min'])) + v['pwm2_min'] - 1
- pwm2_float = (((goal_lm-v['lm2_min']) / (v['lm2_max']-v['lm2_min'])) \
- * (255-v['pwm2_min'])) \
- + v['pwm2_min']
- pwm1 = int(round(pwm1_float))
- pwm2 = int(round(pwm2_float))
- pwm2 = max(min(pwm2,v['pwm2_max']),v['pwm2_min'])
- modes.append((int(pwm1),int(pwm2)))
- # Above the second channel's limit, things get a little more
- # complicated (second channel will be 255, first channel will be
- # adjusted down by the max output of the second channel)
+
+class Empty:
+ pass
+
+
+def multi_pwm(answers, channels):
+ lm_min = channels[0].lm_min
+ # figure out the highest mode
+ lm_max = max([(c.lm_max+c.prev_lm) for c in channels])
+ if channels[-1].type == 'FET':
+ if channels[-1].lm_max > channels[-1].prev_lm:
+ # assume the highest output is with only the FET enabled
+ lm_max = channels[-1].lm_max
else:
- if len(modes) == v['num_levels'] -1: # turbo is special
- #pwm1_float = ((goal_lm / v['lm_max']) * (256-v['pwm_min'])) + v['pwm_min'] - 1
- pwm1_float = float(v['pwm_max'])
- # on a FET+7135 driver, turbo works better without the 7135
- # (we're assuming FET+7135 here)
- pwm2_float = 0.0
- else: # not the highest mode yet
- #pwm1_float = (((goal_lm-v['lm2_max']) / v['lm_max']) * (256-v['pwm_min'])) + v['pwm_min'] - 1
- pwm1_float = (((goal_lm-v['lm_min']-v['lm2_max']) / (v['lm_max']-v['lm_min'])) \
- * (255-v['pwm_min'])) \
- + v['pwm_min']
- pwm2_float = 255.0
- pwm1 = int(round(pwm1_float))
- pwm2 = int(round(pwm2_float))
- pwm1 = max(min(pwm1,v['pwm_max']),v['pwm_min'])
- modes.append((int(pwm1),int(pwm2)))
- print '%i: visually %.2f (%.2f lm): %.2f/255, %.2f/255' % (i+1, goal, goal_lm, pwm1_float, pwm2_float)
- goal += step_size
-
- print 'PWM1/FET values:', ','.join([str(i[0]) for i in modes])
- print 'PWM2/7135 values:', ','.join([str(i[1]) for i in modes])
- print 'On a non-FET driver, the last mode should be 255 on both channels.'
+ # this would be a stupid driver design
+ raise ValueError, "FET channel isn't the most powerful?"
+
+ visual_min = invpower(lm_min)
+ visual_max = invpower(lm_max)
+ step_size = (visual_max - visual_min) / (answers.num_levels-1)
+
+ # Determine ideal lumen levels
+ goals = []
+ goal_vis = visual_min
+ for i in range(answers.num_levels):
+ goal_lm = power(goal_vis)
+ goals.append((goal_vis, goal_lm))
+ goal_vis += step_size
+
+ # Calculate each channel's output for each level
+ for cnum, channel in enumerate(channels):
+ channel.modes = []
+ for i in range(answers.num_levels):
+ goal_vis, goal_lm = goals[i]
+ # This channel already is maxed out
+ if goal_lm >= (channel.lm_max + channel.prev_lm):
+ # This shouldn't happen, the FET is assumed to be the highest channel
+ if channel.type == 'FET':
+ # this would be a stupid driver design
+ raise ValueError, "FET channel isn't the most powerful?"
+
+ # Handle FET turbo specially
+ if (i == (answers.num_levels - 1)) \
+ and (cnum < (len(channels)-1)) \
+ and (channels[-1].type == 'FET'):
+ channel.modes.append(0.0)
+ # Normal non-turbo mode or non-FET turbo
+ else:
+ channel.modes.append(channel.pwm_max)
+ # This channel's active ramp-up range
+ #elif goal_lm > (channel.prev_lm + channel.lm_min):
+ elif goal_lm > channel.prev_lm:
+ # assume 7135 channels all add together
+ if channel.type == '7135':
+ diff = channel.lm_max - channel.lm_min
+ # assume FET channel gets higher output on its own
+ elif channel.type == 'FET':
+ diff = channel.lm_max - channel.prev_lm - channel.lm_min
+
+ needed = goal_lm - channel.prev_lm - channel.lm_min
+
+ ratio = needed / diff * (channel.pwm_max-channel.pwm_min)
+ pwm = max(0, ratio + channel.pwm_min)
+ channel.modes.append(pwm)
+ # This channel isn't active yet, output too low
+ else:
+ channel.modes.append(0)
+
+ # Show individual levels in detail
+ for i in range(answers.num_levels):
+ goal_vis, goal_lm = goals[i]
+ pwms = []
+ for channel in channels:
+ pwms.append('%.2f/%i' % (channel.modes[i], channel.pwm_max))
+ print('%i: visually %.2f (%.2f lm): %s' %
+ (i+1, goal_vis, goal_lm, ', '.join(pwms)))
+
+ # Show values we can paste into source code
+ for cnum, channel in enumerate(channels):
+ print('PWM%s values: %s' %
+ (cnum+1,
+ ','.join([str(int(round(i))) for i in channel.modes])))
+
def get_value(text, default, args):
"""Get input from the user, or from the command line args."""
@@ -127,6 +154,7 @@ def get_value(text, default, args):
result = result.strip()
return result
+
def power(x):
#return x**5
return x**3
@@ -134,6 +162,7 @@ def power(x):
#return math.e**x
#return 2.0**x
+
def invpower(x):
#return math.pow(x, 1/5.0)
return math.pow(x, 1/3.0)
@@ -141,6 +170,7 @@ def invpower(x):
#return math.log(x, math.e)
#return math.log(x, 2.0)
+
if __name__ == "__main__":
import sys
main(sys.argv[1:])
diff --git a/tk-attiny.h b/tk-attiny.h
index 6390416..4058fea 100644
--- a/tk-attiny.h
+++ b/tk-attiny.h
@@ -36,7 +36,7 @@
#define F_CPU 8000000UL
#define EEPSIZE 128
#define V_REF REFS1
- #define BOGOMIPS 2000
+ #define BOGOMIPS (F_CPU/3200)
#else
Hey, you need to define ATTINY.
#endif
@@ -75,6 +75,39 @@
#endif // FET_7135_LAYOUT
+#ifdef TRIPLEDOWN_LAYOUT
+/*
+ * ----
+ * Reset -|1 8|- VCC
+ * OTC -|2 7|- Voltage ADC
+ * PWM (FET) -|3 6|- PWM (6x7135)
+ * GND -|4 5|- PWM (1x7135)
+ * ----
+ */
+
+#define STAR2_PIN PB0 // If this pin isn't used for ALT_PWM
+
+#define CAP_PIN PB3 // pin 2, OTC
+#define CAP_CHANNEL 0x03 // MUX 03 corresponds with PB3 (Star 4)
+#define CAP_DIDR ADC3D // Digital input disable bit corresponding with PB3
+
+#define PWM_PIN PB1 // pin 6, 6x7135 PWM
+#define PWM_LVL OCR0B // OCR0B is the output compare register for PB1
+#define ALT_PWM_PIN PB0 // pin 5, 1x7135 PWM
+#define ALT_PWM_LVL OCR0A // OCR0A is the output compare register for PB0
+#define FET_PWM_PIN PB4 // pin 3
+#define FET_PWM_LVL OCR1B // output compare register for PB4
+
+#define VOLTAGE_PIN PB2 // pin 7, voltage ADC
+#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 TEMP_DIDR ADC4D
+#define TEMP_CHANNEL 0b00001111
+
+#endif // FET_7135_LAYOUT
+
#ifdef FERRERO_ROCHER_LAYOUT
/*
* ----