~toykeeper/flashlight-firmware/trunk

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
//
// LuxDrv 0.30b    DrJones 2011++
//
// 808 bytes with ramping
//
// License: CC-BY-NC-SA (non-commercial use only, derivative works must be under the same license)
//
// changes:
// 0.1  first version: levels,strobe,beacon,timer(136)   (638bytes)
// 0.2  ramping(200), improved timer(180), skip mode1 as next mode if already in mode 1     (898bytes)
// 0.21 changed EEPROM handling to reduce power-off-while-writing glitches -->eepsave(byte)  (874bytes)
// 0.22 bugfix in ramping; mode not stored on (very) short tap   (878bytes)
// 0.3  battery monitoring added; step down and indicator in beacon mode (994bytes)
// 0.3b improved config, const progmem, no dummy any more,  timer removed, 4 main modes as default

//ToDo: 
//  ? off-time checking; requires adding  diode,R,C
//  ? blink your name in morse code :)


#define F_CPU 4800000    //CPU: 4.8MHz  PWM: 9.4kHz       ####### use low fuse: 0x75  #######   
//########################################################################################### MODE/PWM SETUP 
//Special modes; comment out to disable
#define RAMPING   254  //+156
#define STROBE    253  //+50
#define BEACON    252  //+74


#define MODES      6,15,56,255,   RAMPING,  STROBE,  BEACON

#define RAMPMODE 5  //the number of the RAMPING mode in the MODES order; e.g. RAMPING is 5th mode -> 5


//define NOMEM         //deactivate mode memory.
#define LOCKTIME 7   //time in 1/8 s until a mode gets locked, e.g. 12/8=1.5s
#define BATTMON  125 //enable battery monitoring with this threshold
#define MINPWM   5   //needed for ramping
//###########################################################################################


#define outpin 1
 #define PWM OCR0B  //PWM-value
#define adcpin 2
 #define adcchn 1
#define portinit() do{ DDRB=(1<<outpin); PORTB=0xff-(1<<outpin)-(1<<adcpin);  }while(0)

#include <avr/pgmspace.h>
#define byte uint8_t
#define word uint16_t

PROGMEM byte modes[]={ MODES };

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/eeprom.h>



#define WDTIME 0b01000011  //125ms

#define sleepinit() do{ WDTCR=WDTIME; sei(); MCUCR=(MCUCR &~0b00111000)|0b00100000; }while(0) //WDT-int and Idle-Sleep

#define SLEEP asm volatile ("SLEEP")

#define pwminit() do{ TCCR0A=0b00100001; TCCR0B=0b00000001; }while(0)  //chan A, phasePWM, clk/1  ->2.35kHz@1.2MHz

#define adcinit() do{ ADMUX =0b01100000|adcchn; ADCSRA=0b11000100; }while(0) //ref1.1V, left-adjust, ADC1/PB2; enable, start, clk/16
#define adcread() do{ ADCSRA|=64; while (ADCSRA&64); }while(0)
#define adcresult ADCH


#define ADCoff ADCSRA&=~(1<<7) //ADC off (enable=0);
#define ADCon  ADCSRA|=(1<<7)  //ADC on
#define ACoff  ACSR|=(1<<7)    //AC off (disable=1)
#define ACon   ACSR&=~(1<<7)   //AC on  (disable=0)


//_____________________________________________________________________________________________________________________


volatile byte mypwm=0;
volatile byte ticks=0;
volatile byte mode=0;
byte pmode=50;

byte eep[32];  //EEPROM buffer
byte eepos=0;

#ifdef RAMPING
  byte ramped=0;
#endif

byte lowbattcounter=0;



void eepsave(byte data) {  //central method for writing (with wear leveling)
  byte oldpos=eepos;
  eepos=(eepos+1)&31;  //wear leveling, use next cell
  EEARL=eepos; EEDR=data; EECR=32+4; EECR=32+4+2;  //WRITE  //32:write only (no erase)  4:enable  2:go
  while(EECR & 2); //wait for completion
  EEARL=oldpos;           EECR=16+4; EECR=16+4+2;  //ERASE  //16:erase only (no write)  4:enable  2:go
}




