initial commit
[LightSticks] / FIRMWARE / lightstick.c
1
2 #include <avr/io.h>
3 #include <avr/interrupt.h>
4 #include <stdlib.h>
5
6 // ------------- main settings ------------------
7
8 // #define      UART            1       // for debugging, if defined, beep() deactivated
9
10 #define LED_BEEPER      PB1
11 #define MAIN_LED        PB0
12 #define BUTTON_LEFT     PB2
13 #define BUTTON_RIGHT    PB4
14
15 #define BRIGHT_MAX      100
16 #define BRIGHT_DEF      1       // can't be > BRIGHT_MAX
17
18 #define BEFORE_SLEEP    300     // sec before sleep
19
20 // ---------------------------------------------
21
22 #define ADC_SAMPLE_LEN  20      // must be > ADC_SAMPLE_DROP * 3
23 #define ADC_SAMPLE_DROP 5
24
25 #define BAT_15MIN       302     // ADC value, ~15 min left
26 #define BAT_10MIN       301     // ..
27 #define BAT_5MIN        300     // ..
28
29 #define ON              0       // switchState
30 #define SLEEP           1
31 #define OFF             2
32
33 // ---------------------------------------------
34
35 #define false           0
36 #define true            1
37
38 // Some macros that make the code more readable
39
40 #define output_low(port,pin) port &= ~(1<<pin)
41 #define output_high(port,pin) port |= (1<<pin)
42 #define set_input(portdir,pin) portdir &= ~(1<<pin)
43 #define set_output(portdir,pin) portdir |= (1<<pin)
44
45 #define invert(port,pin) port ^= (1<<pin)
46
47 #define KEYBOARD_READ() ( 0b00010100 ^ (PINB & 0b00010100) )
48
49 #define LEFT    0b00010000      
50 #define RIGHT   0b00000100
51
52 // beeper timer stuff 
53
54 #define INT_DISABLE_BEEPER      ({ TIMSK &= ~(1<<TOIE1); })     // interrupt off
55 #define INT_ENABLE_BEEPER       ({ TIMSK |= (1<<TOIE1); })      // interrupt on
56
57 #define TIMER1_DIV      16
58 #define BEEP_LEN        240     // cycles (32kHz / TIMER1_DIV)
59
60 // -------------- simple UART TX ------------
61
62 #ifdef UART 
63
64         #define PRINT_BUFF_LEN  16
65
66 uint8_t print_buff[ PRINT_BUFF_LEN ];
67
68 void put_char( uint8_t c ){
69
70         c = ~c;
71         output_low( PORTB, LED_BEEPER );
72         for( uint8_t i = 10; i; i-- ){          // 10 bits
73
74 //              _delay_us( 1e6 / BAUD );        // bit duration / (16MHz, 9600, 104 us)
75
76                 asm volatile (
77                     "    ldi  r18, 3"   "\n"
78                     "    ldi  r19, 40"  "\n"
79                     "1:  dec  r19"      "\n"
80                     "    brne 1b"       "\n"
81                     "    dec  r18"      "\n"
82                     "    brne 1b"       "\n"
83                     "    nop"   "\n"
84                 );
85
86                 if( c & 1 ) output_low( PORTB, LED_BEEPER ); else output_high( PORTB, LED_BEEPER );     // data bit 0 / data bit 1 or stop bit
87                 c >>= 1;
88         }
89
90
91
92 void put_str(char *s) 
93 {
94     while (*s) put_char(*s++);
95 }
96
97 void put_num( uint16_t num, uint8_t ret ){
98         itoa( num, (char *)print_buff, 10 );
99         put_str((char *)print_buff);
100         if ( ret ) put_str( "\n" ); else put_str( " " );
101 }
102
103 #endif 
104
105 // -------------- EEPROM ---------------------
106
107 unsigned char EEPROM_read(unsigned char ucAddress)
108 {
109         while (EECR & (1<<EEPE));               //wait for completion of previous write
110         EEAR = ucAddress;                               //set up address register
111         EECR |= (1<<EERE);                              //start eeprom read by writing EERE
112         return EEDR;                                    //return data from data register
113 }
114
115 void EEPROM_write(unsigned char ucAddress, unsigned char ucData)
116 {
117         while (EECR & (1<<EEPE));               //wait for completion of previous write
118         EECR = (0<<EEPM1)|(0<<EEPM0);   //set programming mode
119         EEAR = ucAddress;                               //set up address register
120         EEDR = ucData;                                  //set up data register
121         EECR |= (1<<EEMPE);                             //write lgical one to EMPE
122         EECR |= (1<<EEPE);                              //start eeprom write by setting EEPE
123 }
124
125 // uint8_t EEMEM        memoBrightness = 0xff;
126
127
128 // -------------- beeper ---------------------
129
130 static volatile uint32_t beeper_mask;
131 static volatile uint8_t beeper_div;
132
133 ISR ( TIMER1_OVF_vect ) {
134
135         static uint16_t beeper_cnt;
136
137 //      TCNT1 = 255; // 208kHz
138 //      TCNT1 = 128; // 53kHz
139 //      TCNT1 = 64; // 37kHz    // 64
140         TCNT1 = 32; // 32kHz    // 64
141 //      TCNT1 = 1; // 29kHz     // 64
142
143         if ( beeper_div++ % TIMER1_DIV == 0 ){
144
145                 if( beeper_cnt++ < BEEP_LEN ){ 
146                         if(beeper_mask & 1) invert(PORTB, LED_BEEPER);
147                 } else {
148                         beeper_cnt = 0;
149                         beeper_mask >>= 1;
150
151                         if(beeper_mask == 0){
152                                 INT_DISABLE_BEEPER;
153                                 PRR |= (1<<PRTIM1);  // power off Timer 1
154                         }
155                 }
156         }
157 }
158
159 void beep(uint32_t mask){
160
161 #ifdef UART 
162         put_str("BEEP\n");
163 #else 
164         beeper_mask = mask;
165
166         if(beeper_mask){
167                 PRR   &= ~(1<<PRTIM1);  // power on Timer 1
168 //              TCCR1 = 0;              // Normal counter operation
169                 TCCR1 = 0b0001;         // PRESCALER(TIMER1_PRESCALER) // has no effect IMHO (PLL mode?)
170                 TCNT1  = 0xFF;          // Start terminal count at 0xFF
171                 INT_ENABLE_BEEPER;      // Enable Overflow interrupts for Timer 0
172         }
173 #endif 
174 }
175
176 // -------------- delay ---------------------
177
178 void my_delay(){ // 50 ms
179
180         asm volatile (
181             "    ldi  r18, 5"   "\n"
182             "    ldi  r19, 15"  "\n"
183             "    ldi  r20, 242" "\n"
184             "1:  dec  r20"      "\n"
185             "    brne 1b"       "\n"
186             "    dec  r19"      "\n"
187             "    brne 1b"       "\n"
188             "    dec  r18"      "\n"
189             "    brne 1b"       "\n"
190         );
191 }
192
193
194 // --------------- ADC ----------------------
195
196 void adc_init() {
197
198         DIDR0 |= (1<<ADC3D);                                    // disable digital inputs to reduce power consumption in the digital input buffer
199         PRR &= ~(1<<PRADC);                                     // power on ADC
200 //      ADMUX = (1<<REFS1) | (1<<REFS0);                        // AREF = 2.56v
201         ADMUX = (1<<REFS1);                                     // AREF = 1.1v
202
203         ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);      // ADC Enable and prescaler of 128, // 16000000/128 = 125000
204 }
205
206 uint16_t adc_read() {
207
208         PRR &= ~(1<<PRADC);                                     // power on ADC
209         ADMUX |= (1<<REFS1) | (1 << MUX1) | (1 << MUX0);        // ADC3 activated / read ADCH
210         ADCSRA |= (1<<ADSC);                                    // start single convertion
211         while(ADCSRA & (1<<ADSC));                              // wait for conversion to complete
212         PRR |= (1<<PRADC);                                      // power off ADC
213         return (ADC);
214 }
215
216 // ------------ median value ----------------
217
218 uint16_t median( uint16_t * list, uint8_t n, uint8_t drop ){
219
220         uint8_t c, d;
221         uint16_t t;
222
223         for (c = 0 ; c < n - 1; c++) {
224                 for (d = 0 ; d < n - c - 1; d++) {
225                         if (list[d] > list[d+1]) {
226                                 t = list[d];
227                                 list[d] = list[d+1];
228                                 list[d+1] = t;
229                         }
230                 }
231         }
232
233         t =0;
234
235         for (c = drop ; c < n-drop ; c++) t += list[ c ];
236
237         return t/(n - drop*2);
238 }
239
240 // ------------------------------------------
241 // -------------- main ----------------------
242 // ------------------------------------------
243
244 int main(void) {
245
246         uint8_t         keyBlock = 0, keyMask = 0, prevKeyMask = 0, keyCnt = 0;
247         uint8_t         brightness = 0, targBrightness = 0, targBrightnessOnSleep = 0;
248         uint8_t         battStage = 0, switchState = ON;
249         uint16_t        adc[ ADC_SAMPLE_LEN ];
250         uint16_t        ADCVal = 0xffff, secCnt = 0, seconds = 0, sleepCnt = 0;
251
252 //      if (MCUSR != 0) tMCUSRStored = MCUSR; else tMCUSRStored = GPIOR0; // MCU Reset Status Register
253
254         // IO init
255
256         cli();
257
258         set_output(DDRB, LED_BEEPER);
259         set_input(DDRB, BUTTON_LEFT);
260         set_input(DDRB, BUTTON_RIGHT);
261
262         output_high(PORTB, BUTTON_LEFT);        // pull-up
263         output_high(PORTB, BUTTON_RIGHT);       // pull-up
264
265         set_output(DDRB, MAIN_LED);  
266
267         INT_DISABLE_BEEPER;
268
269         sei();
270
271 /*
272         MCU Reset Status Register
273
274         if ( tMCUSRStored == 0 ) beep( 0b10101 ); else { 
275                 for (uint8_t a = 0; a < 6; a++ ) {
276                         beep( 0b10 );
277                         keyBlock = 10;
278                         while (keyBlock-- > 0) my_delay(); 
279                 }
280         }
281 */
282
283         beep( 0b10101 );
284
285         // PWM init
286
287         TCCR0A  = 2<<COM0A0 | 1<<WGM00 | 1<<WGM01 ;
288         TCCR0B  = 0<<WGM02 | 1<<CS00;
289         TCCR1   = 0<<PWM1A | 0<<COM1A0 | 1<<CS10;       // page 89, TCCR1 – Timer/Counter1 Control Register
290 //      GTCCR   = 1<<PWM1B | 2<<COM1B0;
291
292         // set default brightness
293
294         targBrightness = EEPROM_read( 0x01 );
295         if ( targBrightness == 0xff || targBrightness > BRIGHT_MAX ) targBrightness = BRIGHT_DEF;
296
297         adc_init();
298
299 #ifdef UART 
300         put_str("hello...\n");
301 #endif
302
303         // main loop 
304
305         while (1) {
306
307                 // check battery 
308
309                 if ( secCnt++ == 20 ){
310
311                         secCnt = 0; 
312
313                         adc[ seconds % ADC_SAMPLE_LEN ] = adc_read();
314
315                         if (  seconds % ADC_SAMPLE_LEN == 0 && seconds > ADC_SAMPLE_LEN ){
316
317                                 ADCVal = median( adc, ADC_SAMPLE_LEN, ADC_SAMPLE_DROP );
318
319                                 if ( ADCVal <= BAT_15MIN && battStage == 0 ) battStage = 1; 
320                                 if ( ADCVal <= BAT_10MIN && battStage == 1 ) battStage = 2;
321                                 if ( ADCVal <= BAT_10MIN && battStage == 2 ) battStage = 3;
322                 
323                                 if ( battStage == 1 ) beep(0b101);
324                                 if ( battStage == 2 ) beep(0b10101);
325                                 if ( battStage == 3 ) {
326                                         beep(0b1010101);
327                                         targBrightness = 0;
328                                         switchState = OFF;
329                                 }
330 #ifdef UART 
331                                 put_num( ADCVal, true );
332 #endif
333                         }
334
335                         seconds ++;
336                         sleepCnt ++;
337                 }
338
339                 // sleep counter 
340
341                 if ( sleepCnt > BEFORE_SLEEP && switchState == ON ){
342                         targBrightnessOnSleep = brightness;
343                         targBrightness = 0;
344                         beep(0b1);
345                         switchState = SLEEP;
346                 }
347
348                 // keyboard interface 
349
350                 if ( keyBlock > 0 ) {
351                         keyBlock --; 
352                 } else { 
353
354                         keyMask = KEYBOARD_READ();
355
356                         if ( switchState == ON ){ 
357
358                                 if ( prevKeyMask != keyMask ) keyCnt = 0; else keyCnt ++;
359                                 prevKeyMask = keyMask;
360
361                                 if (keyCnt > 2) { 
362
363                                         if ( keyMask == LEFT ) { if ( targBrightness < BRIGHT_MAX ) targBrightness ++; else { beep(0b1); keyBlock = 10; } }
364                                         if ( keyMask == RIGHT && targBrightness > 0 ) targBrightness --;
365
366                                         if ( keyMask == ( LEFT | RIGHT ) ) {
367                                                 EEPROM_write( 0x01, brightness );
368                                                 beep(0b101);
369                                                 keyBlock = 10;
370                                         }
371                                 }
372                         }
373
374                         if ( keyMask != 0 ) {
375
376                                 if ( switchState == OFF ) {
377                                         beep(0b1010101); 
378                                         keyBlock = 10;
379                                 }
380
381                                 if ( switchState == SLEEP ){
382                                         beep(0b1);
383                                         sleepCnt = 0;
384                                         switchState = ON; 
385                                         targBrightness = targBrightnessOnSleep;
386                                         keyBlock = 10;
387                                 }
388                         }
389                 }
390
391                 if ( brightness < targBrightness ) brightness ++;
392                 if ( brightness > targBrightness) brightness --;
393
394                 OCR0A = brightness;
395
396                 my_delay();
397         }
398 }
399
Contact me: dev (at) shalnoff (dot) com
PGP fingerprint: A6B8 3B23 6013 F18A 0C71 198B 83D8 C64D 917A 5717