-
Notifications
You must be signed in to change notification settings - Fork 6
/
timer.c
190 lines (153 loc) · 5.38 KB
/
timer.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
/* timer.c */
/* Copyright (c) 2018-2020 J. M. Spivey */
#include "microbian.h"
#include "hardware.h"
static int TIMER_TASK;
#ifdef UBIT_V1
#define TICK 5 // Interval between updates (ms)
#endif
#ifdef UBIT_V2
#define TICK 1 // Helps with faster display update
#endif
#define MAX_TIMERS 8
/* Millis will overflow in about 46 days, but that's long enough. */
/* millis -- milliseconds since boot */
static unsigned millis = 0;
/* timer -- array of data for pending timer messages */
static struct {
int client; /* Process that receives message, or -1 if empty */
unsigned period; /* Interval between messages, or 0 for one-shot */
unsigned next; /* Next time to send a message */
} timer[MAX_TIMERS];
/* check_timers is called by the timer task and sends messages
directly to clients. We assume that each client is waiting to
receive a PING message: otherwise the progress of the entire system
will be held up, possibly leading to deadlock. */
/* check_timers -- send any messages that are due */
static void check_timers(void)
{
for (int i = 0; i < MAX_TIMERS; i++) {
if (timer[i].client >= 0 && millis >= timer[i].next) {
send_int(timer[i].client, PING, timer[i].next);
if (timer[i].period > 0)
timer[i].next += timer[i].period;
else
timer[i].client = -1;
}
}
}
/* create -- create a new timer */
static void create(int client, int delay, int repeat) {
int i = 0;
while (i < MAX_TIMERS && timer[i].client >= 0)
i++;
if (i == MAX_TIMERS)
panic("Too many timers");
/* If we are between ticks when the timer is created, then the
timer will go off up to one tick early. We could add on a tick
to compensate for this, but most applications work better
without it. Effectively, the delay is counted from the
previous timer tick, and if it is created as a response to that
tick, then the effect is what is usually wanted. */
timer[i].client = client;
timer[i].next = millis + delay;
timer[i].period = repeat;
}
/* timer1_handler -- interrupt handler */
void timer1_handler(void) {
// Update the time here so it is accessible to timer_micros
if (TIMER1_COMPARE[0]) {
millis += TICK;
TIMER1_COMPARE[0] = 0;
interrupt(TIMER_TASK);
}
}
static void timer_task(int n) {
message m;
/* We use Timer 1 because its 16-bit mode is adequate for a clock
with up to 1us resolution and 1ms period, leaving the 32-bit
Timer 0 for other purposes. */
TIMER1_STOP = 1;
TIMER1_MODE = TIMER_MODE_Timer;
TIMER1_BITMODE = TIMER_BITMODE_16Bit;
TIMER1_PRESCALER = 4; // 1MHz = 16MHz / 2^4
TIMER1_CLEAR = 1;
TIMER1_CC[0] = 1000 * TICK;
TIMER1_SHORTS = BIT(TIMER_COMPARE0_CLEAR);
TIMER1_INTENSET = BIT(TIMER_INT_COMPARE0);
TIMER1_START = 1;
enable_irq(TIMER1_IRQ);
priority(P_HANDLER);
while (1) {
receive(ANY, &m);
switch (m.type) {
case INTERRUPT:
tick(TICK);
check_timers();
break;
case REGISTER:
create(m.sender, m.int1, m.int2);
break;
default:
badmesg(m.type);
}
}
}
/* timer_init -- start the timer task */
void timer_init(void) {
int i;
for (i = 0; i < MAX_TIMERS; i++)
timer[i].client = -1;
TIMER_TASK = start("Timer", timer_task, 0, 256);
}
/* timer_now -- return current time in milliseconds since startup */
unsigned timer_now(void) {
return millis;
}
/* The result of timer_micros will overflow after 71 minutes, but even
if it does overflow, shorter durations can be measured by taking the
difference of two readings with unsigned subtraction. */
/* timer_micros -- return microseconds since startup */
unsigned timer_micros(void) {
unsigned my_millis, ticks1, ticks2, extra;
/* We must allow for the possibility the timer has expired but the
interrupt has not yet been handled. Worse, the timer expiry
could happen between looking at the timer and looking at the
interrupt flag. The approach is to take two readings with
interrupts disabled, one before and one after checking the
flag. If the flag is set, but the value has not gone down
between the two readings, that indicates the expiry happened
before the first reading, so an extra tick should be added.*/
intr_disable();
TIMER1_CAPTURE[1] = 1; // Capture count before testing event
extra = TIMER1_COMPARE[0]; // Inspect the expiry event
TIMER1_CAPTURE[2] = 1; // Capture count afterwards
my_millis = millis;
intr_enable();
ticks1 = TIMER1_CC[1];
ticks2 = TIMER1_CC[2];
/* Correct my_millis if the timer expired */
if (extra && ticks1 <= ticks2) my_millis += TICK;
return 1000 * my_millis + ticks1;
}
/* timer_delay -- one-shot delay */
void timer_delay(int msec) {
message m;
m.type = REGISTER;
m.int1 = msec;
m.int2 = 0; /* Don't repeat */
send(TIMER_TASK, &m);
receive(PING, NULL);
}
/* timer_pulse -- regular pulse */
void timer_pulse(int msec) {
message m;
m.type = REGISTER;
m.int1 = msec;
m.int2 = msec; /* Repetitive */
send(TIMER_TASK, &m);
}
/* wait -- sleep until next timer pulse */
void timer_wait(void) {
receive(PING, NULL);
}