Diff
checker
テキスト
テキスト
画像
ドキュメント
Excel
フォルダ
Legal
Enterprise
デスクトップ
料金
ログイン
Diffchecker デスクトップのダウンロード
テキスト比較
2 つのテキスト ファイルの違いを見つける
ツール
履歴
ライブエディター
空白の変更を非表示
未変更行を折りたたむ
折り返しなし
レイアウト
分割
統合
比較精度
スマート
単語
文字
テキストスタイル
外観を変更
シンタックスハイライト
構文を選択
無視
テキスト変換
最初の差分へ移動
入力を編集
Diffchecker Desktop
Diffcheckerを実行する最も安全な方法。Diffchecker Desktopアプリを入手:あなたの差分はコンピューターから出ることはありません!
Desktopを入手
Untitled diff
作成日
11 年前
差分は期限切れになりません
クリア
エクスポート
共有
説明
38 削除
行
合計
削除
文字
合計
削除
この機能を引き続き使用するには、アップグレードしてください
Diff
checker
Pro
価格を見る
736 行
すべてコピー
33 追加
行
合計
追加
文字
合計
追加
この機能を引き続き使用するには、アップグレードしてください
Diff
checker
Pro
価格を見る
733 行
すべてコピー
/*
/*
* "Bistro" firmware
* "Bistro" firmware
* This code runs on a single-channel or dual-channel driver (FET+7135)
* This code runs on a single-channel or dual-channel driver (FET+7135)
* with an attiny25/45/85 MCU and a capacitor to measure offtime (OTC).
* with an attiny25/45/85 MCU and a capacitor to measure offtime (OTC).
*
*
* Copyright (C) 2015 Selene Scriven
* Copyright (C) 2015 Selene Scriven
*
*
* This program is free software: you can redistribute it and/or modify
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*
*
* ATTINY25/45/85 Diagram
* ATTINY25/45/85 Diagram
* ----
* ----
* -|1 8|- VCC
* -|1 8|- VCC
* OTC -|2 7|- Voltage ADC
* OTC -|2 7|- Voltage ADC
* Star 3 -|3 6|- PWM (FET, optional)
* Star 3 -|3 6|- PWM (FET, optional)
* GND -|4 5|- PWM (1x7135)
* GND -|4 5|- PWM (1x7135)
* ----
* ----
*
*
* FUSES
* FUSES
* I use these fuse settings on attiny25
* I use these fuse settings on attiny25
* Low: 0xd2
* Low: 0xd2
* High: 0xde
* High: 0xde
* Ext: 0xff
* Ext: 0xff
*
*
* For more details on these settings:
* For more details on these settings:
* 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
* 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
*
*
* STARS
* STARS
* Star 3 = unused
* Star 3 = unused
*
*
* CALIBRATION
* CALIBRATION
*
*
* To find out what values to use, flash the driver with battcheck.hex
* To find out what values to use, flash the driver with battcheck.hex
* and hook the light up to each voltage you need a value for. This is
* and hook the light up to each voltage you need a value for. This is
* much more reliable than attempting to calculate the values from a
* much more reliable than attempting to calculate the values from a
* theoretical formula.
* theoretical formula.
*
*
* Same for off-time capacitor values. Measure, don't guess.
* Same for off-time capacitor values. Measure, don't guess.
*/
*/
// Choose your MCU here, or in the build script
// Choose your MCU here, or in the build script
//#define ATTINY 13
//#define ATTINY 13
//#define ATTINY 25
//#define ATTINY 25
// FIXME: make 1-channel vs 2-channel power a single #define option
// FIXME: make 1-channel vs 2-channel power a single #define option
#define FET_7135_LAYOUT // specify an I/O pin layout
#define FET_7135_LAYOUT // specify an I/O pin layout
// Also, assign I/O pins in this file:
// Also, assign I/O pins in this file:
#include "tk-attiny.h"
#include "tk-attiny.h"
/*
/*
* =========================================================================
* =========================================================================
* Settings to modify per driver
* Settings to modify per driver
*/
*/
// FIXME: make 1-channel vs 2-channel power a single #define option
// FIXME: make 1-channel vs 2-channel power a single #define option
//#define FAST 0x23 // fast PWM channel 1 only
//#define FAST 0x23 // fast PWM channel 1 only
//#define PHASE 0x21 // phase-correct PWM channel 1 only
//#define PHASE 0x21 // phase-correct PWM channel 1 only
#define FAST 0xA3 // fast PWM both channels
#define FAST 0xA3 // fast PWM both channels
#define PHASE 0xA1 // phase-correct PWM both channels
#define PHASE 0xA1 // phase-correct PWM both channels
#define VOLTAGE_MON // Comment out to disable LVP
#define VOLTAGE_MON // Comment out to disable LVP
#define OFFTIM3 // Use short/med/long off-time presses
#define OFFTIM3 // Use short/med/long off-time presses
// instead of just short/long
// instead of just short/long
コピー
コピー済み
コピー
コピー済み
// ../../bin/level_calc.py 64 1 10 1
4
00 y 3 0.
25
140
// ../../bin/level_calc.py 64 1 10 1
3
00 y 3 0.
23
140
#define RAMP_SIZE 64
#define RAMP_SIZE 64
// log curve
// log curve
//#define RAMP_7135 3,3,3,3,3,3,4,4,4,4,4,5,5,5,6,6,7,7,8,9,10,11,12,13,15,16,18,21,23,27,30,34,39,44,50,57,65,74,85,97,111,127,145,166,190,217,248,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
//#define RAMP_7135 3,3,3,3,3,3,4,4,4,4,4,5,5,5,6,6,7,7,8,9,10,11,12,13,15,16,18,21,23,27,30,34,39,44,50,57,65,74,85,97,111,127,145,166,190,217,248,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
//#define RAMP_FET 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,6,11,17,23,30,39,48,59,72,86,103,121,143,168,197,255
//#define RAMP_FET 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,6,11,17,23,30,39,48,59,72,86,103,121,143,168,197,255
// x**2 curve
// x**2 curve
//#define RAMP_7135 3,5,8,12,17,24,32,41,51,63,75,90,105,121,139,158,178,200,223,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
//#define RAMP_7135 3,5,8,12,17,24,32,41,51,63,75,90,105,121,139,158,178,200,223,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
//#define RAMP_FET 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,6,9,12,16,19,22,26,30,33,37,41,45,50,54,59,63,68,73,78,84,89,94,100,106,111,117,123,130,136,142,149,156,162,169,176,184,191,198,206,214,221,255
//#define RAMP_FET 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,6,9,12,16,19,22,26,30,33,37,41,45,50,54,59,63,68,73,78,84,89,94,100,106,111,117,123,130,136,142,149,156,162,169,176,184,191,198,206,214,221,255
// x**3 curve
// x**3 curve
コピー
コピー済み
コピー
コピー済み
#define RAMP_7135 3,3,4,5,
7
,8,10,
13,16,20,25,30,36,42,50,59,68,78,90
,10
3,116
,13
1,148,165,184,204,226,249
,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
#define RAMP_7135 3,3,4,5,
6
,8,10,
12,15,19,23,28,33,40,47,55,63,73,84,95
,10
8,122
,13
7,153,171,190,210,232
,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
#define RAMP_FET 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,4,7,10,13,16,20,24,28,32,36,41,46,51,56,61,67,73,80,86,93,100,107
,11
5
,12
3,131
,13
9
,14
8
,15
7
,16
6
,17
6
,18
6
,19
6
,20
7
,21
8
,255
#define RAMP_FET 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,2,5,8,11,14,18,22,26,30,34,39,44,49,54,59,65,71,77,84,91,98,105
,11
3
,12
1,129
,13
7
,14
6
,15
5
,16
4
,17
4
,18
4
,19
4
,20
5
,21
6
,255
// x**5 curve
// x**5 curve
//#define RAMP_7135 3,3,3,4,4,5,5,6,7,8,10,11,13,15,18,21,24,28,33,38,44,50,57,66,75,85,96,108,122,137,154,172,192,213,237,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
//#define RAMP_7135 3,3,3,4,4,5,5,6,7,8,10,11,13,15,18,21,24,28,33,38,44,50,57,66,75,85,96,108,122,137,154,172,192,213,237,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
//#define RAMP_FET 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,6,9,13,17,21,25,30,35,41,47,53,60,67,75,83,91,101,111,121,132,144,156,169,183,198,213,255
//#define RAMP_FET 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,6,9,13,17,21,25,30,35,41,47,53,60,67,75,83,91,101,111,121,132,144,156,169,183,198,213,255
// uncomment to ramp up/down to a mode instead of jumping directly
// uncomment to ramp up/down to a mode instead of jumping directly
#define SOFT_START
#define SOFT_START
// Enable battery indicator mode?
// Enable battery indicator mode?
#define USE_BATTCHECK
#define USE_BATTCHECK
// Choose a battery indicator style
// Choose a battery indicator style
//#define BATTCHECK_4bars // up to 4 blinks
//#define BATTCHECK_4bars // up to 4 blinks
//#define BATTCHECK_8bars // up to 8 blinks
//#define BATTCHECK_8bars // up to 8 blinks
#define BATTCHECK_VpT // Volts + tenths
#define BATTCHECK_VpT // Volts + tenths
// output to use for blinks on battery check (and other modes)
// output to use for blinks on battery check (and other modes)
#define BLINK_BRIGHTNESS RAMP_SIZE/4
#define BLINK_BRIGHTNESS RAMP_SIZE/4
// ms per normal-speed blink
// ms per normal-speed blink
#define BLINK_SPEED 500
#define BLINK_SPEED 500
// Hidden modes are *before* the lowest (moon) mode, and should be specified
// Hidden modes are *before* the lowest (moon) mode, and should be specified
// in reverse order. So, to go backward from moon to turbo to strobe to
// in reverse order. So, to go backward from moon to turbo to strobe to
// battcheck, use BATTCHECK,STROBE,TURBO .
// battcheck, use BATTCHECK,STROBE,TURBO .
#define HIDDENMODES BIKING_STROBE,BATTCHECK,POLICE_STROBE,TURBO
#define HIDDENMODES BIKING_STROBE,BATTCHECK,POLICE_STROBE,TURBO
#define TURBO RAMP_SIZE // Convenience code for turbo mode
#define TURBO RAMP_SIZE // Convenience code for turbo mode
#define BATTCHECK 254 // Convenience code for battery check mode
#define BATTCHECK 254 // Convenience code for battery check mode
#define GROUP_SELECT_MODE 253
#define GROUP_SELECT_MODE 253
#define TEMP_CAL_MODE 252
#define TEMP_CAL_MODE 252
// Uncomment to enable tactical strobe mode
// Uncomment to enable tactical strobe mode
//#define STROBE 251 // Convenience code for strobe mode
//#define STROBE 251 // Convenience code for strobe mode
// Uncomment to unable a 2-level stutter beacon instead of a tactical strobe
// Uncomment to unable a 2-level stutter beacon instead of a tactical strobe
#define BIKING_STROBE 250 // Convenience code for biking strobe mode
#define BIKING_STROBE 250 // Convenience code for biking strobe mode
// comment out to use minimal version instead (smaller)
// comment out to use minimal version instead (smaller)
#define FULL_BIKING_STROBE
#define FULL_BIKING_STROBE
//#define RAMP 249 // ramp test mode for tweaking ramp shape
//#define RAMP 249 // ramp test mode for tweaking ramp shape
#define POLICE_STROBE 248
#define POLICE_STROBE 248
//#define RANDOM_STROBE 247
//#define RANDOM_STROBE 247
// thermal step-down
// thermal step-down
#define TEMPERATURE_MON
#define TEMPERATURE_MON
// Calibrate voltage and OTC in this file:
// Calibrate voltage and OTC in this file:
#include "tk-calibration.h"
#include "tk-calibration.h"
/*
/*
* =========================================================================
* =========================================================================
*/
*/
// Ignore a spurious warning, we did the cast on purpose
// Ignore a spurious warning, we did the cast on purpose
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
#include <avr/pgmspace.h>
#include <avr/pgmspace.h>
//#include <avr/io.h>
//#include <avr/io.h>
//#include <avr/interrupt.h>
//#include <avr/interrupt.h>
#include <avr/eeprom.h>
#include <avr/eeprom.h>
#include <avr/sleep.h>
#include <avr/sleep.h>
//#include <avr/power.h>
//#include <avr/power.h>
#include <string.h>
#include <string.h>
#define OWN_DELAY // Don't use stock delay functions.
#define OWN_DELAY // Don't use stock delay functions.
#define USE_DELAY_S // Also use _delay_s(), not just _delay_ms()
#define USE_DELAY_S // Also use _delay_s(), not just _delay_ms()
#include "tk-delay.h"
#include "tk-delay.h"
#include "tk-voltage.h"
#include "tk-voltage.h"
#ifdef RANDOM_STROBE
#ifdef RANDOM_STROBE
#include "tk-random.h"
#include "tk-random.h"
#endif
#endif
/*
/*
* global variables
* global variables
*/
*/
// Config option variables
// Config option variables
#define FIRSTBOOT 0b01010101
#define FIRSTBOOT 0b01010101
uint8_t firstboot = FIRSTBOOT; // detect initial boot or factory reset
uint8_t firstboot = FIRSTBOOT; // detect initial boot or factory reset
uint8_t modegroup = 5; // which mode group (set above in #defines)
uint8_t modegroup = 5; // which mode group (set above in #defines)
uint8_t enable_moon = 1; // Should we add moon to the set of modes?
uint8_t enable_moon = 1; // Should we add moon to the set of modes?
uint8_t reverse_modes = 0; // flip the mode order?
uint8_t reverse_modes = 0; // flip the mode order?
uint8_t memory = 0; // mode memory, or not (set via soldered star)
uint8_t memory = 0; // mode memory, or not (set via soldered star)
#ifdef OFFTIM3
#ifdef OFFTIM3
uint8_t offtim3 = 1; // enable medium-press?
uint8_t offtim3 = 1; // enable medium-press?
#endif
#endif
#ifdef TEMPERATURE_MON
#ifdef TEMPERATURE_MON
uint8_t maxtemp = 79; // temperature step-down threshold
uint8_t maxtemp = 79; // temperature step-down threshold
#endif
#endif
uint8_t muggle_mode = 0; // simple mode designed for muggles
uint8_t muggle_mode = 0; // simple mode designed for muggles
// Other state variables
// Other state variables
uint8_t mode_override = 0; // do we need to enter a special mode?
uint8_t mode_override = 0; // do we need to enter a special mode?
uint8_t mode_idx = 0; // current or last-used mode number
uint8_t mode_idx = 0; // current or last-used mode number
uint8_t eepos = 0;
uint8_t eepos = 0;
// counter for entering config mode
// counter for entering config mode
// (needs to be remembered while off, but only for up to half a second)
// (needs to be remembered while off, but only for up to half a second)
uint8_t fast_presses __attribute__ ((section (".noinit")));
uint8_t fast_presses __attribute__ ((section (".noinit")));
// total length of current mode group's array
// total length of current mode group's array
uint8_t mode_cnt;
uint8_t mode_cnt;
// number of regular non-hidden modes in current mode group
// number of regular non-hidden modes in current mode group
uint8_t solid_modes;
uint8_t solid_modes;
// number of hidden modes in the current mode group
// number of hidden modes in the current mode group
// (hardcoded because both groups have the same hidden modes)
// (hardcoded because both groups have the same hidden modes)
//uint8_t hidden_modes = NUM_HIDDEN; // this is never used
//uint8_t hidden_modes = NUM_HIDDEN; // this is never used
PROGMEM const uint8_t hiddenmodes[] = { HIDDENMODES };
PROGMEM const uint8_t hiddenmodes[] = { HIDDENMODES };
// default values calculated by group_calc.py
// default values calculated by group_calc.py
// Each group must be 8 values long, but can be cut short with a zero.
// Each group must be 8 values long, but can be cut short with a zero.
コピー
コピー済み
コピー
コピー済み
#define NUM_MODEGROUPS
8
// don't count muggle mode
#define NUM_MODEGROUPS
9
// don't count muggle mode
PROGMEM const uint8_t modegroups[] = {
PROGMEM const uint8_t modegroups[] = {
64, 0, 0, 0, 0, 0, 0, 0,
64, 0, 0, 0, 0, 0, 0, 0,
コピー
コピー済み
コピー
コピー済み
10
, 64, 0, 0, 0, 0, 0, 0,
11
, 64, 0, 0, 0, 0, 0, 0,
10, 37
, 64, 0, 0, 0, 0, 0,
11, 35
, 64, 0, 0, 0, 0, 0,
10, 28
, 46, 64, 0, 0, 0, 0,
11, 26
, 46, 64, 0, 0, 0, 0,
10
, 23,
37
, 50, 64, 0, 0, 0,
11
, 23,
36
, 50, 64, 0, 0, 0,
10
, 20, 31,
42
, 53, 64, 0, 0,
11
, 20, 31,
41
, 53, 64, 0, 0,
10, 19, 28, 37
, 46,
54,
64, 0,
29, 64,POLICE_STROBE,0,0,0,0,0, // 7: special group A
1
0,
17, 25, 33, 4
0,
48, 56, 64,
BIKING_STROBE,BATTCHECK,11,29,64,0,0,0, // 8: special group B
10, 30
, 50, 0, // muggle mode, exception to "must be 8 bytes long"
9, 18, 29
, 46,
64, 0,
0,
0,
// 9: special group C
11, 29
, 50, 0, // muggle mode, exception to "must be 8 bytes long"
};
};
uint8_t modes[] = { 1,2,3,4,5,6,7,8,9, HIDDENMODES }; // make sure this is long enough...
uint8_t modes[] = { 1,2,3,4,5,6,7,8,9, HIDDENMODES }; // make sure this is long enough...
// Modes (gets set when the light starts up based on saved config values)
// Modes (gets set when the light starts up based on saved config values)
PROGMEM const uint8_t ramp_7135[] = { RAMP_7135 };
PROGMEM const uint8_t ramp_7135[] = { RAMP_7135 };
PROGMEM const uint8_t ramp_FET[] = { RAMP_FET };
PROGMEM const uint8_t ramp_FET[] = { RAMP_FET };
void save_mode() { // save the current mode index (with wear leveling)
void save_mode() { // save the current mode index (with wear leveling)
uint8_t oldpos=eepos;
uint8_t oldpos=eepos;
eepos = (eepos+1) & ((EEPSIZE/2)-1); // wear leveling, use next cell
eepos = (eepos+1) & ((EEPSIZE/2)-1); // wear leveling, use next cell
eeprom_write_byte((uint8_t *)(eepos), mode_idx); // save current state
eeprom_write_byte((uint8_t *)(eepos), mode_idx); // save current state
eeprom_write_byte((uint8_t *)(oldpos), 0xff); // erase old state
eeprom_write_byte((uint8_t *)(oldpos), 0xff); // erase old state
}
}
#define OPT_firstboot (EEPSIZE-1)
#define OPT_firstboot (EEPSIZE-1)
#define OPT_modegroup (EEPSIZE-2)
#define OPT_modegroup (EEPSIZE-2)
#define OPT_memory (EEPSIZE-3)
#define OPT_memory (EEPSIZE-3)
#define OPT_offtim3 (EEPSIZE-4)
#define OPT_offtim3 (EEPSIZE-4)
#define OPT_maxtemp (EEPSIZE-5)
#define OPT_maxtemp (EEPSIZE-5)
#define OPT_mode_override (EEPSIZE-6)
#define OPT_mode_override (EEPSIZE-6)
#define OPT_moon (EEPSIZE-7)
#define OPT_moon (EEPSIZE-7)
#define OPT_revmodes (EEPSIZE-8)
#define OPT_revmodes (EEPSIZE-8)
#define OPT_muggle (EEPSIZE-9)
#define OPT_muggle (EEPSIZE-9)
void save_state() { // central method for writing complete state
void save_state() { // central method for writing complete state
save_mode();
save_mode();
eeprom_write_byte((uint8_t *)OPT_firstboot, firstboot);
eeprom_write_byte((uint8_t *)OPT_firstboot, firstboot);
eeprom_write_byte((uint8_t *)OPT_modegroup, modegroup);
eeprom_write_byte((uint8_t *)OPT_modegroup, modegroup);
eeprom_write_byte((uint8_t *)OPT_memory, memory);
eeprom_write_byte((uint8_t *)OPT_memory, memory);
#ifdef OFFTIM3
#ifdef OFFTIM3
eeprom_write_byte((uint8_t *)OPT_offtim3, offtim3);
eeprom_write_byte((uint8_t *)OPT_offtim3, offtim3);
#endif
#endif
#ifdef TEMPERATURE_MON
#ifdef TEMPERATURE_MON
eeprom_write_byte((uint8_t *)OPT_maxtemp, maxtemp);
eeprom_write_byte((uint8_t *)OPT_maxtemp, maxtemp);
#endif
#endif
eeprom_write_byte((uint8_t *)OPT_mode_override, mode_override);
eeprom_write_byte((uint8_t *)OPT_mode_override, mode_override);
eeprom_write_byte((uint8_t *)OPT_moon, enable_moon);
eeprom_write_byte((uint8_t *)OPT_moon, enable_moon);
eeprom_write_byte((uint8_t *)OPT_revmodes, reverse_modes);
eeprom_write_byte((uint8_t *)OPT_revmodes, reverse_modes);
eeprom_write_byte((uint8_t *)OPT_muggle, muggle_mode);
eeprom_write_byte((uint8_t *)OPT_muggle, muggle_mode);
}
}
void restore_state() {
void restore_state() {
uint8_t eep;
uint8_t eep;
// check if this is the first time we have powered on
// check if this is the first time we have powered on
eep = eeprom_read_byte((uint8_t *)OPT_firstboot);
eep = eeprom_read_byte((uint8_t *)OPT_firstboot);
if (eep != FIRSTBOOT) {
if (eep != FIRSTBOOT) {
// not much to do; the defaults should already be set
// not much to do; the defaults should already be set
// while defining the variables above
// while defining the variables above
save_state();
save_state();
return;
return;
}
}
// find the mode index data
// find the mode index data
for(eepos=0; eepos<(EEPSIZE/2); eepos++) {
for(eepos=0; eepos<(EEPSIZE/2); eepos++) {
eep = eeprom_read_byte((const uint8_t *)eepos);
eep = eeprom_read_byte((const uint8_t *)eepos);
if (eep != 0xff) {
if (eep != 0xff) {
mode_idx = eep;
mode_idx = eep;
break;
break;
}
}
}
}
// load other config values
// load other config values
modegroup = eeprom_read_byte((uint8_t *)OPT_modegroup);
modegroup = eeprom_read_byte((uint8_t *)OPT_modegroup);
memory = eeprom_read_byte((uint8_t *)OPT_memory);
memory = eeprom_read_byte((uint8_t *)OPT_memory);
#ifdef OFFTIM3
#ifdef OFFTIM3
offtim3 = eeprom_read_byte((uint8_t *)OPT_offtim3);
offtim3 = eeprom_read_byte((uint8_t *)OPT_offtim3);
#endif
#endif
#ifdef TEMPERATURE_MON
#ifdef TEMPERATURE_MON
maxtemp = eeprom_read_byte((uint8_t *)OPT_maxtemp);
maxtemp = eeprom_read_byte((uint8_t *)OPT_maxtemp);
#endif
#endif
mode_override = eeprom_read_byte((uint8_t *)OPT_mode_override);
mode_override = eeprom_read_byte((uint8_t *)OPT_mode_override);
enable_moon = eeprom_read_byte((uint8_t *)OPT_moon);
enable_moon = eeprom_read_byte((uint8_t *)OPT_moon);
reverse_modes = eeprom_read_byte((uint8_t *)OPT_revmodes);
reverse_modes = eeprom_read_byte((uint8_t *)OPT_revmodes);
muggle_mode = eeprom_read_byte((uint8_t *)OPT_muggle);
muggle_mode = eeprom_read_byte((uint8_t *)OPT_muggle);
// unnecessary, save_state handles wrap-around
// unnecessary, save_state handles wrap-around
// (and we don't really care about it skipping cell 0 once in a while)
// (and we don't really care about it skipping cell 0 once in a while)
//else eepos=0;
//else eepos=0;
}
}
inline void next_mode() {
inline void next_mode() {
mode_idx += 1;
mode_idx += 1;
if (mode_idx >= solid_modes) {
if (mode_idx >= solid_modes) {
// Wrap around, skipping the hidden modes
// Wrap around, skipping the hidden modes
// (note: this also applies when going "forward" from any hidden mode)
// (note: this also applies when going "forward" from any hidden mode)
// FIXME? Allow this to cycle through hidden modes?
// FIXME? Allow this to cycle through hidden modes?
mode_idx = 0;
mode_idx = 0;
}
}
}
}
#ifdef OFFTIM3
#ifdef OFFTIM3
inline void prev_mode() {
inline void prev_mode() {
// simple mode has no reverse
// simple mode has no reverse
if (muggle_mode) { return next_mode(); }
if (muggle_mode) { return next_mode(); }
if (mode_idx == solid_modes) {
if (mode_idx == solid_modes) {
// If we hit the end of the hidden modes, go back to moon
// If we hit the end of the hidden modes, go back to moon
mode_idx = 0;
mode_idx = 0;
} else if (mode_idx > 0) {
} else if (mode_idx > 0) {
// Regular mode: is between 1 and TOTAL_MODES
// Regular mode: is between 1 and TOTAL_MODES
mode_idx -= 1;
mode_idx -= 1;
} else {
} else {
// Otherwise, wrap around (this allows entering hidden modes)
// Otherwise, wrap around (this allows entering hidden modes)
mode_idx = mode_cnt - 1;
mode_idx = mode_cnt - 1;
}
}
}
}
#endif
#endif
void count_modes() {
void count_modes() {
/*
/*
* Determine how many solid and hidden modes we have.
* Determine how many solid and hidden modes we have.
*
*
* (this matters because we have more than one set of modes to choose
* (this matters because we have more than one set of modes to choose
* from, so we need to count at runtime)
* from, so we need to count at runtime)
*/
*/
// copy config to local vars to avoid accidentally overwriting them in muggle mode
// copy config to local vars to avoid accidentally overwriting them in muggle mode
// (also, it seems to reduce overall program size)
// (also, it seems to reduce overall program size)
uint8_t my_modegroup = modegroup;
uint8_t my_modegroup = modegroup;
uint8_t my_enable_moon = enable_moon;
uint8_t my_enable_moon = enable_moon;
uint8_t my_reverse_modes = reverse_modes;
uint8_t my_reverse_modes = reverse_modes;
// override config if we're in simple mode
// override config if we're in simple mode
if (muggle_mode) {
if (muggle_mode) {
my_modegroup = NUM_MODEGROUPS;
my_modegroup = NUM_MODEGROUPS;
my_enable_moon = 0;
my_enable_moon = 0;
my_reverse_modes = 0;
my_reverse_modes = 0;
}
}
uint8_t *dest;
uint8_t *dest;
const uint8_t *src = modegroups + (my_modegroup<<3);
const uint8_t *src = modegroups + (my_modegroup<<3);
dest = modes;
dest = modes;
// Figure out how many modes are in this group
// Figure out how many modes are in this group
//solid_modes = modegroup + 1; // Assume group N has N modes
//solid_modes = modegroup + 1; // Assume group N has N modes
// No, how about actually counting the modes instead?
// No, how about actually counting the modes instead?
// (in case anyone changes the mode groups above so they don't form a triangle)
// (in case anyone changes the mode groups above so they don't form a triangle)
for(solid_modes=0;
for(solid_modes=0;
(solid_modes<8) && pgm_read_byte(src + solid_modes);
(solid_modes<8) && pgm_read_byte(src + solid_modes);
solid_modes++ ) {}
solid_modes++ ) {}
// add moon mode (or not) if config says to add it
// add moon mode (or not) if config says to add it
if (my_enable_moon) {
if (my_enable_moon) {
modes[0] = 1;
modes[0] = 1;
dest ++;
dest ++;
}
}
// add regular modes
// add regular modes
memcpy_P(dest, src, solid_modes);
memcpy_P(dest, src, solid_modes);
// add hidden modes
// add hidden modes
memcpy_P(dest + solid_modes, hiddenmodes, sizeof(hiddenmodes));
memcpy_P(dest + solid_modes, hiddenmodes, sizeof(hiddenmodes));
// final count
// final count
mode_cnt = solid_modes + sizeof(hiddenmodes);
mode_cnt = solid_modes + sizeof(hiddenmodes);
if (my_reverse_modes) {
if (my_reverse_modes) {
// TODO: yuck, isn't there a better way to do this?
// TODO: yuck, isn't there a better way to do this?
int8_t i;
int8_t i;
src += solid_modes;
src += solid_modes;
dest = modes;
dest = modes;
for(i=0; i<solid_modes; i++) {
for(i=0; i<solid_modes; i++) {
src --;
src --;
*dest = pgm_read_byte(src);
*dest = pgm_read_byte(src);
dest ++;
dest ++;
}
}
if (my_enable_moon) {
if (my_enable_moon) {
*dest = 1;
*dest = 1;
}
}
mode_cnt --; // get rid of last hidden mode, since it's a duplicate turbo
mode_cnt --; // get rid of last hidden mode, since it's a duplicate turbo
}
}
if (my_enable_moon) {
if (my_enable_moon) {
mode_cnt ++;
mode_cnt ++;
solid_modes ++;
solid_modes ++;
}
}
}
}
inline void set_output(uint8_t pwm1, uint8_t pwm2) {
inline void set_output(uint8_t pwm1, uint8_t pwm2) {
/* This is no longer needed since we always use PHASE mode.
/* This is no longer needed since we always use PHASE mode.
// Need PHASE to properly turn off the light
// Need PHASE to properly turn off the light
if ((pwm1==0) && (pwm2==0)) {
if ((pwm1==0) && (pwm2==0)) {
TCCR0A = PHASE;
TCCR0A = PHASE;
}
}
*/
*/
PWM_LVL = pwm1;
PWM_LVL = pwm1;
ALT_PWM_LVL = pwm2;
ALT_PWM_LVL = pwm2;
}
}
void set_level(uint8_t level) {
void set_level(uint8_t level) {
if (level == 0) {
if (level == 0) {
set_output(0,0);
set_output(0,0);
} else {
} else {
level -= 1;
level -= 1;
set_output(pgm_read_byte(ramp_FET + level),
set_output(pgm_read_byte(ramp_FET + level),
pgm_read_byte(ramp_7135 + level));
pgm_read_byte(ramp_7135 + level));
}
}
}
}
void set_mode(uint8_t mode) {
void set_mode(uint8_t mode) {
#ifdef SOFT_START
#ifdef SOFT_START
static uint8_t actual_level = 0;
static uint8_t actual_level = 0;
uint8_t target_level = mode;
uint8_t target_level = mode;
int8_t shift_amount;
int8_t shift_amount;
int8_t diff;
int8_t diff;
do {
do {
diff = target_level - actual_level;
diff = target_level - actual_level;
shift_amount = (diff >> 2) | (diff!=0);
shift_amount = (diff >> 2) | (diff!=0);
actual_level += shift_amount;
actual_level += shift_amount;
set_level(actual_level);
set_level(actual_level);
//_delay_ms(RAMP_SIZE/20); // slow ramp
//_delay_ms(RAMP_SIZE/20); // slow ramp
_delay_ms(RAMP_SIZE/4); // fast ramp
_delay_ms(RAMP_SIZE/4); // fast ramp
} while (target_level != actual_level);
} while (target_level != actual_level);
#else
#else
set_level(mode);
set_level(mode);
#endif // SOFT_START
#endif // SOFT_START
}
}
void blink(uint8_t val, uint16_t speed)
void blink(uint8_t val, uint16_t speed)
{
{
for (; val>0; val--)
for (; val>0; val--)
{
{
set_level(BLINK_BRIGHTNESS);
set_level(BLINK_BRIGHTNESS);
_delay_ms(speed);
_delay_ms(speed);
set_level(0);
set_level(0);
_delay_ms(speed<<2);
_delay_ms(speed<<2);
}
}
}
}
void strobe(uint8_t ontime, uint8_t offtime) {
void strobe(uint8_t ontime, uint8_t offtime) {
set_level(RAMP_SIZE);
set_level(RAMP_SIZE);
_delay_ms(ontime);
_delay_ms(ontime);
set_level(0);
set_level(0);
_delay_ms(offtime);
_delay_ms(offtime);
}
}
void toggle(uint8_t *var, uint8_t num) {
void toggle(uint8_t *var, uint8_t num) {
// Used for config mode
// Used for config mode
// Changes the value of a config option, waits for the user to "save"
// Changes the value of a config option, waits for the user to "save"
// by turning the light off, then changes the value back in case they
// by turning the light off, then changes the value back in case they
// didn't save. Can be used repeatedly on different options, allowing
// didn't save. Can be used repeatedly on different options, allowing
// the user to change and save only one at a time.
// the user to change and save only one at a time.
blink(num, BLINK_SPEED/8); // indicate which option number this is
blink(num, BLINK_SPEED/8); // indicate which option number this is
*var ^= 1;
*var ^= 1;
save_state();
save_state();
// "buzz" for a while to indicate the active toggle window
// "buzz" for a while to indicate the active toggle window
for(uint8_t i=0; i<32; i++) {
for(uint8_t i=0; i<32; i++) {
set_level(BLINK_BRIGHTNESS * 3 / 4);
set_level(BLINK_BRIGHTNESS * 3 / 4);
_delay_ms(20);
_delay_ms(20);
set_level(0);
set_level(0);
_delay_ms(20);
_delay_ms(20);
}
}
// if the user didn't click, reset the value and return
// if the user didn't click, reset the value and return
*var ^= 1;
*var ^= 1;
save_state();
save_state();
_delay_s();
_delay_s();
}
}
#ifdef TEMPERATURE_MON
#ifdef TEMPERATURE_MON
uint8_t get_temperature() {
uint8_t get_temperature() {
ADC_on_temperature();
ADC_on_temperature();
// average a few values; temperature is noisy
// average a few values; temperature is noisy
uint16_t temp = 0;
uint16_t temp = 0;
uint8_t i;
uint8_t i;
get_voltage();
get_voltage();
for(i=0; i<16; i++) {
for(i=0; i<16; i++) {
temp += get_voltage();
temp += get_voltage();
_delay_ms(5);
_delay_ms(5);
}
}
temp >>= 4;
temp >>= 4;
return temp;
return temp;
}
}
#endif // TEMPERATURE_MON
#endif // TEMPERATURE_MON
inline uint8_t read_otc() {
inline uint8_t read_otc() {
// Read and return the off-time cap value
// Read and return the off-time cap value
// Start up ADC for capacitor pin
// Start up ADC for capacitor pin
// disable digital input on ADC pin to reduce power consumption
// disable digital input on ADC pin to reduce power consumption
DIDR0 |= (1 << CAP_DIDR);
DIDR0 |= (1 << CAP_DIDR);
// 1.1v reference, left-adjust, ADC3/PB3
// 1.1v reference, left-adjust, ADC3/PB3
ADMUX = (1 << V_REF) | (1 << ADLAR) | CAP_CHANNEL;
ADMUX = (1 << V_REF) | (1 << ADLAR) | CAP_CHANNEL;
// enable, start, prescale
// enable, start, prescale
ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL;
ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL;
// Wait for completion
// Wait for completion
while (ADCSRA & (1 << ADSC));
while (ADCSRA & (1 << ADSC));
// Start again as datasheet says first result is unreliable
// Start again as datasheet says first result is unreliable
ADCSRA |= (1 << ADSC);
ADCSRA |= (1 << ADSC);
// Wait for completion
// Wait for completion
while (ADCSRA & (1 << ADSC));
while (ADCSRA & (1 << ADSC));
// ADCH should have the value we wanted
// ADCH should have the value we wanted
return ADCH;
return ADCH;
}
}
int main(void)
int main(void)
{
{
// check the OTC immediately before it has a chance to charge or discharge
// check the OTC immediately before it has a chance to charge or discharge
uint8_t cap_val = read_otc(); // save it for later
uint8_t cap_val = read_otc(); // save it for later
// Set PWM pin to output
// Set PWM pin to output
DDRB |= (1 << PWM_PIN); // enable main channel
DDRB |= (1 << PWM_PIN); // enable main channel
DDRB |= (1 << ALT_PWM_PIN); // enable second channel
DDRB |= (1 << ALT_PWM_PIN); // enable second channel
// Set timer to do PWM for correct output pin and set prescaler timing
// 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
//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...)
//TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
TCCR0A = PHASE;
TCCR0A = PHASE;
// Set timer to do PWM for correct output pin and set prescaler timing
// Set timer to do PWM for correct output pin and set prescaler timing
TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
// Read config values and saved state
// Read config values and saved state
restore_state();
restore_state();
// Enable the current mode group
// Enable the current mode group
count_modes();
count_modes();
// TODO: Enable this? (might prevent some corner cases, but requires extra room)
// TODO: Enable this? (might prevent some corner cases, but requires extra room)
// memory decayed, reset it
// memory decayed, reset it
// (should happen on med/long press instead
// (should happen on med/long press instead
// because mem decay is *much* slower when the OTC is charged
// because mem decay is *much* slower when the OTC is charged
// so let's not wait until it decays to reset it)
// so let's not wait until it decays to reset it)
//if (fast_presses > 0x20) { fast_presses = 0; }
//if (fast_presses > 0x20) { fast_presses = 0; }
// check button press time, unless the mode is overridden
// check button press time, unless the mode is overridden
if (! mode_override) {
if (! mode_override) {
if (cap_val > CAP_SHORT) {
if (cap_val > CAP_SHORT) {
// Indicates they did a short press, go to the next mode
// Indicates they did a short press, go to the next mode
// We don't care what the fast_presses value is as long as it's over 15
// We don't care what the fast_presses value is as long as it's over 15
fast_presses = (fast_presses+1) & 0x1f;
fast_presses = (fast_presses+1) & 0x1f;
next_mode(); // Will handle wrap arounds
next_mode(); // Will handle wrap arounds
#ifdef OFFTIM3
#ifdef OFFTIM3
} else if (cap_val > CAP_MED) {
} else if (cap_val > CAP_MED) {
// User did a medium press, go back one mode
// User did a medium press, go back one mode
fast_presses = 0;
fast_presses = 0;
if (offtim3) {
if (offtim3) {
prev_mode(); // Will handle "negative" modes and wrap-arounds
prev_mode(); // Will handle "negative" modes and wrap-arounds
} else {
} else {
next_mode(); // disabled-med-press acts like short-press
next_mode(); // disabled-med-press acts like short-press
// (except that fast_presses isn't reliable then)
// (except that fast_presses isn't reliable then)
}
}
#endif
#endif
} else {
} else {
// Long press, keep the same mode
// Long press, keep the same mode
// ... or reset to the first mode
// ... or reset to the first mode
fast_presses = 0;
fast_presses = 0;
if (muggle_mode || (! memory)) {
if (muggle_mode || (! memory)) {
// Reset to the first mode
// Reset to the first mode
mode_idx = 0;
mode_idx = 0;
}
}
}
}
}
}
save_mode();
save_mode();
// Charge up the capacitor by setting CAP_PIN to output
// Charge up the capacitor by setting CAP_PIN to output
DDRB |= (1 << CAP_PIN); // Output
DDRB |= (1 << CAP_PIN); // Output
PORTB |= (1 << CAP_PIN); // High
PORTB |= (1 << CAP_PIN); // High
// Turn features on or off as needed
// Turn features on or off as needed
#ifdef VOLTAGE_MON
#ifdef VOLTAGE_MON
ADC_on();
ADC_on();
#else
#else
ADC_off();
ADC_off();
#endif
#endif
uint8_t output;
uint8_t output;
uint8_t actual_level;
uint8_t actual_level;
#ifdef TEMPERATURE_MON
#ifdef TEMPERATURE_MON
uint8_t overheat_count = 0;
uint8_t overheat_count = 0;
#endif
#endif
#ifdef VOLTAGE_MON
#ifdef VOLTAGE_MON
uint8_t lowbatt_cnt = 0;
uint8_t lowbatt_cnt = 0;
uint8_t i = 0;
uint8_t i = 0;
uint8_t voltage;
uint8_t voltage;
// Make sure voltage reading is running for later
// Make sure voltage reading is running for later
ADCSRA |= (1 << ADSC);
ADCSRA |= (1 << ADSC);
#endif
#endif
//output = pgm_read_byte(modes + mode_idx);
//output = pgm_read_byte(modes + mode_idx);
output = modes[mode_idx];
output = modes[mode_idx];
actual_level = output;
actual_level = output;
// handle mode overrides, like mode group selection and temperature calibration
// handle mode overrides, like mode group selection and temperature calibration
if (mode_override) {
if (mode_override) {
// do nothing; mode is already set
// do nothing; mode is already set
//mode_idx = mode_override;
//mode_idx = mode_override;
fast_presses = 0;
fast_presses = 0;
output = mode_idx;
output = mode_idx;
}
}
while(1) {
while(1) {
if (fast_presses > 0x0f) { // Config mode
if (fast_presses > 0x0f) { // Config mode
_delay_s(); // wait for user to stop fast-pressing button
_delay_s(); // wait for user to stop fast-pressing button
fast_presses = 0; // exit this mode after one use
fast_presses = 0; // exit this mode after one use
mode_idx = 0;
mode_idx = 0;
// Enter or leave "muggle mode"?
// Enter or leave "muggle mode"?
toggle(&muggle_mode, 1);
toggle(&muggle_mode, 1);
if (muggle_mode) { continue; }; // don't offer other options in muggle mode
if (muggle_mode) { continue; }; // don't offer other options in muggle mode
toggle(&memory, 2);
toggle(&memory, 2);
toggle(&enable_moon, 3);
toggle(&enable_moon, 3);
toggle(&reverse_modes, 4);
toggle(&reverse_modes, 4);
// Enter the mode group selection mode?
// Enter the mode group selection mode?
mode_idx = GROUP_SELECT_MODE;
mode_idx = GROUP_SELECT_MODE;
toggle(&mode_override, 5);
toggle(&mode_override, 5);
mode_idx = 0;
mode_idx = 0;
#ifdef OFFTIM3
#ifdef OFFTIM3
toggle(&offtim3, 6);
toggle(&offtim3, 6);
#endif
#endif
#ifdef TEMPERATURE_MON
#ifdef TEMPERATURE_MON
// Enter temperature calibration mode?
// Enter temperature calibration mode?
mode_idx = TEMP_CAL_MODE;
mode_idx = TEMP_CAL_MODE;
toggle(&mode_override, 7);
toggle(&mode_override, 7);
mode_idx = 0;
mode_idx = 0;
#endif
#endif
toggle(&firstboot, 8);
toggle(&firstboot, 8);
//output = pgm_read_byte(modes + mode_idx);
//output = pgm_read_byte(modes + mode_idx);
output = modes[mode_idx];
output = modes[mode_idx];
actual_level = output;
actual_level = output;
}
}
#ifdef STROBE
#ifdef STROBE
else if (output == STROBE) {
else if (output == STROBE) {
// 10Hz tactical strobe
// 10Hz tactical strobe
strobe(50,50);
strobe(50,50);
}
}
#endif // ifdef STROBE
#endif // ifdef STROBE
#ifdef POLICE_STROBE
#ifdef POLICE_STROBE
else if (output == POLICE_STROBE) {
else if (output == POLICE_STROBE) {
// police-like strobe
// police-like strobe
for(i=0;i<8;i++) {
for(i=0;i<8;i++) {
strobe(20,40);
strobe(20,40);
}
}
for(i=0;i<8;i++) {
for(i=0;i<8;i++) {
strobe(40,80);
strobe(40,80);
}
}
}
}
#endif // ifdef POLICE_STROBE
#endif // ifdef POLICE_STROBE
#ifdef RANDOM_STROBE
#ifdef RANDOM_STROBE
else if (output == RANDOM_STROBE) {
else if (output == RANDOM_STROBE) {
// pseudo-random strobe
// pseudo-random strobe
uint8_t ms = 34 + (pgm_rand() & 0x3f);
uint8_t ms = 34 + (pgm_rand() & 0x3f);
strobe(ms, ms);
strobe(ms, ms);
strobe(ms, ms);
strobe(ms, ms);
}
}
#endif // ifdef RANDOM_STROBE
#endif // ifdef RANDOM_STROBE
#ifdef BIKING_STROBE
#ifdef BIKING_STROBE
else if (output == BIKING_STROBE) {
else if (output == BIKING_STROBE) {
// 2-level stutter beacon for biking and such
// 2-level stutter beacon for biking and such
#ifdef FULL_BIKING_STROBE
#ifdef FULL_BIKING_STROBE
// normal version
// normal version
for(i=0;i<4;i++) {
for(i=0;i<4;i++) {
set_output(255,0);
set_output(255,0);
_delay_ms(5);
_delay_ms(5);
set_output(0,255);
set_output(0,255);
_delay_ms(65);
_delay_ms(65);
}
}
_delay_ms(720);
_delay_ms(720);
#else
#else
// small/minimal version
// small/minimal version
set_output(255,0);
set_output(255,0);
_delay_ms(10);
_delay_ms(10);
set_output(0,255);
set_output(0,255);
_delay_s();
_delay_s();
#endif
#endif
}
}
#endif // ifdef BIKING_STROBE
#endif // ifdef BIKING_STROBE
#ifdef RAMP
#ifdef RAMP
else if (output == RAMP) {
else if (output == RAMP) {
int8_t r;
int8_t r;
// simple ramping test
// simple ramping test
for(r=1; r<=RAMP_SIZE; r++) {
for(r=1; r<=RAMP_SIZE; r++) {
set_level(r);
set_level(r);
_delay_ms(25);
_delay_ms(25);
}
}
for(r=RAMP_SIZE; r>0; r--) {
for(r=RAMP_SIZE; r>0; r--) {
set_level(r);
set_level(r);
_delay_ms(25);
_delay_ms(25);
}
}
}
}
#endif // ifdef RAMP
#endif // ifdef RAMP
#ifdef BATTCHECK
#ifdef BATTCHECK
else if (output == BATTCHECK) {
else if (output == BATTCHECK) {
#ifdef BATTCHECK_VpT
#ifdef BATTCHECK_VpT
// blink out volts and tenths
// blink out volts and tenths
uint8_t result = battcheck();
uint8_t result = battcheck();
blink(result >> 5, BLINK_SPEED/8);
blink(result >> 5, BLINK_SPEED/8);
_delay_ms(BLINK_SPEED);
_delay_ms(BLINK_SPEED);
blink(1,5);
blink(1,5);
_delay_ms(BLINK_SPEED*3/2);
_delay_ms(BLINK_SPEED*3/2);
blink(result & 0b00011111, BLINK_SPEED/8);
blink(result & 0b00011111, BLINK_SPEED/8);
#else // ifdef BATTCHECK_VpT
#else // ifdef BATTCHECK_VpT
// blink zero to five times to show voltage
// blink zero to five times to show voltage
// (~0%, ~25%, ~50%, ~75%, ~100%, >100%)
// (~0%, ~25%, ~50%, ~75%, ~100%, >100%)
blink(battcheck(), BLINK_SPEED/8);
blink(battcheck(), BLINK_SPEED/8);
#endif // ifdef BATTCHECK_VpT
#endif // ifdef BATTCHECK_VpT
// wait between readouts
// wait between readouts
_delay_s(); _delay_s();
_delay_s(); _delay_s();
}
}
#endif // ifdef BATTCHECK
#endif // ifdef BATTCHECK
else if (output == GROUP_SELECT_MODE) {
else if (output == GROUP_SELECT_MODE) {
// exit this mode after one use
// exit this mode after one use
mode_idx = 0;
mode_idx = 0;
mode_override = 0;
mode_override = 0;
for(i=0; i<NUM_MODEGROUPS; i++) {
for(i=0; i<NUM_MODEGROUPS; i++) {
modegroup = i;
modegroup = i;
save_state();
save_state();
blink(1, BLINK_SPEED/3);
blink(1, BLINK_SPEED/3);
}
}
_delay_s(); _delay_s();
_delay_s(); _delay_s();
}
}
#ifdef TEMP_CAL_MODE
#ifdef TEMP_CAL_MODE
else if (output == TEMP_CAL_MODE) {
else if (output == TEMP_CAL_MODE) {
// make sure we don't stay in this mode after button press
// make sure we don't stay in this mode after button press
mode_idx = 0;
mode_idx = 0;
mode_override = 0;
mode_override = 0;
// Allow the user to turn off thermal regulation if they want
// Allow the user to turn off thermal regulation if they want
maxtemp = 255;
maxtemp = 255;
save_state();
save_state();
set_mode(RAMP_SIZE/4); // start somewhat dim during turn-off-regulation mode
set_mode(RAMP_SIZE/4); // start somewhat dim during turn-off-regulation mode
_delay_s(); _delay_s();
_delay_s(); _delay_s();
// run at highest output level, to generate heat
// run at highest output level, to generate heat
コピー
コピー済み
コピー
コピー済み
set_mode(RAMP_SIZE);
// measure, save, wait... repeat
while(1) {
ma
保存された差分
原文
ファイルを開く
/* * "Bistro" firmware * This code runs on a single-channel or dual-channel driver (FET+7135) * with an attiny25/45/85 MCU and a capacitor to measure offtime (OTC). * * Copyright (C) 2015 Selene Scriven * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * * ATTINY25/45/85 Diagram * ---- * -|1 8|- VCC * OTC -|2 7|- Voltage ADC * Star 3 -|3 6|- PWM (FET, optional) * GND -|4 5|- PWM (1x7135) * ---- * * FUSES * I use these fuse settings on attiny25 * Low: 0xd2 * High: 0xde * Ext: 0xff * * For more details on these settings: * 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 * * STARS * Star 3 = unused * * CALIBRATION * * To find out what values to use, flash the driver with battcheck.hex * and hook the light up to each voltage you need a value for. This is * much more reliable than attempting to calculate the values from a * theoretical formula. * * Same for off-time capacitor values. Measure, don't guess. */ // Choose your MCU here, or in the build script //#define ATTINY 13 //#define ATTINY 25 // FIXME: make 1-channel vs 2-channel power a single #define option #define FET_7135_LAYOUT // specify an I/O pin layout // Also, assign I/O pins in this file: #include "tk-attiny.h" /* * ========================================================================= * Settings to modify per driver */ // FIXME: make 1-channel vs 2-channel power a single #define option //#define FAST 0x23 // fast PWM channel 1 only //#define PHASE 0x21 // phase-correct PWM channel 1 only #define FAST 0xA3 // fast PWM both channels #define PHASE 0xA1 // phase-correct PWM both channels #define VOLTAGE_MON // Comment out to disable LVP #define OFFTIM3 // Use short/med/long off-time presses // instead of just short/long // ../../bin/level_calc.py 64 1 10 1400 y 3 0.25 140 #define RAMP_SIZE 64 // log curve //#define RAMP_7135 3,3,3,3,3,3,4,4,4,4,4,5,5,5,6,6,7,7,8,9,10,11,12,13,15,16,18,21,23,27,30,34,39,44,50,57,65,74,85,97,111,127,145,166,190,217,248,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 //#define RAMP_FET 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,6,11,17,23,30,39,48,59,72,86,103,121,143,168,197,255 // x**2 curve //#define RAMP_7135 3,5,8,12,17,24,32,41,51,63,75,90,105,121,139,158,178,200,223,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 //#define RAMP_FET 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,6,9,12,16,19,22,26,30,33,37,41,45,50,54,59,63,68,73,78,84,89,94,100,106,111,117,123,130,136,142,149,156,162,169,176,184,191,198,206,214,221,255 // x**3 curve #define RAMP_7135 3,3,4,5,7,8,10,13,16,20,25,30,36,42,50,59,68,78,90,103,116,131,148,165,184,204,226,249,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 #define RAMP_FET 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,7,10,13,16,20,24,28,32,36,41,46,51,56,61,67,73,80,86,93,100,107,115,123,131,139,148,157,166,176,186,196,207,218,255 // x**5 curve //#define RAMP_7135 3,3,3,4,4,5,5,6,7,8,10,11,13,15,18,21,24,28,33,38,44,50,57,66,75,85,96,108,122,137,154,172,192,213,237,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 //#define RAMP_FET 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,6,9,13,17,21,25,30,35,41,47,53,60,67,75,83,91,101,111,121,132,144,156,169,183,198,213,255 // uncomment to ramp up/down to a mode instead of jumping directly #define SOFT_START // Enable battery indicator mode? #define USE_BATTCHECK // Choose a battery indicator style //#define BATTCHECK_4bars // up to 4 blinks //#define BATTCHECK_8bars // up to 8 blinks #define BATTCHECK_VpT // Volts + tenths // output to use for blinks on battery check (and other modes) #define BLINK_BRIGHTNESS RAMP_SIZE/4 // ms per normal-speed blink #define BLINK_SPEED 500 // Hidden modes are *before* the lowest (moon) mode, and should be specified // in reverse order. So, to go backward from moon to turbo to strobe to // battcheck, use BATTCHECK,STROBE,TURBO . #define HIDDENMODES BIKING_STROBE,BATTCHECK,POLICE_STROBE,TURBO #define TURBO RAMP_SIZE // Convenience code for turbo mode #define BATTCHECK 254 // Convenience code for battery check mode #define GROUP_SELECT_MODE 253 #define TEMP_CAL_MODE 252 // Uncomment to enable tactical strobe mode //#define STROBE 251 // Convenience code for strobe mode // Uncomment to unable a 2-level stutter beacon instead of a tactical strobe #define BIKING_STROBE 250 // Convenience code for biking strobe mode // comment out to use minimal version instead (smaller) #define FULL_BIKING_STROBE //#define RAMP 249 // ramp test mode for tweaking ramp shape #define POLICE_STROBE 248 //#define RANDOM_STROBE 247 // thermal step-down #define TEMPERATURE_MON // Calibrate voltage and OTC in this file: #include "tk-calibration.h" /* * ========================================================================= */ // Ignore a spurious warning, we did the cast on purpose #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" #include <avr/pgmspace.h> //#include <avr/io.h> //#include <avr/interrupt.h> #include <avr/eeprom.h> #include <avr/sleep.h> //#include <avr/power.h> #include <string.h> #define OWN_DELAY // Don't use stock delay functions. #define USE_DELAY_S // Also use _delay_s(), not just _delay_ms() #include "tk-delay.h" #include "tk-voltage.h" #ifdef RANDOM_STROBE #include "tk-random.h" #endif /* * global variables */ // Config option variables #define FIRSTBOOT 0b01010101 uint8_t firstboot = FIRSTBOOT; // detect initial boot or factory reset uint8_t modegroup = 5; // which mode group (set above in #defines) uint8_t enable_moon = 1; // Should we add moon to the set of modes? uint8_t reverse_modes = 0; // flip the mode order? uint8_t memory = 0; // mode memory, or not (set via soldered star) #ifdef OFFTIM3 uint8_t offtim3 = 1; // enable medium-press? #endif #ifdef TEMPERATURE_MON uint8_t maxtemp = 79; // temperature step-down threshold #endif uint8_t muggle_mode = 0; // simple mode designed for muggles // Other state variables uint8_t mode_override = 0; // do we need to enter a special mode? uint8_t mode_idx = 0; // current or last-used mode number uint8_t eepos = 0; // counter for entering config mode // (needs to be remembered while off, but only for up to half a second) uint8_t fast_presses __attribute__ ((section (".noinit"))); // total length of current mode group's array uint8_t mode_cnt; // number of regular non-hidden modes in current mode group uint8_t solid_modes; // number of hidden modes in the current mode group // (hardcoded because both groups have the same hidden modes) //uint8_t hidden_modes = NUM_HIDDEN; // this is never used PROGMEM const uint8_t hiddenmodes[] = { HIDDENMODES }; // default values calculated by group_calc.py // Each group must be 8 values long, but can be cut short with a zero. #define NUM_MODEGROUPS 8 // don't count muggle mode PROGMEM const uint8_t modegroups[] = { 64, 0, 0, 0, 0, 0, 0, 0, 10, 64, 0, 0, 0, 0, 0, 0, 10, 37, 64, 0, 0, 0, 0, 0, 10, 28, 46, 64, 0, 0, 0, 0, 10, 23, 37, 50, 64, 0, 0, 0, 10, 20, 31, 42, 53, 64, 0, 0, 10, 19, 28, 37, 46, 54, 64, 0, 10, 17, 25, 33, 40, 48, 56, 64, 10, 30, 50, 0, // muggle mode, exception to "must be 8 bytes long" }; uint8_t modes[] = { 1,2,3,4,5,6,7,8,9, HIDDENMODES }; // make sure this is long enough... // Modes (gets set when the light starts up based on saved config values) PROGMEM const uint8_t ramp_7135[] = { RAMP_7135 }; PROGMEM const uint8_t ramp_FET[] = { RAMP_FET }; void save_mode() { // save the current mode index (with wear leveling) uint8_t oldpos=eepos; eepos = (eepos+1) & ((EEPSIZE/2)-1); // wear leveling, use next cell eeprom_write_byte((uint8_t *)(eepos), mode_idx); // save current state eeprom_write_byte((uint8_t *)(oldpos), 0xff); // erase old state } #define OPT_firstboot (EEPSIZE-1) #define OPT_modegroup (EEPSIZE-2) #define OPT_memory (EEPSIZE-3) #define OPT_offtim3 (EEPSIZE-4) #define OPT_maxtemp (EEPSIZE-5) #define OPT_mode_override (EEPSIZE-6) #define OPT_moon (EEPSIZE-7) #define OPT_revmodes (EEPSIZE-8) #define OPT_muggle (EEPSIZE-9) void save_state() { // central method for writing complete state save_mode(); eeprom_write_byte((uint8_t *)OPT_firstboot, firstboot); eeprom_write_byte((uint8_t *)OPT_modegroup, modegroup); eeprom_write_byte((uint8_t *)OPT_memory, memory); #ifdef OFFTIM3 eeprom_write_byte((uint8_t *)OPT_offtim3, offtim3); #endif #ifdef TEMPERATURE_MON eeprom_write_byte((uint8_t *)OPT_maxtemp, maxtemp); #endif eeprom_write_byte((uint8_t *)OPT_mode_override, mode_override); eeprom_write_byte((uint8_t *)OPT_moon, enable_moon); eeprom_write_byte((uint8_t *)OPT_revmodes, reverse_modes); eeprom_write_byte((uint8_t *)OPT_muggle, muggle_mode); } void restore_state() { uint8_t eep; // check if this is the first time we have powered on eep = eeprom_read_byte((uint8_t *)OPT_firstboot); if (eep != FIRSTBOOT) { // not much to do; the defaults should already be set // while defining the variables above save_state(); return; } // find the mode index data for(eepos=0; eepos<(EEPSIZE/2); eepos++) { eep = eeprom_read_byte((const uint8_t *)eepos); if (eep != 0xff) { mode_idx = eep; break; } } // load other config values modegroup = eeprom_read_byte((uint8_t *)OPT_modegroup); memory = eeprom_read_byte((uint8_t *)OPT_memory); #ifdef OFFTIM3 offtim3 = eeprom_read_byte((uint8_t *)OPT_offtim3); #endif #ifdef TEMPERATURE_MON maxtemp = eeprom_read_byte((uint8_t *)OPT_maxtemp); #endif mode_override = eeprom_read_byte((uint8_t *)OPT_mode_override); enable_moon = eeprom_read_byte((uint8_t *)OPT_moon); reverse_modes = eeprom_read_byte((uint8_t *)OPT_revmodes); muggle_mode = eeprom_read_byte((uint8_t *)OPT_muggle); // unnecessary, save_state handles wrap-around // (and we don't really care about it skipping cell 0 once in a while) //else eepos=0; } inline void next_mode() { mode_idx += 1; if (mode_idx >= solid_modes) { // Wrap around, skipping the hidden modes // (note: this also applies when going "forward" from any hidden mode) // FIXME? Allow this to cycle through hidden modes? mode_idx = 0; } } #ifdef OFFTIM3 inline void prev_mode() { // simple mode has no reverse if (muggle_mode) { return next_mode(); } if (mode_idx == solid_modes) { // If we hit the end of the hidden modes, go back to moon mode_idx = 0; } else if (mode_idx > 0) { // Regular mode: is between 1 and TOTAL_MODES mode_idx -= 1; } else { // Otherwise, wrap around (this allows entering hidden modes) mode_idx = mode_cnt - 1; } } #endif void count_modes() { /* * Determine how many solid and hidden modes we have. * * (this matters because we have more than one set of modes to choose * from, so we need to count at runtime) */ // copy config to local vars to avoid accidentally overwriting them in muggle mode // (also, it seems to reduce overall program size) uint8_t my_modegroup = modegroup; uint8_t my_enable_moon = enable_moon; uint8_t my_reverse_modes = reverse_modes; // override config if we're in simple mode if (muggle_mode) { my_modegroup = NUM_MODEGROUPS; my_enable_moon = 0; my_reverse_modes = 0; } uint8_t *dest; const uint8_t *src = modegroups + (my_modegroup<<3); dest = modes; // Figure out how many modes are in this group //solid_modes = modegroup + 1; // Assume group N has N modes // No, how about actually counting the modes instead? // (in case anyone changes the mode groups above so they don't form a triangle) for(solid_modes=0; (solid_modes<8) && pgm_read_byte(src + solid_modes); solid_modes++ ) {} // add moon mode (or not) if config says to add it if (my_enable_moon) { modes[0] = 1; dest ++; } // add regular modes memcpy_P(dest, src, solid_modes); // add hidden modes memcpy_P(dest + solid_modes, hiddenmodes, sizeof(hiddenmodes)); // final count mode_cnt = solid_modes + sizeof(hiddenmodes); if (my_reverse_modes) { // TODO: yuck, isn't there a better way to do this? int8_t i; src += solid_modes; dest = modes; for(i=0; i<solid_modes; i++) { src --; *dest = pgm_read_byte(src); dest ++; } if (my_enable_moon) { *dest = 1; } mode_cnt --; // get rid of last hidden mode, since it's a duplicate turbo } if (my_enable_moon) { mode_cnt ++; solid_modes ++; } } inline void set_output(uint8_t pwm1, uint8_t pwm2) { /* This is no longer needed since we always use PHASE mode. // Need PHASE to properly turn off the light if ((pwm1==0) && (pwm2==0)) { TCCR0A = PHASE; } */ PWM_LVL = pwm1; ALT_PWM_LVL = pwm2; } void set_level(uint8_t level) { if (level == 0) { set_output(0,0); } else { level -= 1; set_output(pgm_read_byte(ramp_FET + level), pgm_read_byte(ramp_7135 + level)); } } void set_mode(uint8_t mode) { #ifdef SOFT_START static uint8_t actual_level = 0; uint8_t target_level = mode; int8_t shift_amount; int8_t diff; do { diff = target_level - actual_level; shift_amount = (diff >> 2) | (diff!=0); actual_level += shift_amount; set_level(actual_level); //_delay_ms(RAMP_SIZE/20); // slow ramp _delay_ms(RAMP_SIZE/4); // fast ramp } while (target_level != actual_level); #else set_level(mode); #endif // SOFT_START } void blink(uint8_t val, uint16_t speed) { for (; val>0; val--) { set_level(BLINK_BRIGHTNESS); _delay_ms(speed); set_level(0); _delay_ms(speed<<2); } } void strobe(uint8_t ontime, uint8_t offtime) { set_level(RAMP_SIZE); _delay_ms(ontime); set_level(0); _delay_ms(offtime); } void toggle(uint8_t *var, uint8_t num) { // Used for config mode // Changes the value of a config option, waits for the user to "save" // by turning the light off, then changes the value back in case they // didn't save. Can be used repeatedly on different options, allowing // the user to change and save only one at a time. blink(num, BLINK_SPEED/8); // indicate which option number this is *var ^= 1; save_state(); // "buzz" for a while to indicate the active toggle window for(uint8_t i=0; i<32; i++) { set_level(BLINK_BRIGHTNESS * 3 / 4); _delay_ms(20); set_level(0); _delay_ms(20); } // if the user didn't click, reset the value and return *var ^= 1; save_state(); _delay_s(); } #ifdef TEMPERATURE_MON uint8_t get_temperature() { ADC_on_temperature(); // average a few values; temperature is noisy uint16_t temp = 0; uint8_t i; get_voltage(); for(i=0; i<16; i++) { temp += get_voltage(); _delay_ms(5); } temp >>= 4; return temp; } #endif // TEMPERATURE_MON inline uint8_t read_otc() { // Read and return the off-time cap value // Start up ADC for capacitor pin // disable digital input on ADC pin to reduce power consumption DIDR0 |= (1 << CAP_DIDR); // 1.1v reference, left-adjust, ADC3/PB3 ADMUX = (1 << V_REF) | (1 << ADLAR) | CAP_CHANNEL; // enable, start, prescale ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL; // 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)); // ADCH should have the value we wanted return ADCH; } int main(void) { // check the OTC immediately before it has a chance to charge or discharge uint8_t cap_val = read_otc(); // save it for later // Set PWM pin to output DDRB |= (1 << PWM_PIN); // enable main channel DDRB |= (1 << ALT_PWM_PIN); // enable second channel // 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...) TCCR0A = PHASE; // Set timer to do PWM for correct output pin and set prescaler timing TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...) // Read config values and saved state restore_state(); // Enable the current mode group count_modes(); // TODO: Enable this? (might prevent some corner cases, but requires extra room) // memory decayed, reset it // (should happen on med/long press instead // because mem decay is *much* slower when the OTC is charged // so let's not wait until it decays to reset it) //if (fast_presses > 0x20) { fast_presses = 0; } // check button press time, unless the mode is overridden if (! mode_override) { if (cap_val > CAP_SHORT) { // Indicates they did a short press, go to the next mode // We don't care what the fast_presses value is as long as it's over 15 fast_presses = (fast_presses+1) & 0x1f; next_mode(); // Will handle wrap arounds #ifdef OFFTIM3 } else if (cap_val > CAP_MED) { // User did a medium press, go back one mode fast_presses = 0; if (offtim3) { prev_mode(); // Will handle "negative" modes and wrap-arounds } else { next_mode(); // disabled-med-press acts like short-press // (except that fast_presses isn't reliable then) } #endif } else { // Long press, keep the same mode // ... or reset to the first mode fast_presses = 0; if (muggle_mode || (! memory)) { // Reset to the first mode mode_idx = 0; } } } save_mode(); // Charge up the capacitor by setting CAP_PIN to output DDRB |= (1 << CAP_PIN); // Output PORTB |= (1 << CAP_PIN); // High // Turn features on or off as needed #ifdef VOLTAGE_MON ADC_on(); #else ADC_off(); #endif uint8_t output; uint8_t actual_level; #ifdef TEMPERATURE_MON uint8_t overheat_count = 0; #endif #ifdef VOLTAGE_MON uint8_t lowbatt_cnt = 0; uint8_t i = 0; uint8_t voltage; // Make sure voltage reading is running for later ADCSRA |= (1 << ADSC); #endif //output = pgm_read_byte(modes + mode_idx); output = modes[mode_idx]; actual_level = output; // handle mode overrides, like mode group selection and temperature calibration if (mode_override) { // do nothing; mode is already set //mode_idx = mode_override; fast_presses = 0; output = mode_idx; } while(1) { if (fast_presses > 0x0f) { // Config mode _delay_s(); // wait for user to stop fast-pressing button fast_presses = 0; // exit this mode after one use mode_idx = 0; // Enter or leave "muggle mode"? toggle(&muggle_mode, 1); if (muggle_mode) { continue; }; // don't offer other options in muggle mode toggle(&memory, 2); toggle(&enable_moon, 3); toggle(&reverse_modes, 4); // Enter the mode group selection mode? mode_idx = GROUP_SELECT_MODE; toggle(&mode_override, 5); mode_idx = 0; #ifdef OFFTIM3 toggle(&offtim3, 6); #endif #ifdef TEMPERATURE_MON // Enter temperature calibration mode? mode_idx = TEMP_CAL_MODE; toggle(&mode_override, 7); mode_idx = 0; #endif toggle(&firstboot, 8); //output = pgm_read_byte(modes + mode_idx); output = modes[mode_idx]; actual_level = output; } #ifdef STROBE else if (output == STROBE) { // 10Hz tactical strobe strobe(50,50); } #endif // ifdef STROBE #ifdef POLICE_STROBE else if (output == POLICE_STROBE) { // police-like strobe for(i=0;i<8;i++) { strobe(20,40); } for(i=0;i<8;i++) { strobe(40,80); } } #endif // ifdef POLICE_STROBE #ifdef RANDOM_STROBE else if (output == RANDOM_STROBE) { // pseudo-random strobe uint8_t ms = 34 + (pgm_rand() & 0x3f); strobe(ms, ms); strobe(ms, ms); } #endif // ifdef RANDOM_STROBE #ifdef BIKING_STROBE else if (output == BIKING_STROBE) { // 2-level stutter beacon for biking and such #ifdef FULL_BIKING_STROBE // normal version for(i=0;i<4;i++) { set_output(255,0); _delay_ms(5); set_output(0,255); _delay_ms(65); } _delay_ms(720); #else // small/minimal version set_output(255,0); _delay_ms(10); set_output(0,255); _delay_s(); #endif } #endif // ifdef BIKING_STROBE #ifdef RAMP else if (output == RAMP) { int8_t r; // simple ramping test for(r=1; r<=RAMP_SIZE; r++) { set_level(r); _delay_ms(25); } for(r=RAMP_SIZE; r>0; r--) { set_level(r); _delay_ms(25); } } #endif // ifdef RAMP #ifdef BATTCHECK else if (output == BATTCHECK) { #ifdef BATTCHECK_VpT // blink out volts and tenths 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 // ifdef BATTCHECK_VpT // blink zero to five times to show voltage // (~0%, ~25%, ~50%, ~75%, ~100%, >100%) blink(battcheck(), BLINK_SPEED/8); #endif // ifdef BATTCHECK_VpT // wait between readouts _delay_s(); _delay_s(); } #endif // ifdef BATTCHECK else if (output == GROUP_SELECT_MODE) { // exit this mode after one use mode_idx = 0; mode_override = 0; for(i=0; i<NUM_MODEGROUPS; i++) { modegroup = i; save_state(); blink(1, BLINK_SPEED/3); } _delay_s(); _delay_s(); } #ifdef TEMP_CAL_MODE else if (output == TEMP_CAL_MODE) { // make sure we don't stay in this mode after button press mode_idx = 0; mode_override = 0; // Allow the user to turn off thermal regulation if they want maxtemp = 255; save_state(); set_mode(RAMP_SIZE/4); // start somewhat dim during turn-off-regulation mode _delay_s(); _delay_s(); // run at highest output level, to generate heat set_mode(RAMP_SIZE); // measure, save, wait... repeat while(1) { maxtemp = get_temperature(); save_state(); _delay_s(); _delay_s(); } } #endif // TEMP_CAL_MODE else { // Regular non-hidden solid mode set_mode(actual_level); #ifdef TEMPERATURE_MON uint8_t temp = get_temperature(); // step down? (or step back up?) if (temp >= maxtemp) { overheat_count ++; // reduce noise, and limit the lowest step-down level if ((overheat_count > 15) && (actual_level > (RAMP_SIZE/8))) { actual_level --; //_delay_ms(5000); // don't ramp down too fast overheat_count = 0; // don't ramp down too fast } } else { // if we're not overheated, ramp up to the user-requested level overheat_count = 0; if ((temp < maxtemp - 2) && (actual_level < output)) { actual_level ++; } } set_mode(actual_level); ADC_on(); // return to voltage mode #endif // Otherwise, just sleep. _delay_ms(500); // If we got this far, the user has stopped fast-pressing. // So, don't enter config mode. fast_presses = 0; } #ifdef VOLTAGE_MON if (ADCSRA & (1 << ADIF)) { // if a voltage reading is ready voltage = ADCH; // get the waiting value // See if voltage is lower than what we were looking for if (voltage < ADC_LOW) { lowbatt_cnt ++; } else { lowbatt_cnt = 0; } // See if it's been low for a while, and maybe step down if (lowbatt_cnt >= 8) { // DEBUG: blink on step-down: //set_level(0); _delay_ms(100); if (actual_level > RAMP_SIZE) { // hidden / blinky modes // step down from blinky modes to medium actual_level = RAMP_SIZE / 2; } else if (actual_level > 1) { // regular solid mode // step down from solid modes somewhat gradually // drop by 25% each time actual_level = (actual_level >> 2) + (actual_level >> 1); // drop by 50% each time //actual_level = (actual_level >> 1); } else { // Already at the lowest mode //mode_idx = 0; // unnecessary; we never leave this clause //actual_level = 0; // unnecessary; we never leave this clause // Turn off the light set_level(0); // Power down as many components as possible set_sleep_mode(SLEEP_MODE_PWR_DOWN); sleep_mode(); } set_mode(actual_level); output = actual_level; //save_mode(); // we didn't actually change the mode lowbatt_cnt = 0; // Wait at least 2 seconds before lowering the level again _delay_ms(250); // this will interrupt blinky modes } // Make sure conversion is running for next time through ADCSRA |= (1 << ADSC); } #endif // ifdef VOLTAGE_MON } //return 0; // Standard Return Code }
変更されたテキスト
ファイルを開く
/* * "Bistro" firmware * This code runs on a single-channel or dual-channel driver (FET+7135) * with an attiny25/45/85 MCU and a capacitor to measure offtime (OTC). * * Copyright (C) 2015 Selene Scriven * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * * ATTINY25/45/85 Diagram * ---- * -|1 8|- VCC * OTC -|2 7|- Voltage ADC * Star 3 -|3 6|- PWM (FET, optional) * GND -|4 5|- PWM (1x7135) * ---- * * FUSES * I use these fuse settings on attiny25 * Low: 0xd2 * High: 0xde * Ext: 0xff * * For more details on these settings: * 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 * * STARS * Star 3 = unused * * CALIBRATION * * To find out what values to use, flash the driver with battcheck.hex * and hook the light up to each voltage you need a value for. This is * much more reliable than attempting to calculate the values from a * theoretical formula. * * Same for off-time capacitor values. Measure, don't guess. */ // Choose your MCU here, or in the build script //#define ATTINY 13 //#define ATTINY 25 // FIXME: make 1-channel vs 2-channel power a single #define option #define FET_7135_LAYOUT // specify an I/O pin layout // Also, assign I/O pins in this file: #include "tk-attiny.h" /* * ========================================================================= * Settings to modify per driver */ // FIXME: make 1-channel vs 2-channel power a single #define option //#define FAST 0x23 // fast PWM channel 1 only //#define PHASE 0x21 // phase-correct PWM channel 1 only #define FAST 0xA3 // fast PWM both channels #define PHASE 0xA1 // phase-correct PWM both channels #define VOLTAGE_MON // Comment out to disable LVP #define OFFTIM3 // Use short/med/long off-time presses // instead of just short/long // ../../bin/level_calc.py 64 1 10 1300 y 3 0.23 140 #define RAMP_SIZE 64 // log curve //#define RAMP_7135 3,3,3,3,3,3,4,4,4,4,4,5,5,5,6,6,7,7,8,9,10,11,12,13,15,16,18,21,23,27,30,34,39,44,50,57,65,74,85,97,111,127,145,166,190,217,248,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 //#define RAMP_FET 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,6,11,17,23,30,39,48,59,72,86,103,121,143,168,197,255 // x**2 curve //#define RAMP_7135 3,5,8,12,17,24,32,41,51,63,75,90,105,121,139,158,178,200,223,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 //#define RAMP_FET 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,6,9,12,16,19,22,26,30,33,37,41,45,50,54,59,63,68,73,78,84,89,94,100,106,111,117,123,130,136,142,149,156,162,169,176,184,191,198,206,214,221,255 // x**3 curve #define RAMP_7135 3,3,4,5,6,8,10,12,15,19,23,28,33,40,47,55,63,73,84,95,108,122,137,153,171,190,210,232,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 #define RAMP_FET 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,5,8,11,14,18,22,26,30,34,39,44,49,54,59,65,71,77,84,91,98,105,113,121,129,137,146,155,164,174,184,194,205,216,255 // x**5 curve //#define RAMP_7135 3,3,3,4,4,5,5,6,7,8,10,11,13,15,18,21,24,28,33,38,44,50,57,66,75,85,96,108,122,137,154,172,192,213,237,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 //#define RAMP_FET 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,6,9,13,17,21,25,30,35,41,47,53,60,67,75,83,91,101,111,121,132,144,156,169,183,198,213,255 // uncomment to ramp up/down to a mode instead of jumping directly #define SOFT_START // Enable battery indicator mode? #define USE_BATTCHECK // Choose a battery indicator style //#define BATTCHECK_4bars // up to 4 blinks //#define BATTCHECK_8bars // up to 8 blinks #define BATTCHECK_VpT // Volts + tenths // output to use for blinks on battery check (and other modes) #define BLINK_BRIGHTNESS RAMP_SIZE/4 // ms per normal-speed blink #define BLINK_SPEED 500 // Hidden modes are *before* the lowest (moon) mode, and should be specified // in reverse order. So, to go backward from moon to turbo to strobe to // battcheck, use BATTCHECK,STROBE,TURBO . #define HIDDENMODES BIKING_STROBE,BATTCHECK,POLICE_STROBE,TURBO #define TURBO RAMP_SIZE // Convenience code for turbo mode #define BATTCHECK 254 // Convenience code for battery check mode #define GROUP_SELECT_MODE 253 #define TEMP_CAL_MODE 252 // Uncomment to enable tactical strobe mode //#define STROBE 251 // Convenience code for strobe mode // Uncomment to unable a 2-level stutter beacon instead of a tactical strobe #define BIKING_STROBE 250 // Convenience code for biking strobe mode // comment out to use minimal version instead (smaller) #define FULL_BIKING_STROBE //#define RAMP 249 // ramp test mode for tweaking ramp shape #define POLICE_STROBE 248 //#define RANDOM_STROBE 247 // thermal step-down #define TEMPERATURE_MON // Calibrate voltage and OTC in this file: #include "tk-calibration.h" /* * ========================================================================= */ // Ignore a spurious warning, we did the cast on purpose #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" #include <avr/pgmspace.h> //#include <avr/io.h> //#include <avr/interrupt.h> #include <avr/eeprom.h> #include <avr/sleep.h> //#include <avr/power.h> #include <string.h> #define OWN_DELAY // Don't use stock delay functions. #define USE_DELAY_S // Also use _delay_s(), not just _delay_ms() #include "tk-delay.h" #include "tk-voltage.h" #ifdef RANDOM_STROBE #include "tk-random.h" #endif /* * global variables */ // Config option variables #define FIRSTBOOT 0b01010101 uint8_t firstboot = FIRSTBOOT; // detect initial boot or factory reset uint8_t modegroup = 5; // which mode group (set above in #defines) uint8_t enable_moon = 1; // Should we add moon to the set of modes? uint8_t reverse_modes = 0; // flip the mode order? uint8_t memory = 0; // mode memory, or not (set via soldered star) #ifdef OFFTIM3 uint8_t offtim3 = 1; // enable medium-press? #endif #ifdef TEMPERATURE_MON uint8_t maxtemp = 79; // temperature step-down threshold #endif uint8_t muggle_mode = 0; // simple mode designed for muggles // Other state variables uint8_t mode_override = 0; // do we need to enter a special mode? uint8_t mode_idx = 0; // current or last-used mode number uint8_t eepos = 0; // counter for entering config mode // (needs to be remembered while off, but only for up to half a second) uint8_t fast_presses __attribute__ ((section (".noinit"))); // total length of current mode group's array uint8_t mode_cnt; // number of regular non-hidden modes in current mode group uint8_t solid_modes; // number of hidden modes in the current mode group // (hardcoded because both groups have the same hidden modes) //uint8_t hidden_modes = NUM_HIDDEN; // this is never used PROGMEM const uint8_t hiddenmodes[] = { HIDDENMODES }; // default values calculated by group_calc.py // Each group must be 8 values long, but can be cut short with a zero. #define NUM_MODEGROUPS 9 // don't count muggle mode PROGMEM const uint8_t modegroups[] = { 64, 0, 0, 0, 0, 0, 0, 0, 11, 64, 0, 0, 0, 0, 0, 0, 11, 35, 64, 0, 0, 0, 0, 0, 11, 26, 46, 64, 0, 0, 0, 0, 11, 23, 36, 50, 64, 0, 0, 0, 11, 20, 31, 41, 53, 64, 0, 0, 29, 64,POLICE_STROBE,0,0,0,0,0, // 7: special group A BIKING_STROBE,BATTCHECK,11,29,64,0,0,0, // 8: special group B 9, 18, 29, 46, 64, 0, 0, 0, // 9: special group C 11, 29, 50, 0, // muggle mode, exception to "must be 8 bytes long" }; uint8_t modes[] = { 1,2,3,4,5,6,7,8,9, HIDDENMODES }; // make sure this is long enough... // Modes (gets set when the light starts up based on saved config values) PROGMEM const uint8_t ramp_7135[] = { RAMP_7135 }; PROGMEM const uint8_t ramp_FET[] = { RAMP_FET }; void save_mode() { // save the current mode index (with wear leveling) uint8_t oldpos=eepos; eepos = (eepos+1) & ((EEPSIZE/2)-1); // wear leveling, use next cell eeprom_write_byte((uint8_t *)(eepos), mode_idx); // save current state eeprom_write_byte((uint8_t *)(oldpos), 0xff); // erase old state } #define OPT_firstboot (EEPSIZE-1) #define OPT_modegroup (EEPSIZE-2) #define OPT_memory (EEPSIZE-3) #define OPT_offtim3 (EEPSIZE-4) #define OPT_maxtemp (EEPSIZE-5) #define OPT_mode_override (EEPSIZE-6) #define OPT_moon (EEPSIZE-7) #define OPT_revmodes (EEPSIZE-8) #define OPT_muggle (EEPSIZE-9) void save_state() { // central method for writing complete state save_mode(); eeprom_write_byte((uint8_t *)OPT_firstboot, firstboot); eeprom_write_byte((uint8_t *)OPT_modegroup, modegroup); eeprom_write_byte((uint8_t *)OPT_memory, memory); #ifdef OFFTIM3 eeprom_write_byte((uint8_t *)OPT_offtim3, offtim3); #endif #ifdef TEMPERATURE_MON eeprom_write_byte((uint8_t *)OPT_maxtemp, maxtemp); #endif eeprom_write_byte((uint8_t *)OPT_mode_override, mode_override); eeprom_write_byte((uint8_t *)OPT_moon, enable_moon); eeprom_write_byte((uint8_t *)OPT_revmodes, reverse_modes); eeprom_write_byte((uint8_t *)OPT_muggle, muggle_mode); } void restore_state() { uint8_t eep; // check if this is the first time we have powered on eep = eeprom_read_byte((uint8_t *)OPT_firstboot); if (eep != FIRSTBOOT) { // not much to do; the defaults should already be set // while defining the variables above save_state(); return; } // find the mode index data for(eepos=0; eepos<(EEPSIZE/2); eepos++) { eep = eeprom_read_byte((const uint8_t *)eepos); if (eep != 0xff) { mode_idx = eep; break; } } // load other config values modegroup = eeprom_read_byte((uint8_t *)OPT_modegroup); memory = eeprom_read_byte((uint8_t *)OPT_memory); #ifdef OFFTIM3 offtim3 = eeprom_read_byte((uint8_t *)OPT_offtim3); #endif #ifdef TEMPERATURE_MON maxtemp = eeprom_read_byte((uint8_t *)OPT_maxtemp); #endif mode_override = eeprom_read_byte((uint8_t *)OPT_mode_override); enable_moon = eeprom_read_byte((uint8_t *)OPT_moon); reverse_modes = eeprom_read_byte((uint8_t *)OPT_revmodes); muggle_mode = eeprom_read_byte((uint8_t *)OPT_muggle); // unnecessary, save_state handles wrap-around // (and we don't really care about it skipping cell 0 once in a while) //else eepos=0; } inline void next_mode() { mode_idx += 1; if (mode_idx >= solid_modes) { // Wrap around, skipping the hidden modes // (note: this also applies when going "forward" from any hidden mode) // FIXME? Allow this to cycle through hidden modes? mode_idx = 0; } } #ifdef OFFTIM3 inline void prev_mode() { // simple mode has no reverse if (muggle_mode) { return next_mode(); } if (mode_idx == solid_modes) { // If we hit the end of the hidden modes, go back to moon mode_idx = 0; } else if (mode_idx > 0) { // Regular mode: is between 1 and TOTAL_MODES mode_idx -= 1; } else { // Otherwise, wrap around (this allows entering hidden modes) mode_idx = mode_cnt - 1; } } #endif void count_modes() { /* * Determine how many solid and hidden modes we have. * * (this matters because we have more than one set of modes to choose * from, so we need to count at runtime) */ // copy config to local vars to avoid accidentally overwriting them in muggle mode // (also, it seems to reduce overall program size) uint8_t my_modegroup = modegroup; uint8_t my_enable_moon = enable_moon; uint8_t my_reverse_modes = reverse_modes; // override config if we're in simple mode if (muggle_mode) { my_modegroup = NUM_MODEGROUPS; my_enable_moon = 0; my_reverse_modes = 0; } uint8_t *dest; const uint8_t *src = modegroups + (my_modegroup<<3); dest = modes; // Figure out how many modes are in this group //solid_modes = modegroup + 1; // Assume group N has N modes // No, how about actually counting the modes instead? // (in case anyone changes the mode groups above so they don't form a triangle) for(solid_modes=0; (solid_modes<8) && pgm_read_byte(src + solid_modes); solid_modes++ ) {} // add moon mode (or not) if config says to add it if (my_enable_moon) { modes[0] = 1; dest ++; } // add regular modes memcpy_P(dest, src, solid_modes); // add hidden modes memcpy_P(dest + solid_modes, hiddenmodes, sizeof(hiddenmodes)); // final count mode_cnt = solid_modes + sizeof(hiddenmodes); if (my_reverse_modes) { // TODO: yuck, isn't there a better way to do this? int8_t i; src += solid_modes; dest = modes; for(i=0; i<solid_modes; i++) { src --; *dest = pgm_read_byte(src); dest ++; } if (my_enable_moon) { *dest = 1; } mode_cnt --; // get rid of last hidden mode, since it's a duplicate turbo } if (my_enable_moon) { mode_cnt ++; solid_modes ++; } } inline void set_output(uint8_t pwm1, uint8_t pwm2) { /* This is no longer needed since we always use PHASE mode. // Need PHASE to properly turn off the light if ((pwm1==0) && (pwm2==0)) { TCCR0A = PHASE; } */ PWM_LVL = pwm1; ALT_PWM_LVL = pwm2; } void set_level(uint8_t level) { if (level == 0) { set_output(0,0); } else { level -= 1; set_output(pgm_read_byte(ramp_FET + level), pgm_read_byte(ramp_7135 + level)); } } void set_mode(uint8_t mode) { #ifdef SOFT_START static uint8_t actual_level = 0; uint8_t target_level = mode; int8_t shift_amount; int8_t diff; do { diff = target_level - actual_level; shift_amount = (diff >> 2) | (diff!=0); actual_level += shift_amount; set_level(actual_level); //_delay_ms(RAMP_SIZE/20); // slow ramp _delay_ms(RAMP_SIZE/4); // fast ramp } while (target_level != actual_level); #else set_level(mode); #endif // SOFT_START } void blink(uint8_t val, uint16_t speed) { for (; val>0; val--) { set_level(BLINK_BRIGHTNESS); _delay_ms(speed); set_level(0); _delay_ms(speed<<2); } } void strobe(uint8_t ontime, uint8_t offtime) { set_level(RAMP_SIZE); _delay_ms(ontime); set_level(0); _delay_ms(offtime); } void toggle(uint8_t *var, uint8_t num) { // Used for config mode // Changes the value of a config option, waits for the user to "save" // by turning the light off, then changes the value back in case they // didn't save. Can be used repeatedly on different options, allowing // the user to change and save only one at a time. blink(num, BLINK_SPEED/8); // indicate which option number this is *var ^= 1; save_state(); // "buzz" for a while to indicate the active toggle window for(uint8_t i=0; i<32; i++) { set_level(BLINK_BRIGHTNESS * 3 / 4); _delay_ms(20); set_level(0); _delay_ms(20); } // if the user didn't click, reset the value and return *var ^= 1; save_state(); _delay_s(); } #ifdef TEMPERATURE_MON uint8_t get_temperature() { ADC_on_temperature(); // average a few values; temperature is noisy uint16_t temp = 0; uint8_t i; get_voltage(); for(i=0; i<16; i++) { temp += get_voltage(); _delay_ms(5); } temp >>= 4; return temp; } #endif // TEMPERATURE_MON inline uint8_t read_otc() { // Read and return the off-time cap value // Start up ADC for capacitor pin // disable digital input on ADC pin to reduce power consumption DIDR0 |= (1 << CAP_DIDR); // 1.1v reference, left-adjust, ADC3/PB3 ADMUX = (1 << V_REF) | (1 << ADLAR) | CAP_CHANNEL; // enable, start, prescale ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL; // 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)); // ADCH should have the value we wanted return ADCH; } int main(void) { // check the OTC immediately before it has a chance to charge or discharge uint8_t cap_val = read_otc(); // save it for later // Set PWM pin to output DDRB |= (1 << PWM_PIN); // enable main channel DDRB |= (1 << ALT_PWM_PIN); // enable second channel // 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...) TCCR0A = PHASE; // Set timer to do PWM for correct output pin and set prescaler timing TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...) // Read config values and saved state restore_state(); // Enable the current mode group count_modes(); // TODO: Enable this? (might prevent some corner cases, but requires extra room) // memory decayed, reset it // (should happen on med/long press instead // because mem decay is *much* slower when the OTC is charged // so let's not wait until it decays to reset it) //if (fast_presses > 0x20) { fast_presses = 0; } // check button press time, unless the mode is overridden if (! mode_override) { if (cap_val > CAP_SHORT) { // Indicates they did a short press, go to the next mode // We don't care what the fast_presses value is as long as it's over 15 fast_presses = (fast_presses+1) & 0x1f; next_mode(); // Will handle wrap arounds #ifdef OFFTIM3 } else if (cap_val > CAP_MED) { // User did a medium press, go back one mode fast_presses = 0; if (offtim3) { prev_mode(); // Will handle "negative" modes and wrap-arounds } else { next_mode(); // disabled-med-press acts like short-press // (except that fast_presses isn't reliable then) } #endif } else { // Long press, keep the same mode // ... or reset to the first mode fast_presses = 0; if (muggle_mode || (! memory)) { // Reset to the first mode mode_idx = 0; } } } save_mode(); // Charge up the capacitor by setting CAP_PIN to output DDRB |= (1 << CAP_PIN); // Output PORTB |= (1 << CAP_PIN); // High // Turn features on or off as needed #ifdef VOLTAGE_MON ADC_on(); #else ADC_off(); #endif uint8_t output; uint8_t actual_level; #ifdef TEMPERATURE_MON uint8_t overheat_count = 0; #endif #ifdef VOLTAGE_MON uint8_t lowbatt_cnt = 0; uint8_t i = 0; uint8_t voltage; // Make sure voltage reading is running for later ADCSRA |= (1 << ADSC); #endif //output = pgm_read_byte(modes + mode_idx); output = modes[mode_idx]; actual_level = output; // handle mode overrides, like mode group selection and temperature calibration if (mode_override) { // do nothing; mode is already set //mode_idx = mode_override; fast_presses = 0; output = mode_idx; } while(1) { if (fast_presses > 0x0f) { // Config mode _delay_s(); // wait for user to stop fast-pressing button fast_presses = 0; // exit this mode after one use mode_idx = 0; // Enter or leave "muggle mode"? toggle(&muggle_mode, 1); if (muggle_mode) { continue; }; // don't offer other options in muggle mode toggle(&memory, 2); toggle(&enable_moon, 3); toggle(&reverse_modes, 4); // Enter the mode group selection mode? mode_idx = GROUP_SELECT_MODE; toggle(&mode_override, 5); mode_idx = 0; #ifdef OFFTIM3 toggle(&offtim3, 6); #endif #ifdef TEMPERATURE_MON // Enter temperature calibration mode? mode_idx = TEMP_CAL_MODE; toggle(&mode_override, 7); mode_idx = 0; #endif toggle(&firstboot, 8); //output = pgm_read_byte(modes + mode_idx); output = modes[mode_idx]; actual_level = output; } #ifdef STROBE else if (output == STROBE) { // 10Hz tactical strobe strobe(50,50); } #endif // ifdef STROBE #ifdef POLICE_STROBE else if (output == POLICE_STROBE) { // police-like strobe for(i=0;i<8;i++) { strobe(20,40); } for(i=0;i<8;i++) { strobe(40,80); } } #endif // ifdef POLICE_STROBE #ifdef RANDOM_STROBE else if (output == RANDOM_STROBE) { // pseudo-random strobe uint8_t ms = 34 + (pgm_rand() & 0x3f); strobe(ms, ms); strobe(ms, ms); } #endif // ifdef RANDOM_STROBE #ifdef BIKING_STROBE else if (output == BIKING_STROBE) { // 2-level stutter beacon for biking and such #ifdef FULL_BIKING_STROBE // normal version for(i=0;i<4;i++) { set_output(255,0); _delay_ms(5); set_output(0,255); _delay_ms(65); } _delay_ms(720); #else // small/minimal version set_output(255,0); _delay_ms(10); set_output(0,255); _delay_s(); #endif } #endif // ifdef BIKING_STROBE #ifdef RAMP else if (output == RAMP) { int8_t r; // simple ramping test for(r=1; r<=RAMP_SIZE; r++) { set_level(r); _delay_ms(25); } for(r=RAMP_SIZE; r>0; r--) { set_level(r); _delay_ms(25); } } #endif // ifdef RAMP #ifdef BATTCHECK else if (output == BATTCHECK) { #ifdef BATTCHECK_VpT // blink out volts and tenths 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 // ifdef BATTCHECK_VpT // blink zero to five times to show voltage // (~0%, ~25%, ~50%, ~75%, ~100%, >100%) blink(battcheck(), BLINK_SPEED/8); #endif // ifdef BATTCHECK_VpT // wait between readouts _delay_s(); _delay_s(); } #endif // ifdef BATTCHECK else if (output == GROUP_SELECT_MODE) { // exit this mode after one use mode_idx = 0; mode_override = 0; for(i=0; i<NUM_MODEGROUPS; i++) { modegroup = i; save_state(); blink(1, BLINK_SPEED/3); } _delay_s(); _delay_s(); } #ifdef TEMP_CAL_MODE else if (output == TEMP_CAL_MODE) { // make sure we don't stay in this mode after button press mode_idx = 0; mode_override = 0; // Allow the user to turn off thermal regulation if they want maxtemp = 255; save_state(); set_mode(RAMP_SIZE/4); // start somewhat dim during turn-off-regulation mode _delay_s(); _delay_s(); // run at highest output level, to generate heat set_mode(RAMP_SIZE); // measure, save, wait... repeat while(1) { maxtemp = get_temperature(); save_state(); _delay_s(); _delay_s(); } } #endif // TEMP_CAL_MODE else { // Regular non-hidden solid mode set_mode(actual_level); #ifdef TEMPERATURE_MON uint8_t temp = get_temperature(); // step down? (or step back up?) if (temp >= maxtemp) { overheat_count ++; // reduce noise, and limit the lowest step-down level if ((overheat_count > 15) && (actual_level > (RAMP_SIZE/8))) { actual_level --; //_delay_ms(5000); // don't ramp down too fast overheat_count = 0; // don't ramp down too fast } } else { // if we're not overheated, ramp up to the user-requested level overheat_count = 0; if ((temp < maxtemp - 2) && (actual_level < output)) { actual_level ++; } } set_mode(actual_level); ADC_on(); // return to voltage mode #endif // Otherwise, just sleep. _delay_ms(500); // If we got this far, the user has stopped fast-pressing. // So, don't enter config mode. fast_presses = 0; } #ifdef VOLTAGE_MON if (ADCSRA & (1 << ADIF)) { // if a voltage reading is ready voltage = ADCH; // get the waiting value // See if voltage is lower than what we were looking for if (voltage < ADC_LOW) { lowbatt_cnt ++; } else { lowbatt_cnt = 0; } // See if it's been low for a while, and maybe step down if (lowbatt_cnt >= 8) { // DEBUG: blink on step-down: //set_level(0); _delay_ms(100); if (actual_level > RAMP_SIZE) { // hidden / blinky modes // step down from blinky modes to medium actual_level = RAMP_SIZE / 2; } else if (actual_level > 1) { // regular solid mode // step down from solid modes somewhat gradually // drop by 25% each time actual_level = (actual_level >> 2) + (actual_level >> 1); // drop by 50% each time //actual_level = (actual_level >> 1); } else { // Already at the lowest mode //mode_idx = 0; // unnecessary; we never leave this clause //actual_level = 0; // unnecessary; we never leave this clause // Turn off the light set_level(0); // Power down as many components as possible set_sleep_mode(SLEEP_MODE_PWR_DOWN); sleep_mode(); } set_mode(actual_level); output = actual_level; //save_mode(); // we didn't actually change the mode lowbatt_cnt = 0; // Wait at least 2 seconds before lowering the level again _delay_ms(250); // this will interrupt blinky modes } // Make sure conversion is running for next time through ADCSRA |= (1 << ADSC); } #endif // ifdef VOLTAGE_MON } //return 0; // Standard Return Code }
違いを見つける