-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtinycylon.c
More file actions
341 lines (249 loc) · 8.07 KB
/
tinycylon.c
File metadata and controls
341 lines (249 loc) · 8.07 KB
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
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
// tinyCylon2.c
// revised firmware for tinyCylon LED scanner
// written by dale wheat - 18 november 2008
// based on behavior of original tinyCylon firmware
// notes:
// device = ATtiny13A
// clock = 128 KHz internal RC oscillator
// max ISP frequency ~20 KHz
// brown-out detect = 1.8 V
#include <avr/io.h>
#include <avr/interrupt.h>
// These registers are available on the ATtiny13A but not the original ATtiny13
// Brown out detector control register
#define BODCR _SFR_IO8(0x30)
#define BODSE 0
#define BODS 1
// Power reduction register
#ifndef PRR
#define PRR _SFR_IO8(0x3C)
#endif
#define PRADC 0
#define PRTIM0 1
typedef enum {
MODE_0 = 0, // cylon scanner
MODE_0a, // single direction cylon scan
MODE_0b, // other direction cylon scan
MODE_1, // glowing pig eyes (2)
MODE_1a, // single glowing pig eye
MODE_1b, // single (random) glowing pig eye
MODE_2, // random blinking - multi
MODE_2a, // random blinking - single
MODE_2b, // random blinking - intermittant
MODE_2c, // random bblinking - really sparse
MODE_MAX // off
} MODE;
volatile MODE mode __attribute__ ((section (".noinit")));
///////////////////////////////////////////////////////////////////////////////
// init() - initialize everything
// note: this "function" is in section .init3 so it is executed before main()
///////////////////////////////////////////////////////////////////////////////
void init(void) __attribute__ ((naked, section(".init3")));
void init(void) {
// turn off unused peripherals to save power
ACSR = 1<<ACD; // disable analog comparator
DIDR0 = 1<<ADC3D | 1<<ADC2D | 1<<ADC1D | 1<<ADC0D | 1<<AIN1D | 1<<AIN0D; // disable all digital inputs
// determine cause of device reset; act accordingly
if(bit_is_set(MCUSR, PORF)) {
mode = MODE_0; // power on!
} else if(bit_is_set(MCUSR, EXTRF)) {
mode++; // advance mode
if(mode > MODE_MAX) {
mode = MODE_0; // reset mode
}
}
MCUSR = 0; // reset bits
// initialize ATtiny13 input & output port
// PORTB
// PB0 5 MOSI/AIN0/OC0A/PCINT0 D1 output, active low
// PB1 6 MISO/AIN1/OC0B/INT0/PCINT1 D2 output, active low
// PB2 7 SCK/ADC1/T0/PCINT2 D3 output, active low
// PB3 2 PCINT3/CLKI/ADC3 D4 output, active low
// PB4 3 PCINT4/ADC2 D5 output, active low
// PB5 1 PCINT5/-RESET/ADC0/dW MODE advance pushbutton
PORTB = 0<<PORTB5 | 1<<PORTB4 | 1<<PORTB3 | 1<<PORTB2 | 1<<PORTB1 | 1<<PORTB0;
DDRB = 0<<DDB5 | 1<<DDB4 | 1<<DDB3 | 1<<DDB2 | 1<<DDB1 | 1<<DDB0;
// initialize ATtiny13 timer/counter
TCCR0B = 0<<FOC0A | 0<<FOC0B | 0<<WGM02 | 0<<CS02 | 0<<CS01 | 1<<CS00;
TIMSK0 = 0<<OCIE0B | 0<<OCIE0A | 1<<TOIE0; // interrupts
sei(); // enable global interrupts
}
///////////////////////////////////////////////////////////////////////////////
// timing & delay functions
///////////////////////////////////////////////////////////////////////////////
volatile unsigned int downcounter;
void delay(unsigned char n) {
downcounter = n<<3;
while(downcounter) {
MCUCR = 1<<PUD | 1<<SE | 0<<SM1 | 0<<SM0 | 0<<ISC01 | 0<<ISC00; // idle mode
asm("sleep"); // go to sleep to save power
}
}
///////////////////////////////////////////////////////////////////////////////
// pseudorandom number generator
///////////////////////////////////////////////////////////////////////////////
unsigned int prand(void) {
static unsigned int prand_value = 0xDA1E; // randomly seeded ;)
prand_value = (prand_value >> 1) ^ (-(prand_value & 1) & 0xd001);
return prand_value;
}
///////////////////////////////////////////////////////////////////////////////
// cylon() - simulate cylon scanner
///////////////////////////////////////////////////////////////////////////////
#define CYLON_SCAN_DELAY 30
void cylon(unsigned char cylon_style) {
const unsigned char cylon_bits[] = {
0b00010110, // 1 & 5
0b00011110, // l
0b00011100, // 1 & 2
0b00011101, // 2
0b00011001, // 2 & 3
0b00011011, // 3
0b00001011, // 3 & 4
0b00001111, // 4
0b00000111, // 4 & 5
0b00010111 // 5
};
unsigned char i; // array iterator
while(1) {
if(cylon_style == 0) {
// traditional (back & forth) cylon scanner
for(i = 1; i < sizeof(cylon_bits); i++) {
PORTB = cylon_bits[i];
delay(CYLON_SCAN_DELAY);
}
for(i = sizeof(cylon_bits) - 2; i > 1; i--) {
PORTB = cylon_bits[i];
delay(CYLON_SCAN_DELAY);
}
} else if(cylon_style == 1) {
// single direction scan
for(i = 0; i < sizeof(cylon_bits); i++) {
PORTB = cylon_bits[i];
delay(CYLON_SCAN_DELAY);
}
} else if(cylon_style == 2) {
// other direction scan
for(i = sizeof(cylon_bits); i > 0; i--) {
PORTB = cylon_bits[i - 1];
delay(CYLON_SCAN_DELAY);
}
}
}
}
///////////////////////////////////////////////////////////////////////////////
// pig_eyes() - glowing pig eyes are scary
///////////////////////////////////////////////////////////////////////////////
void pig_eyes(unsigned char n) {
unsigned char leds_on, leds_off = 0b00011111;
unsigned char pwm_counter, pwm_value;
while(1) {
if(n == 1) {
leds_on = 0b00011011; // one eye
} else if(n == 2) {
leds_on = 0b00010110; // 2 eyes
} else {
leds_on = 0b00011111 ^ (1 << (prand() % 5)); // single (random) eye
}
// ramp up...
for(pwm_value = 1; pwm_value < 128; pwm_value++) {
for(pwm_counter = 0; pwm_counter < 128; pwm_counter++) {
if(pwm_value > pwm_counter) {
PORTB = leds_on;
} else {
PORTB = leds_off;
}
}
}
// ... & ramp back down again
for(pwm_value = 127; pwm_value > 0; pwm_value--) {
for(pwm_counter = 0; pwm_counter < 128; pwm_counter++) {
if(pwm_value > pwm_counter) {
PORTB = leds_on;
} else {
PORTB = leds_off;
}
}
}
delay(250); // scary dark time
}
}
///////////////////////////////////////////////////////////////////////////////
// random() - random blinking
///////////////////////////////////////////////////////////////////////////////
void random(unsigned char n) {
while(1) {
if(n == 0) {
// light up a random number of LEDs
PORTB = prand() & 0x1F;
} else if(n == 1) {
// light up a single LED every time
PORTB = 0b00011111 ^ (1 << (prand() % 5));
} else {
if((prand() & n) == n) {
// light up an LED (maybe)
PORTB = 0b00011111 ^ (1 << (prand() % 5));
} else {
// no LEDs on (maybe not)
PORTB = 0b00011111;
}
}
delay(50);
}
}
///////////////////////////////////////////////////////////////////////////////
// main() - main program function
///////////////////////////////////////////////////////////////////////////////
void main(void) {
switch(mode) {
case MODE_0: // traditional (back & forth) cylon scanner
cylon(0);
break;
case MODE_0a: // single direction cylon scanner
cylon(1);
break;
case MODE_0b: // other direction cylon scanner
cylon(2);
break;
case MODE_1: // 2 glowing pig eyes
pig_eyes(2);
break;
case MODE_1a: // single glowing pig eye
pig_eyes(1);
break;
case MODE_1b: // single (random) glowing pig eye
pig_eyes(0);
break;
case MODE_2: // random blinking - multi
random(0);
break;
case MODE_2a: // random blinking - single
random(1);
break;
case MODE_2b: // random blinking - intermittant
random(3);
break;
case MODE_2c: // random blinking - really sparse
random(15);
break;
case MODE_MAX: // off
PORTB = 0b00011111; // all LEDs off
while(1) {
// deepest sleep mode
cli(); // disable interrupts
PRR = 1<<PRTIM0 | 1<<PRADC; // power down timer/counter0 & ADC
BODCR = 1<<BODS | 1<<BODSE; // enable BOD disable during sleep, step 1
BODCR = 1<<BODS | 0<<BODSE; // step 2
MCUCR = 1<<PUD | 1<<SE | 1<<SM1 | 0<<SM0 | 0<<ISC01 | 0<<ISC00; // select "power down" mode
asm("sleep"); // go to sleep to save power
}
}
}
///////////////////////////////////////////////////////////////////////////////
// timer/counter0 overflow interrupt handler
///////////////////////////////////////////////////////////////////////////////
ISR(TIM0_OVF_vect) {
downcounter--; // decrement downcounter for delay functions
}
///////////////////////////////////////////////////////////////////////////////
// [end-of-file]