/
sharkdrv.c
294 lines (274 loc) · 9.67 KB
/
sharkdrv.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
//
// SharkDrv 0.1
//
// License: Released to the Public Domain.
//
// For use in 105d type drivers, no support for stars.
//
//###############################################################
// Start user tweaking, don't comment out, just edit values
//###############################################################
// Mode output levels
#define MODE001 3
#define MODE002 5
#define MODE003 8
#define MODE004 10
#define MODE005 13
#define MODE007 18
#define MODE010 26
#define MODE015 38
#define MODE020 51
#define MODE025 64
#define MODE030 77
#define MODE035 89
#define MODE040 102
#define MODE045 115
#define MODE050 128
#define MODE060 153
#define MODE070 179
#define MODE080 204
#define MODE100 255
// Identifiers for special modes
#define MODE_PROGRAM 254 // Just an id dummy number, must not be used for other modes!
#define MODE_STROBE 253 // Just an id dummy number, must not be used for other modes!
// Mode groups
#define GROUP00 {MODE010, MODE050, MODE100}
#define GROUP01 {MODE010, MODE050, MODE100, MODE_STROBE}
#define GROUP02 {MODE_RAMPING, MODE010, MODE050, MODE100}
#define GROUP03 {MODE_RAMPING, MODE007, MODE020, MODE060, MODE100}
#define GROUP04 {MODE100, MODE050, MODE010}
#define GROUP05 {MODE100, MODE050, MODE010, MODE_STROBE}
#define GROUP06 {MODE100}
// Strobe settings
#define STROBE_ON 40 // Strobe on time (ms)
#define STROBE_OFF 40 // Strobe off time (ms)
#define STROBE_ON_OUT 255 // Strobe output level (0-255)
#define STROBE_OFF_OUT 0 // Strobe output level (0-255)
// Battery monitoring
#define BATT_MON 1 // Enable battery monitoring, 0 off and 1 on
#define BATT_TIMEOUT 30 // Number of seconds between checks (10-200)
#define ADC_LOW 130 // When do we start ramping
#define ADC_CRIT 120 // When do we shut the light off
#define ADC_LOW_OUT 20 // Output level in low battery mode (0-255)
// Misc settings
#define MODE_TIMEOUT 2 // Number of seconds before mode is saved (1-9)
#define FAST_PWM_START 8 // Above what output level should we switch from phase correct to fast-PWM?
#define PROGRAM_SPRESS 9 // Number of short presses to enter program mode, from 0
#define PROGRAM_MODES 5 // Number of program modes
#define PROGRAM_PAUSE 150 // Pause between blinks (5ms)
#define PROGRAM_OUT MODE020 // Output level (1-255)
#define PROGRAM_BLINKS 20 // Number of strobe blinks when entering program mode
#define PROGRAM_DELAY 45 // Delay between blinks when entering program mode (5ms)
#define MODE100_LOW MODE050 // Output level (1-255)
//################################
// End user tweaking
//################################
#define F_CPU 4800000UL // CPU frequency
#define PWM_PIN PB1 // Default PWM pin
#define PWM_LVL OCR0B // OCR0B is the output compare register for PB1
#define PWM_FAST 0x23 // Fast-PWM
#define PWM_PHASE 0x21 // Phase corrected PWM
#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
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <avr/eeprom.h>
#include <avr/sleep.h>
//### Globals start ###
uint8_t eepos; // Mode byte position in eeprom
uint8_t mode_idx = 0; // Mode position in modes array
uint8_t mode_memory; // Mode memory
uint8_t mypwm = 100; // Mode identifier/output level
uint8_t spress_cnt = 0; // Short press counter
uint8_t ftimer; // Full mode timer
uint8_t strobe_delay; // Strobe frequency delay
//### Globals end ###
static void delay_5ms(uint8_t n) { // Use own delay function
while(n-- > 0) {
_delay_ms(5);
}
}
void get_mypwm(const uint8_t modes[], uint8_t mode_cnt) {
if (spress_cnt >= PROGRAM_SPRESS && spress_cnt < PROGRAM_SPRESS + PROGRAM_MODES) {
mypwm = 254; // Enter program mode
} else {
if (mode_idx >= mode_cnt) {
mode_idx = 0; // Wrap around
}
mypwm = modes[mode_idx]; // Get mode identifier/output level
}
}
inline void get_mode() { // Get mode and store with short press indicator
uint8_t groupint;
uint8_t MODE_RAMPING;
uint8_t oldpos;
groupint = eeprom_read_byte((const uint8_t *)(uint16_t)0); // Number of group array
mode_memory = eeprom_read_byte((const uint8_t *)(uint16_t)1) - 1; // Mode memory
ftimer = eeprom_read_byte((const uint8_t *)(uint16_t)2) - 1; // Number of timer seconds
strobe_delay = eeprom_read_byte((const uint8_t *)(uint16_t)3); // Strobe delay
MODE_RAMPING = eeprom_read_byte((const uint8_t *)(uint16_t)4); // Ramping output level
if (MODE_RAMPING > 250) {
MODE_RAMPING = 255;
}
for (oldpos = 5; oldpos < 63; oldpos++) {
mode_idx = eeprom_read_byte((const uint8_t *)(uint16_t)oldpos);
if (mode_idx != 0xff) {
break;
}
}
eepos = oldpos + 1; // Wear leveling, use next cell
uint8_t oldspos = eepos;
if (eepos > 62) {
eepos = 5;
}
if (mode_idx & 0x10) { // Indicates we did a short press last time
mode_idx &= 0x0f; // Remove short press indicator
mode_idx++; // Go to the next mode
spress_cnt = eeprom_read_byte((const uint8_t *)(uint16_t)oldspos);
if (spress_cnt != 0xff) {
spress_cnt++;
}
}
//const uint8_t memarray[] = MODE_MEMORY;
//mode_memory = memarray[modesarr];
//if (modesarr > 7) {
// mode_memory = 1;
//}
if (groupint == 2) {
const uint8_t modes[] = GROUP01;
get_mypwm(modes, sizeof(modes));
} else if (groupint == 3) {
const uint8_t modes[] = GROUP02;
get_mypwm(modes, sizeof(modes));
} else if (groupint == 4) {
const uint8_t modes[] = GROUP03;
get_mypwm(modes, sizeof(modes));
} else if (groupint == 5) {
const uint8_t modes[] = GROUP04;
get_mypwm(modes, sizeof(modes));
} else if (groupint == 6) {
const uint8_t modes[] = GROUP05;
get_mypwm(modes, sizeof(modes));
} else if (groupint == 7) {
const uint8_t modes[] = GROUP06;
get_mypwm(modes, sizeof(modes));
} else {
const uint8_t modes[] = GROUP00;
get_mypwm(modes, sizeof(modes));
}
eeprom_write_byte((uint8_t *)(uint16_t)(eepos), (mode_idx | 0x10)); // Store current mode
eeprom_write_byte((uint8_t *)(uint16_t)(oldpos), 0xff); // Erase old mode
eeprom_write_byte((uint8_t *)(uint16_t)(eepos + 1), spress_cnt); // Store short press counter
}
inline void WDT_on() { // Setup watchdog timer to only interrupt, not reset
cli(); // Disable interrupts
wdt_reset(); // Reset the WDT
WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence
WDTCR = (1<<WDTIE) | (1<<WDP2) | (1<<WDP1); // Enable interrupt every second
sei(); // Enable interrupts
}
inline void WDT_off() { // Stop watchdog timer
cli(); // Disable interrupts
wdt_reset(); // Reset the WDT
MCUSR &= ~(1<<WDRF); // Clear Watchdog reset flag
WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence
WDTCR = 0x00; // Disable WDT
sei(); // Enable interrupts
}
inline void ADC_ctrl(void) { // Battery monitoring
if (BATT_MON) {
ADMUX = (1 << REFS0) | (1 << ADLAR) | ADC_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2
DIDR0 |= (1 << ADC_DIDR); // disable digital input on ADC pin to reduce power consumption
ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL; // enable, start, prescale
} else {
ADCSRA &= ~(1<<7); // ADC off
}
}
void set_output(uint8_t pwm_lvl, uint8_t pwm_mode) {
if (pwm_mode) {
if (pwm_lvl > FAST_PWM_START && pwm_lvl != 255) {
TCCR0A = PWM_FAST; // Fast-PWM
} else {
TCCR0A = PWM_PHASE; // Phase corrected PWM
}
}
PWM_LVL = pwm_lvl;
}
static inline void mode_strobe(void) {
while(1) {
set_output(STROBE_ON_OUT, 0);
delay_5ms(strobe_delay);
set_output(STROBE_OFF_OUT, 0);
delay_5ms(strobe_delay);
}
}
static inline void mode_program(void) {
uint8_t i;
uint8_t j = spress_cnt - PROGRAM_SPRESS;
for (i = 0; i < PROGRAM_BLINKS; i++) {
set_output(PROGRAM_OUT, 0);
delay_5ms(PROGRAM_DELAY);
set_output(0, 0);
delay_5ms(PROGRAM_DELAY);
}
for (i = 0; i < 255;) {
i++;
set_output(0, 0);
delay_5ms(PROGRAM_PAUSE);
set_output(PROGRAM_OUT, 0);
eeprom_write_byte((uint8_t *)(uint16_t)(j), i);
delay_5ms(PROGRAM_PAUSE);
}
}
ISR(WDT_vect) { // WatchDogTimer interrupt
static uint8_t lowbatt_cnt = 0;
static uint8_t ticks = 0;
if (ticks < 255) ticks++;
if (ticks == MODE_TIMEOUT) { // Lock mode
if (mode_memory) { // Store current mode
eeprom_write_byte((uint8_t *)(uint16_t)(eepos), mode_idx);
} else { // No mode memory
eeprom_write_byte((uint8_t *)(uint16_t)(eepos), 0);
}
}
if (mypwm == 255 && ticks == ftimer) { // MODE100 timeout
mypwm = MODE100_LOW;
set_output(mypwm, 1);
}
if (BATT_MON && ticks == 255) {
ticks = 255 - BATT_TIMEOUT; // Battery monitoring interval
ADCSRA |= (1 << ADSC); // Start conversion
while (ADCSRA & (1 << ADSC)); // Wait for completion
if (ADCH < ADC_CRIT && (++lowbatt_cnt > 10)) { // See if voltage is lower than what we were looking for
WDT_off(); // Disable WDT so it doesn't wake us up
ADCSRA &= ~(1<<7); // ADC off
DDRB = (0 << PWM_PIN); // Set PWM pin to input
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Power down as many components as possible
sleep_mode();
}
}
}
int main(void) {
DDRB = (1 << PWM_PIN); // Set PWM pin to output
ACSR |= (1<<7); // AC (Analog Comparator) off
TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
set_sleep_mode(SLEEP_MODE_IDLE); // Will allow us to go idle between WDT interrupts
ADC_ctrl(); // Battery monitoring
WDT_on(); // Start watchdogtimer
set_output(0, 1); // Set phase pwm as default
get_mode(); // Get mode identifier and store with short press indicator
if (mypwm == MODE_PROGRAM) {
mode_program();
} else if (mypwm == MODE_STROBE) {
mode_strobe();
} else {
set_output(mypwm, 1);
}
while(1) {
sleep_mode(); // Enter sleep mode
}
return 0;
}