~gabe/flashlight-firmware/anduril2

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
//v0 5/1/2014 by Everett
	//initial version
	//simple flashlight controller. mode change on power cycle
//v1 5/3/2014 by Everett
	//adapted to momentary switch
//v2 5/6/2014 by Everett
	//ported to Attiny10 device

#define F_CPU 1000000
#define pwm OCR0BL
#define pwm_invert
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/sleep.h>

unsigned char mode;
enum mode{
	max=0,
	med=1,
	low=2,
	off=3,
};
#define max_mode 3
#define default_mode 0

unsigned int v_timer;
char v_sample;
#define voltage_rate 100 //milliseconds

char pressed;
char new_press;
char switch_count;
unsigned char isr_prescale;

void configure(void);
unsigned char read_voltage(void);
void initialize_mode(void);
void debounce(void);
void shutdown(void);

ISR(TIM0_OVF_vect) //fires at 4kHz
{
	isr_prescale++;
	if(isr_prescale>=4){	//prescale since timer only has the 4kHz option
		isr_prescale=0;
		debounce();
		if(++v_timer==voltage_rate){v_timer=0; v_sample=1;}
	}
}

ISR(INT0_vect)
{
	//no action, but ISR must be included since it will execute upon waking
}


int main(void)
{
	configure();	//set up hardware peripherals
	
	mode=default_mode;
	initialize_mode();
	pressed=0; new_press=0; switch_count=10;
	sei();	//turn on interrupts

	while(1){
		
		if(v_sample){
			v_sample=0;
			if(mode==max){				//if battery goes below threshold in max mode, force down to medium mode
				if(read_voltage()<100){	//set threshold for external voltage here
					mode=med;
					initialize_mode();
				}
			}
		}
		
		if(new_press){
			new_press=0;
			mode++;
			if(mode>max_mode) mode=0;
			initialize_mode();
		}
		
		if(mode==off)
		{
			shutdown();	//shutdown will lock up here until a press wakes the device
		}
		
	}
}

void shutdown(void)
{
	TIMSK0=0;	//stop timer interrupt
	pwm=0;	//zero output
	TCCR0A=0; //turn off pwm
	#ifdef pwm_invert
	PORTB|=0b00000010;
	#else
	PORTB=0;		//ensure pin is low
	#endif
	ADCSRA=0;	//adc off
	PRR=3;	//power reduce on adc and timer0
	
	while(1)	//make this a loop so we stay here until sure the switch went down
	{
		do{
			debounce();
			_delay_ms((char)1);
		}while(pressed);	//ensure switch is up
		
		EICRA=0;	//interrupt on low level of int0
		EIMSK=1;	
		SMCR=0b00000101;	//enable sleep
		sleep_cpu();
		EIMSK=0;
		SMCR=0b00000100;	//disable sleep	
		
		pressed=0; switch_count=10;
		for(char i=0; i<40; i++){	//watch for up to 40ms for a solid press
			debounce();
			_delay_ms((char)1);
			if(pressed) break;	//if pressed break out of for loop
		}
		if(pressed) break;	//if pressed break out of sleep loop
	}
	
	configure();	//set up hardware for operation
	
}

void debounce(void)
{
	static char port_copy=0xff;
	#define switch_mask 0b00000001	//this selects PB0 as the switch
	
	if((PINB&switch_mask)==port_copy)	//if the current state matches previous state
	{
		if(--switch_count==0)	//count down samples. if 10 consecutive samples matched
		{
			switch_count=10;	//reset sample counter
			if(PINB&switch_mask) pressed=0;	//if the state is high, switch is up
			else		//else switch is down. check for new press
			{
				if(pressed==0) new_press=1;	//if last state of pressed was 0, this is a new press
				pressed=1;	//switch is now down
			}
		}
	}
	else	//state doesn't match,
	{
		switch_count=10;	//reset sample counter
		port_copy=(PINB&switch_mask);	//get new sample
	}
}

void initialize_mode(void)
{
	switch(mode){	//initialize current mode
		default:
		case max:
		pwm=255;
		break;
		case med:
		pwm=25;
		break;
		case low:
		pwm=1;
		break;
		case off:
		pwm=0;
		break;
	}
}

unsigned char read_voltage(void)
{
	ADCSRA|=(1<<ADSC);	
	while(ADCSRA&(1<<ADSC));
	return ADCL;
}


void configure(void)
{
	PRR=0;
	TIMSK0=0b00000001;	//interrupt on t0 overflow
	pwm=0;
	#ifdef pwm_invert
	TCCR0A=0b00110001;	//output B inverted
	#else
	TCCR0A=0b00100001;	//output B on, not inverted, 8bit pwm,
	#endif
	TCCR0B=0b00001001;	//no prescale for 3906Hz pwm and interrupt
	GTCCR=0;
	
	PORTB=0;
	DDRB=0b00000010;	//PB1 output
	DIDR0=0b00000100;	//PB2 analog
	PUEB=0b11111001;	//pull up switch and reset

	ADMUX=2;	//PB2
	ADCSRA=0b10000011;	// 

	SMCR=0b00000100;	//enable power down mode
}