-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
timeanddate.cpp
198 lines (176 loc) · 5.63 KB
/
timeanddate.cpp
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
/**
* Bill Siever
*
* This code is released under the [MIT License](http://opensource.org/licenses/MIT).
* Please review the LICENSE file.
*/
// Enable debugging or not: (comment out / #ifdefs)
//#define DEBUG 1
#include "pxt.h"
#include "nrf.h"
#ifdef SOFTDEVICE_PRESENT
#include "nrf_nvic.h"
// #warning "****SOFTDEVICE PRESENT****"
#else
// #warning "******NO SOFTDEVICE PRESENT******"
#endif
#if MICROBIT_CODAL
#else
//From: https://github.com/ARMmbed/nrf51-sdk/blob/master/source/nordic_sdk/components/drivers_nrf/delay/nrf_delay.h
static void __INLINE nrf_delay_us(uint32_t volatile number_of_us) __attribute__((always_inline));
static void __INLINE nrf_delay_us(uint32_t volatile number_of_us)
{
register uint32_t delay __ASM ("r0") = number_of_us;
__ASM volatile (
".syntax unified\n"
"1:\n"
" SUBS %0, %0, #1\n"
" NOP\n"
" NOP\n"
" NOP\n"
" NOP\n"
" NOP\n"
" NOP\n"
" NOP\n"
" NOP\n"
" NOP\n"
" NOP\n"
" NOP\n"
" NOP\n"
" BNE 1b\n"
".syntax divided\n"
: "+r" (delay));
}
#endif
namespace timeanddate
{
/*
Return the current system CPU time in s
Must be called 2x+ every 2^32/1000000 s (~=<15min) or more frequently to
ensure proper time keeping
*/
//%
uint32_t cpuTimeInSeconds() {
static uint64_t totalUs = 0;
static uint32_t lastUs = 0;
uint32_t currentUs;
#ifdef DEBUG
uint32_t retries = 0;
static uint32_t lastLastUs = 0;
#endif
#if MICROBIT_CODAL
static NRF_TIMER_Type *timer = NULL;
#ifdef SOFTDEVICE_PRESENT
sd_nvic_DisableIRQ(TIMER1_IRQn);
#else
NVIC_DisableIRQ(TIMER1_IRQn);
#endif
// If we haven't gotten the timer yet, do startup tasks, including getting the timer.
if(timer == NULL) {
#ifdef SOFTDEVICE_PRESENT
if(ble_running() == true) {
sd_clock_hfclk_request();
} else {
// Ensure the HFCLOCK is running
NRF_CLOCK_Type *clock = NRF_CLOCK;
clock->TASKS_HFCLKSTART = 1;
}
#else
// Ensure the HFCLOCK is running
NRF_CLOCK_Type *clock = NRF_CLOCK;
clock->TASKS_HFCLKSTART = 1;
#endif
// Get the timer (ensures this is only done once)
timer = NRF_TIMER1;
// Disable timer
timer->TASKS_STOP = 1;
// Set bit mode
timer->BITMODE = 3;
// Restart it
timer->TASKS_START = 1;
}
// Capture the current timer value
timer->TASKS_CAPTURE[3] = 1;
currentUs = timer->CC[3];
#ifdef SOFTDEVICE_PRESENT
sd_nvic_EnableIRQ(TIMER1_IRQn);
#else
NVIC_EnableIRQ(TIMER1_IRQn);
#endif
// Update the time
totalUs += (currentUs - lastUs);
lastUs = currentUs;
#ifdef DEBUG
uBit.serial.send("\ncurrentUs=");
uBit.serial.send((int)currentUs);
#endif
#else
// Continue to get ticker values until they are valid
while(true) {
// Try a read
currentUs = us_ticker_read();
// If it was near the end of the phase, read again
// (avoid non-atomic access / race condition error)
while((currentUs & 0x0000FFFF) > 0x0000FFC0) {
// Ensure timer before last 64uS of cycle (uS ticker should be about 5uS? Check!)
nrf_delay_us(100);
currentUs = us_ticker_read();
}
uint32_t newUs = currentUs - lastUs;
// Sanity check: only proceed if it's NOT negative
if(!(newUs & 0x80000000)) {
// Only add if it's positive (time increased) / valid NOT if the high bit is set)...
totalUs += newUs;
#ifdef DEBUG
lastLastUs = lastUs;
#endif
lastUs = currentUs;
break; // Leave the loop on valid update
}
else {
// The read isn't valid. Try again soon.
fiber_sleep(0);
#ifdef DEBUG
retries++;
// Additional debugging info:
uBit.serial.send("\nError");
uBit.serial.send("\nlastLastUs=");
uBit.serial.send((int)lastLastUs);
uBit.serial.send("\nlastUs=");
uBit.serial.send((int)lastUs);
uBit.serial.send("\ncurrentUs=");
uBit.serial.send((int)currentUs);
uBit.serial.send("\n");
#endif
}
}
#ifdef DEBUG
if(retries!=0) {
uBit.serial.send("Retries=");
uBit.serial.send((int)retries);
uBit.serial.send("\nFinal\nlastUs=");
uBit.serial.send((int)lastUs);
uBit.serial.send("\ncurrentUs=");
uBit.serial.send((int)currentUs);
uBit.serial.send("\nlastLastUs=");
uBit.serial.send((int)lastLastUs);
uBit.serial.send("\n");
}
#endif
#endif
return totalUs / 1000000;
}
} // namespace timeanddate
/*
Here's the us_ticker_read():
https://github.com/lancaster-university/mbed-classic/blob/427f56863c9f9a9132a213ec583469dfc61c6244/targets/hal/TARGET_NORDIC/TARGET_MCU_NRF51822/us_ticker.c
Returns:
TMR1_UNITS_TO_MICROSECONDS(tmr1_getCounter64());
Which expands to:
tmr1_getCounter64() (Yeah, that's dumb / unnesc)
And tmr1_getCounter64() is an inline that returns:
return (((uint64_t)(overflowCount)) << 16) | (NRF_TIMER1->CC[2] & MAX_TMR1_COUNTER_VAL);
NOTE THE 16-bit BOUNDARY!!!!
This is a non-atomic read problem. Just reject reads if NRF_TIMER1->CC[2] & MAX_TMR1_COUNTER_VAL is close to the
boundary (and of the phase / wrap around).
*/