From 56ffaa3f4002a04b34d77a37935b406abffd3049 Mon Sep 17 00:00:00 2001 From: Nathael Pajani Date: Wed, 2 Sep 2015 11:13:42 +0200 Subject: [PATCH] First bits of time handling Must be modified for use of the RTC interrupt for seconds counting when there is a RTC external oscilator as stated in the comments. Not fully tested yet. --- include/lib/time.h | 85 +++++++++++++++++++++++ lib/time.c | 168 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 253 insertions(+) create mode 100644 include/lib/time.h create mode 100644 lib/time.c diff --git a/include/lib/time.h b/include/lib/time.h new file mode 100644 index 0000000..6f549ed --- /dev/null +++ b/include/lib/time.h @@ -0,0 +1,85 @@ +/**************************************************************************** + * lib/time.h + * + * + * + * Copyright 2013-2014 Nathael Pajani + * + * + * 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 . + * + ****************************************************************************/ + +#ifndef LIB_TIME_H +#define LIB_TIME_H + + +#include + + +/******************************************************************************/ +/* 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 index 0000000..8f428b1 --- /dev/null +++ b/lib/time.c @@ -0,0 +1,168 @@ +/**************************************************************************** + * lib/time.c + * + * + * Copyright 2013-2014 Nathael Pajani + * + * + * 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 . + * + ****************************************************************************/ + +#include +#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); +} + + -- 2.43.0