First bits of time handling Must be modified for use of the RTC interrupt for seconds...
authorNathael Pajani <nathael.pajani@ed3l.fr>
Wed, 2 Sep 2015 09:13:42 +0000 (11:13 +0200)
committerNathael Pajani <nathael.pajani@ed3l.fr>
Tue, 8 Nov 2022 16:03:04 +0000 (17:03 +0100)
include/lib/time.h [new file with mode: 0644]
lib/time.c [new file with mode: 0644]

diff --git a/include/lib/time.h b/include/lib/time.h
new file mode 100644 (file)
index 0000000..6f549ed
--- /dev/null
@@ -0,0 +1,85 @@
+/****************************************************************************
+ *   lib/time.h
+ *
+ *
+ *
+ * Copyright 2013-2014 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ****************************************************************************/
+
+#ifndef LIB_TIME_H
+#define LIB_TIME_H
+
+
+#include <stdint.h>
+
+
+/******************************************************************************/
+/* Common parts for time handling for LPC Boards with or without RTC */
+
+/* Notes on time tracking using this library :
+ *
+ * - Unless there is some known good time source used to set time (using set_time()) the time is
+ *     counted from the system power ON or RTC power ON as origin.
+ *
+ * - This code relies on systick timer configured with a 1ms period (systick_timer_on(1)) for whole
+ *     time on systems without a RTC oscilator and on systick timer for mili-seconds only on systems
+ *     with a 32.768 KHz external oscilator for the RTC.
+ *
+ * - When used with systick only, the time tracking is far from the precision time one should
+ *     obtain using the RTC with an external 32.768 KHz cristal. It uses the 12MHz internal RC
+ *     Oscilator, which is at 1% accuracy.
+ *     Thus the time error may be as much as 1s every 100 seconds !
+ */
+
+struct time_spec {
+       uint32_t seconds;
+       uint16_t msec;
+};
+
+
+/* Call this to set the time from a known good time source. */
+void set_time(struct time_spec* new_time);
+
+
+/* Call this to set the time from a known good time source.
+ * This function returns the time difference in the given time_spec.
+ */
+void set_time_and_get_difference(struct time_spec* new_time, struct time_spec* diff);
+
+
+/* Put a time struct in a buffer, swapping both fields to network endian. */
+void time_to_buff_swapped(uint8_t* buf, struct time_spec* src_time);
+
+
+/* Get a snapshot of the time when this is called.
+ * It is unsafe to call this one when in interrupt, use get_time_in_interrupt()
+ */
+void get_time(struct time_spec* save_time);
+
+/* Get a snapshot of the time when this is called
+ * It is safe to call this one in interrupt.
+ */
+void get_time_in_interrupt(struct time_spec* save_time);
+
+/* Must be called once to register the systick callback. */
+void time_init(void);
+
+
+
+#endif /* LIB_TIME_H */
+
diff --git a/lib/time.c b/lib/time.c
new file mode 100644 (file)
index 0000000..8f428b1
--- /dev/null
@@ -0,0 +1,168 @@
+/****************************************************************************
+ *   lib/time.c
+ *
+ *
+ * Copyright 2013-2014 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ****************************************************************************/
+
+#include <stdint.h>
+#include "core/lpc_core_cm0.h"
+#include "core/system.h"
+#include "lib/time.h"
+#include "lib/string.h"
+
+
+/******************************************************************************/
+/* Common parts for time handling for LPC Boards with or without RTC */
+
+/* Notes on time tracking using this library :
+ *
+ * - Unless there is some known good time source used to set time (using set_time()) the time is
+ *     counted from the system power ON or RTC power ON as origin.
+ *
+ * - This code relies on systick timer configured with a 1ms period (systick_timer_on(1)) for whole
+ *     time on systems without a RTC oscilator and on systick timer for mili-seconds only on systems
+ *     with a 32.768 KHz external oscilator for the RTC.
+ *
+ * - When used with systick only, the time tracking is far from the precision time one should
+ *     obtain using the RTC with an external 32.768 KHz cristal. It uses the 12MHz internal RC
+ *     Oscilator, which is at 1% accuracy.
+ *     Thus the time error may be as much as 1s every 100 seconds !
+ */
+
+static volatile struct time_spec time = { 0, 0, };
+static volatile uint32_t time_lock = 0;;
+
+/* Interupt routine which keeps track of the time */
+void time_track(uint32_t ms)
+{
+    /* This lock may have us miss one ms when time is changed, but this is perfectly OK, time is
+     *    being changed !
+     * Anyway, we are in interrupt context, we MUST NOT loop or sleep !
+     */
+    if (sync_lock_test_and_set(&time_lock, 1) == 1) {
+        return;
+    }
+    time.msec++;
+    if (time.msec >= 1000) {
+        time.msec = 0;
+        time.seconds++;
+    }
+    sync_lock_release(&time_lock);
+}
+
+/* Call this to set the time from a known good time source. */
+void set_time(struct time_spec* new_time)
+{
+    /* We are not in interrupt context, we can wait for the lock to be released */
+    while (sync_lock_test_and_set(&time_lock, 1) == 1) {};
+    time.seconds = new_time->seconds;
+    time.msec = new_time->msec;
+    sync_lock_release(&time_lock);
+}
+
+/* Call this to set the time from a known good time source.
+ * This function returns the time difference in the given time_spec.
+ */
+void set_time_and_get_difference(struct time_spec* new_time, struct time_spec* diff)
+{
+       struct time_spec tmp_old;
+       if (new_time == NULL) {
+               return;
+       }
+    /* We are not in interrupt context, we can wait for the lock to be released */
+    while (sync_lock_test_and_set(&time_lock, 1) == 1) {};
+       /* Save the old time */
+       tmp_old.seconds = time.seconds;
+       tmp_old.msec = time.msec;
+       /* Set the time as soon as possible */
+    time.seconds = new_time->seconds;
+    time.msec = new_time->msec;
+    sync_lock_release(&time_lock);
+
+       /* And now compute the time difference */
+       if (diff == NULL) {
+               return;
+       }
+       if (new_time->seconds == tmp_old.seconds) {
+               diff->msec = new_time->msec - tmp_old.msec;
+       } else {
+               diff->seconds = new_time->seconds - tmp_old.seconds;
+               if (new_time->seconds > tmp_old.seconds) {
+                       diff->msec = (1000 - tmp_old.msec) + new_time->msec;
+               } else {
+                       diff->msec = (1000 - new_time->msec) + tmp_old.msec;
+               }
+               if (diff->msec > 1000) {
+                       diff->msec -= 1000;
+               } else {
+                       diff->seconds -= 1;
+               }
+       }
+}
+
+
+/* Put a time struct in a buffer, swapping both fields to network endian. */
+void time_to_buff_swapped(uint8_t* buf, struct time_spec* src_time)
+{
+       struct time_spec time; /* Warning, this one will hold time in network endian ! */
+       time.seconds = byte_swap_32(src_time->seconds);
+       time.msec = (uint16_t)byte_swap_16(src_time->msec);
+       memcpy(buf, &(time.seconds), 4);
+       memcpy((buf + 4), &(time.msec), 2);
+}
+
+
+/* Get a snapshot of the time when this is called.
+ * It is unsafe to call this one when in interrupt, use get_time_in_interrupt()
+ */
+void get_time(struct time_spec* save_time)
+{
+       /* FIXME : Check that we are not in interrupt ... */
+    /* We are not in interrupt context, we can wait for the lock to be released */
+    while (sync_lock_test_and_set(&time_lock, 1) == 1) {};
+       save_time->seconds = time.seconds;
+       save_time->msec = time.msec;
+    sync_lock_release(&time_lock);
+}
+
+/* Get a snapshot of the time when this is called
+ * It is safe to call this one in interrupt.
+ */
+void get_time_in_interrupt(struct time_spec* save_time)
+{
+    /* We are in interrupt context, we can wait for the lock to be released */
+       save_time->seconds = time.seconds;
+       save_time->msec = time.msec;
+}
+
+/* Must be called once to register the systick callback. */
+static uint8_t time_configured = 0;
+void time_init(void)
+{
+    while (sync_lock_test_and_set(&time_lock, 1) == 1) {};
+       if (time_configured != 0) {
+       sync_lock_release(&time_lock);
+               return;
+       }
+       time_configured = 1;
+       add_systick_callback(time_track, 1); /* callback, period (ms) */
+    sync_lock_release(&time_lock);
+}
+
+