d722d568d75da0e23e759b26231d3c185822bde9
[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; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 #include <ao.h>
20
21 volatile AO_TICK_TYPE   ao_tick_count;
22 static volatile AO_TICK_TYPE    ao_wakeup_count;
23
24 ISR(TIMER1_COMPA_vect)
25 {
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 #if AVR_CLOCK == 8000000UL
43 #define AO_CLKPS        0       /* divide by 1 */
44 #define AO_CS           10      /* prescale by 512 */
45 #endif
46 #if AVR_CLOCK == 4000000UL
47 #define AO_CLKPS        1       /* divide by 2 */
48 #define AO_CS           9       /* prescale by 256 */
49 #endif
50 #if AVR_CLOCK == 2000000UL
51 #define AO_CLKPS        2       /* divide by 4 */
52 #define AO_CS           8       /* prescale by 128 */
53 #endif
54 #if AVR_CLOCK == 1000000UL
55 #define AO_CLKPS        3       /* divide by 8 */
56 #define AO_CS           7       /* prescale by 64 */
57 #endif
58 #if AVR_CLOCK == 500000UL
59 #define AO_CLKPS        4       /* divide by 16 */
60 #define AO_CS           6       /* prescale by 32 */
61 #endif
62 #if AVR_CLOCK == 250000UL
63 #define AO_CLKPS        5       /* divide by 32 */
64 #define AO_CS           5       /* prescale by 16 */
65 #endif
66 #if AVR_CLOCK == 125000UL
67 #define AO_CLKPS        6       /* divide by 64 */
68 #define AO_CS           4       /* prescale by 32 */
69 #endif
70 #if AVR_CLOCK == 62500UL
71 #define AO_CLKPS        7       /* divide by 128 */
72 #define AO_CS           4       /* prescale by 32 */
73 #endif
74
75 void
76 ao_timer_init(void)
77 {
78         cli();
79         CLKPR = (1 << CLKPCE);
80         CLKPR = (AO_CLKPS << CLKPS0);
81         sei();
82
83         /* Overall division ratio is 512 * 125,
84          * so our 8MHz base clock ends up as a 125Hz
85          * clock
86          */
87         TCCR1 = ((1 << CTC1) |          /* Clear timer on match */
88                  (0 << PWM1A) |         /* Not PWM mode */
89                  (0 << COM1A0) |        /* Don't change output pins */
90                  (0 << COM1A1) |        /*  ... */
91                  (AO_CS << CS10));      /* Prescale */
92         GTCCR = ((0 << PWM1B) |         /* Not PWM mode */
93                  (0 << COM1B1) |        /* Don't change output pins */
94                  (0 << COM1B0) |        /*  ... */
95                  (0 << FOC1B) |         /* Don't force output compare */
96                  (0 << FOC1A) |         /*  ... */
97                  (0 << PSR1));          /* Don't bother to reset scaler */
98
99         OCR1A = 0;
100         OCR1B = 0;
101         OCR1C = 124;                    /* Divide by as many 5s as we can (5^3 = 125) */
102
103         TIMSK = ((1 << OCIE1A) |        /* Enable TIMER1_COMPA interrupt */
104                  (0 << OCIE1B) |        /* Disable TIMER1_COMPB interrupt */
105                  (0 << TOIE1));         /* Disable TIMER1_OVF interrupt */
106         DDRB |= 2;
107 }
108
109 #define PER_LOOP        8
110 #define US_LOOPS        ((AVR_CLOCK / 1000000) / PER_LOOP)
111
112 void ao_delay_us(uint16_t us)
113 {
114 #if US_LOOPS > 1
115         us *= US_LOOPS;
116 #endif
117         for (;;) {
118                 ao_arch_nop();
119                 ao_arch_nop();
120                 ao_arch_nop();
121                 --us;
122                 /* A bit funky to keep the optimizer
123                  * from short-circuiting the test */
124                 if (!((uint8_t) (us | (us >> 8))))
125                         break;
126         }
127 }
128
129 void
130 ao_delay_until(uint16_t target)
131 {
132         cli();
133         ao_wakeup_count = target;
134         while ((int16_t) (target - ao_tick_count) > 0)
135                 ao_sleep((void *) &ao_tick_count);
136         sei();
137 }
138
139 void
140 ao_delay(uint16_t ticks)
141 {
142         ao_delay_until(ao_time() + ticks);
143 }
144