altos: Add attiny architecture files
[fw/altos] / src / attiny / ao_clock.c
1 /*
2  * Copyright © 2012 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #include <ao.h>
19
20 volatile AO_TICK_TYPE   ao_tick_count;
21 static volatile AO_TICK_TYPE    ao_wakeup_count;
22
23 ISR(TIMER1_COMPA_vect)
24 {
25         PORTB ^= 2;
26         ++ao_tick_count;
27         if ((int16_t) (ao_tick_count - ao_wakeup_count) >= 0)
28                 ao_wakeup((void *) &ao_tick_count);
29 }
30
31 uint16_t
32 ao_time(void)
33 {
34         uint16_t        r;
35
36         cli();
37         r = ao_tick_count;
38         sei();
39         return r;
40 }
41
42 void
43 ao_timer_init(void)
44 {
45         cli();
46         CLKPR = (1 << CLKPCE);
47         CLKPR = 0;
48         sei();
49
50         /* Overall division ratio is 512 * 125,
51          * so our 8MHz base clock ends up as a 125Hz
52          * clock
53          */
54         TCCR1 = ((1 << CTC1) |          /* Clear timer on match */
55                  (0 << PWM1A) |         /* Not PWM mode */
56                  (0 << COM1A0) |        /* Don't change output pins */
57                  (0 << COM1A1) |        /*  ... */
58                  (1 << CS13) |          /* Prescale by 512 */
59                  (0 << CS12) |          /*  ... */
60                  (1 << CS11) |          /*  ... */
61                  (0 << CS10));          /*  ... */
62         GTCCR = ((0 << PWM1B) |         /* Not PWM mode */
63                  (0 << COM1B1) |        /* Don't change output pins */
64                  (0 << COM1B0) |        /*  ... */
65                  (0 << FOC1B) |         /* Don't force output compare */
66                  (0 << FOC1A) |         /*  ... */
67                  (0 << PSR1));          /* Don't bother to reset scaler */
68
69         OCR1A = 0;
70         OCR1B = 0;
71         OCR1C = 124;                    /* Divide by as many 5s as we can (5^3 = 125) */
72
73         TIMSK = ((1 << OCIE1A) |        /* Enable TIMER1_COMPA interrupt */
74                  (0 << OCIE1B) |        /* Disable TIMER1_COMPB interrupt */
75                  (0 << TOIE1));         /* Disable TIMER1_OVF interrupt */
76         DDRB |= 2;
77 }
78
79 #define PER_LOOP        8
80 #define US_LOOPS        ((AVR_CLOCK / 1000000) / PER_LOOP)
81
82 void ao_delay_us(uint16_t us)
83 {
84 #if US_LOOPS > 1
85         us *= US_LOOPS;
86 #endif
87         for (;;) {
88                 ao_arch_nop();
89                 ao_arch_nop();
90                 ao_arch_nop();
91                 --us;
92                 /* A bit funky to keep the optimizer
93                  * from short-circuiting the test */
94                 if (!((uint8_t) (us | (us >> 8))))
95                         break;
96         }
97 }
98
99 void
100 ao_delay_until(uint16_t target)
101 {
102         cli();
103         ao_wakeup_count = target;
104         while ((int16_t) (target - ao_tick_count) > 0)
105                 ao_sleep((void *) &ao_tick_count);
106         sei();
107 }
108
109 void
110 ao_delay(uint16_t ticks)
111 {
112         ao_delay_until(ao_time() + ticks);
113 }
114