Initial commit, code based on LPC1224 support and LPC812-MAX CMSIS-DAP interface...
[lpc11u3x] / lib / time.c
1 /****************************************************************************
2  *   lib/time.c
3  *
4  *
5  * Copyright 2013-2014 Nathael Pajani <nathael.pajani@ed3l.fr>
6  *
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  *
21  ****************************************************************************/
23 #include <stdint.h>
24 #include "core/lpc_core_cm0.h"
25 #include "core/system.h"
26 #include "core/systick.h"
27 #include "lib/time.h"
28 #include "lib/string.h"
31 /******************************************************************************/
32 /* Common parts for time handling for LPC Boards with or without RTC */
34 /* Notes on time tracking using this library :
35  *
36  * - Unless there is some known good time source used to set time (using set_time()) the time is
37  *     counted from the system power ON or RTC power ON as origin.
38  *
39  * - This code relies on systick timer configured with a 1ms period (systick_timer_on(1)) for whole
40  *     time on systems without a RTC oscilator and on systick timer for mili-seconds only on systems
41  *     with a 32.768 KHz external oscilator for the RTC.
42  *
43  * - When used with systick only, the time tracking is far from the precision time one should
44  *     obtain using the RTC with an external 32.768 KHz cristal. It uses the 12MHz internal RC
45  *     Oscilator, which is at 1% accuracy.
46  *     Thus the time error may be as much as 1s every 100 seconds !
47  */
49 static volatile struct time_spec time = { 0, 0, };
50 static volatile uint32_t time_lock = 0;;
52 /* Interupt routine which keeps track of the time */
53 void time_track(uint32_t ms)
54 {
55     /* This lock may have us miss one ms when time is changed, but this is perfectly OK, time is
56      *    being changed !
57      * Anyway, we are in interrupt context, we MUST NOT loop or sleep !
58      */
59     if (sync_lock_test_and_set(&time_lock, 1) == 1) {
60         return;
61     }
62     time.msec++;
63     if (time.msec >= 1000) {
64         time.msec = 0;
65         time.seconds++;
66     }
67     sync_lock_release(&time_lock);
68 }
70 /* Call this to set the time from a known good time source. */
71 void set_time(struct time_spec* new_time)
72 {
73     /* We are not in interrupt context, we can wait for the lock to be released */
74     while (sync_lock_test_and_set(&time_lock, 1) == 1) {};
75     time.seconds = new_time->seconds;
76     time.msec = new_time->msec;
77     sync_lock_release(&time_lock);
78 }
80 /* Call this to set the time from a known good time source.
81  * This function returns the time difference in the given time_spec.
82  */
83 void set_time_and_get_difference(struct time_spec* new_time, struct time_spec* diff)
84 {
85         struct time_spec tmp_old;
86         if (new_time == NULL) {
87                 return;
88         }
89     /* We are not in interrupt context, we can wait for the lock to be released */
90     while (sync_lock_test_and_set(&time_lock, 1) == 1) {};
91         /* Save the old time */
92         tmp_old.seconds = time.seconds;
93         tmp_old.msec = time.msec;
94         /* Set the time as soon as possible */
95     time.seconds = new_time->seconds;
96     time.msec = new_time->msec;
97     sync_lock_release(&time_lock);
99         /* And now compute the time difference */
100         if (diff == NULL) {
101                 return;
102         }
103         if (new_time->seconds == tmp_old.seconds) {
104                 diff->msec = new_time->msec - tmp_old.msec;
105         } else {
106                 diff->seconds = new_time->seconds - tmp_old.seconds;
107                 if (new_time->seconds > tmp_old.seconds) {
108                         diff->msec = (1000 - tmp_old.msec) + new_time->msec;
109                 } else {
110                         diff->msec = (1000 - new_time->msec) + tmp_old.msec;
111                 }
112                 if (diff->msec > 1000) {
113                         diff->msec -= 1000;
114                 } else {
115                         diff->seconds -= 1;
116                 }
117         }
121 /* Put a time struct in a buffer, swapping both fields to network endian. */
122 void time_to_buff_swapped(uint8_t* buf, struct time_spec* src_time)
124         struct time_spec time; /* Warning, this one will hold time in network endian ! */
125         time.seconds = byte_swap_32(src_time->seconds);
126         time.msec = (uint16_t)byte_swap_16(src_time->msec);
127         memcpy(buf, &(time.seconds), 4);
128         memcpy((buf + 4), &(time.msec), 2);
132 /* Get a snapshot of the time when this is called.
133  * It is unsafe to call this one when in interrupt, use get_time_in_interrupt()
134  */
135 void get_time(struct time_spec* save_time)
137         /* FIXME : Check that we are not in interrupt ... */
138         if (get_priority_mask() == 0) {
139                 get_time_in_interrupt(save_time);
140                 return;
141         }
142     /* We are not in interrupt context, we can wait for the lock to be released */
143     while (sync_lock_test_and_set(&time_lock, 1) == 1) {};
144         save_time->seconds = time.seconds;
145         save_time->msec = time.msec;
146     sync_lock_release(&time_lock);
149 /* Get a snapshot of the time when this is called
150  * It is safe to call this one in interrupt.
151  */
152 void get_time_in_interrupt(struct time_spec* save_time)
154     /* We are in interrupt context, we can't wait for the lock to be released */
155         save_time->seconds = time.seconds;
156         save_time->msec = time.msec;
159 /* Must be called once to register the systick callback. */
160 static uint8_t time_configured = 0;
161 void time_init(void)
163         if (time_configured != 0) {
164                 return;
165         }
166         time_configured = 1;
167         add_systick_callback(time_track, 1); /* callback, period (ms) */
171 /* Compute a time difference
172  * Return 0 if both times are the same, 1 if (t1 > t2), -1 if (t1 < t2)
173  */
174 int get_time_diff(const struct time_spec* t1, const struct time_spec* t2, struct time_spec* diff)
176         if (t1->seconds < t2->seconds) {
177                 diff->seconds = (t2->seconds - t1->seconds - 1);
178                 diff->msec = ((t2->msec + 1000) - t1->msec);
179                 if (diff->msec >= 1000) {
180                         diff->msec -= 1000;
181                         diff->seconds++;
182                 }
183                 return -1;
184         } else if (t1->seconds == t2->seconds) {
185                 diff->seconds = 0;
186                 if (t1->msec < t2->msec) {
187                         diff->msec = t2->msec - t1->msec;
188                         return -1;
189                 } else if (t1->msec == t2->msec) {
190                         diff->msec = 0;
191                         return 0;
192                 } else {
193                         diff->msec = t1->msec - t2->msec;
194                         return 1;
195                 }
196         } else {
197                 diff->seconds = (t1->seconds - t2->seconds - 1);
198                 diff->msec = ((t1->msec + 1000) - t2->msec);
199                 if (diff->msec >= 1000) {
200                         diff->msec -= 1000;
201                         diff->seconds++;
202                 }
203                 return 1;
204         }