ISR(WDT_vect) {  //WatchDogTimer interrupt
  if (ticks<255) ticks++;
  if (ticks==LOCKTIME)
  #ifdef NOMEM
    eepsave(0);  //current mode locked -> next time start over, no memory.
  #else 
    eepsave(mode);
  #endif


  #ifdef BATTMON //code to check voltage and ramp down
   adcread(); 
   if (adcresult<BATTMON) { if (++lowbattcounter>8) {mypwm=(mypwm>>1)+3;lowbattcounter=0;} } 
   else lowbattcounter=0;
  #endif
}




inline void getmode(void) {  //read current mode from EEPROM and write next mode

  eeprom_read_block(&eep, 0, 32);                     //read block 
  while((eep[eepos]==0xff) && (eepos<32)) eepos++;    //find mode byte
  if (eepos<32) mode=eep[eepos];
  else eepos=0;

  byte next=0;
  if (mode==0) next=1; //skip 1st mode if memory is 1st mode already

  #ifdef RAMPING  
   if (mode & 0x40) {   //RAMPING
     ramped=1;
     if (mode & 0x80)  { next=RAMPMODE; mode&=0x7f; } //mode: for savemode
   }else              //ENDRAMPING
  #endif

  if (mode & 0x80) {  //last on-time was short
    mode&=0x7f;   if (mode>=sizeof(modes)) mode=0;
    next=mode+1;  if (next>=sizeof(modes)) next=0; 
  } 

  eepsave(next|0x80); //write next mode, with short-on marker
}





int main(void) {

  portinit();
  sleepinit();
  ACoff;
  #ifdef BATTMON
    adcinit();
  #else
    ADCoff;
  #endif
  pwminit();


  getmode();  //get current mode number from EEPROM

  byte i=0;

  #ifdef RAMPING
   byte p,j=0,dn=1;  //for ramping
   if (ramped) {  //use the ramped mode
      pmode=255; i=30-MINPWM-(mode&0x3f); while(i--){pmode=pmode-(pmode>>3)-1;}  //get PWM value  //never gives 250..254
   } else
  #endif

  pmode=pgm_read_byte(&modes[mode]);  //get actual PWM value (or special mode code)


  switch(pmode){

    #ifdef RAMPING
    case RAMPING: 
      ticks=100; //crude way to deactivate the 2s-lock       //RAMPING
      while(EECR & 2); //wait for completion of getmode's write
      while(1){ p=255; i=30-MINPWM-j; while(i--){p=p-(p>>3)-1;}   //ramp up
                PWM=p;
                eepsave(192+j);    
                SLEEP;
                if (dn) {if (j          ) j--; else {dn=0;SLEEP;SLEEP;} } 
                else    {if (j<30-MINPWM) j++; else {dn=1;SLEEP;SLEEP;} }
      } break;
    #endif

    #ifdef STROBE
    case STROBE:  mypwm=255; while(1){ PWM=mypwm; _delay_ms(20); PWM=0; _delay_ms(60); } break; //strobe 12.5Hz  //+48
    #endif

    #ifdef BEACON
    case BEACON:  
      #ifdef BATTMON    
        adcread(); i=adcresult; while (i>BATTMON) {PWM=8; SLEEP;SLEEP; PWM=0; SLEEP; i-=5;}   SLEEP;SLEEP;SLEEP;
      #endif    
      mypwm=255; while(1){ PWM=mypwm; _delay_ms(20); PWM=0; i=70;do{SLEEP;}while(--i);  } break;  //beacon 10s //+48
    #endif


    default:   mypwm=pmode; while(1){PWM=mypwm;SLEEP;}  //all other: us as PWM value

  }//switch
  return 0;
}//main