/
ajastin.c
216 lines (207 loc) · 6.23 KB
/
ajastin.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
/**
* Ajastin
* Copyright (C) Tomi Leppänen, 2016
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* This is a small timer program for AtTiny85
*
* This code is made for AtTiny85, but should work with AtTiny[24]5 too.
* Use 1 MHz clock with this code. It is quite safe to disable brown-out
* detection as this code doesn't write any memory. The code runs
* ISR(TIMER0_COMPA_vect) (FREQUENCY * NUMBER_OF_LEDS) times a second.
* Time is adjusted by TIME in seconds.
*
* The code uses charlieplexing to drive 5 leds. Therefore every led
* will be lit FREQUENCY times per second and their duty cycle is
* 1/NUMBER_OF_LEDS. The piezo is DC so it will generate whatever
* frequency it happens to have and it beeps a half second beep once
* every second.
*
* Usage:
* Power on. Set time by pressing the button until the time you want is
* shown. Wait for about BUTTON_WAIT_TIME seconds. Now the LED_ON led
* lights and the timer starts counting. When it finished the piezo
* speaker starts buzzing. You can stop it and power down the counter by
* pressing the button. To restart the timer press reset button. The
* button doesn't do anything while counting and the reset button resets
* the timer back to setting the timer.
*/
/* vim: set tw=72 */
#include <avr/cpufunc.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/sleep.h>
#include <stdint.h>
#include <util/delay.h>
#define TIME 60
#define FREQUENCY 50
#define MAXIMUM 0x0F
#define NUMBER_OF_LEDS 5
#define LED_ON (1 << 4)
#define PORTB_NULL (1 << PB2)
#define BUTTON (1 << PB2)
#define BUTTON_WAIT_TIME 5
#define PIEZO (1 << PB4)
const unsigned char LEDS_HIGH[] = { // PORTB values
0b01100,
0b00101,
0b00110,
0b00101,
0b01100
};
const unsigned char LEDS_INPUT[] = { // DDRB values
0b11001,
0b10011,
0b10011,
0b11001,
0b11010
};
volatile uint8_t counter;
volatile uint8_t counter_max;
volatile uint8_t leds;
volatile uint8_t piezo;
volatile uint8_t stopped;
// stopped is the state of this program:
// 0 = timer running,
// 1 = counter_max not determined (before timer),
// 2 = counter_max refreshed (before timer),
// 3 = counter_max determined (before timer),
// 4 = piezo active (after timer),
// 5 = timer finished (after timer)
ISR(TIMER0_COMPA_vect)
{
static uint8_t multiplier = 0;
static uint8_t time = 0;
uint8_t state = (multiplier % NUMBER_OF_LEDS);
PORTB = PORTB_NULL | piezo; // Drive the leds
if (leds & (1 << state)) {
DDRB = LEDS_INPUT[state];
PORTB = LEDS_HIGH[state] | piezo;
}
if (multiplier >= FREQUENCY*NUMBER_OF_LEDS) {
switch (stopped) {
case 0: // While counter is active
if (++time >= TIME) {
counter++;
time = 0;
}
break;
case 1: // While determining counter_max
if (++time >= BUTTON_WAIT_TIME) {
stopped = 3;
time = 0;
}
break;
case 2: // Above and if counter_max was refreshed
time = 0;
stopped = 1;
break;
case 4: // Begin piezo beep
piezo = PIEZO;
break;
default:
break;
}
multiplier = 0;
} else {
if (stopped == 4 && multiplier >= FREQUENCY*NUMBER_OF_LEDS/2) {
piezo = 0; // End piezo beep
}
multiplier++;
}
OCR0A ^= 2; // Switch between values 61 and 62
}
ISR(INT0_vect)
{
if (stopped < 3) {
if (++counter_max > MAXIMUM)
counter_max = 1;
stopped = 2;
} else if (stopped == 4) {
cli();
stopped = 5;
}
}
int main(void)
{
{ // Set up timers, sleep and such
cli();
set_sleep_mode(SLEEP_MODE_IDLE);
PRR = ((1 << PRTIM1) | (1 << PRUSI) | (1 << PRADC));
// TIMER0
TCCR0A = (1 << WGM01);
TCCR0B = ((1 << CS01) | (1 << CS00));
OCR0A = 61;
OCR0B = 0;
TIMSK = (1 << OCIE0A);
// INT0
MCUCR |= (1 << ISC01);
GIMSK = (1 << INT0);
PORTB = PORTB_NULL;
counter = leds = 0;
counter_max = MAXIMUM;
piezo = 0;
stopped = 1;
sei();
}
{ // Set up the counter and start
while (stopped < 3)
{
leds = counter_max;
sleep_mode();
}
leds = LED_ON;
}
{ // Prepare for the timer
cli();
GIMSK = 0; // Stop INT0
stopped = 0;
TCNT0 = 0; // Clear TIMER0
sei();
}
{ // Display the timer while it's running
while (counter < counter_max)
{
leds = counter | LED_ON;
sleep_mode();
}
stopped = 4;
leds = counter;
}
{ // Yell!
cli();
GIMSK = (1 << INT0); // Start INT0
sei();
while (stopped == 4) // Sleep!
sleep_mode();
GIMSK = 0; // Stop INT0
TCCR0A &= ~((1 << COM0B1) | (1 << COM0B0)); // Stop yelling!
}
{ // Timer finished, sleep forever!
cli();
TIMSK = 0;
PORTB = 0; // Everything off
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_mode();
}
return 0;
}