--- /dev/null
+/****************************************************************************
+ * drivers/systick.c
+ *
+ * Copyright 2012 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/>.
+ *
+ *************************************************************************** */
+
+
+
+/***************************************************************************** */
+/* System Tick Timer */
+/***************************************************************************** */
+
+#include <stdint.h>
+#include "core/lpc_regs_12xx.h"
+#include "core/lpc_core_cm0.h"
+#include "core/system.h"
+
+
+/* Static variables */
+static volatile uint32_t sleep_count = 0;
+static volatile uint32_t tick_ms = 0;
+static volatile uint32_t systick_running = 0;
+
+/* Wraps every 50 days or so with a 1ms tick */
+static volatile uint32_t global_wrapping_system_ticks = 0;
+
+/* System Tick Timer Interrupt Handler */
+void SysTick_Handler(void)
+{
+ global_wrapping_system_ticks++;
+ if (sleep_count != 0) {
+ sleep_count--;
+ }
+}
+
+
+
+/***************************************************************************** */
+/* systick timer control function */
+
+/* Start the system tick timer */
+void systick_start(void)
+{
+ struct lpc_system_tick* systick = LPC_SYSTICK;
+ systick->value = 0;
+ systick_running = 1;
+ systick->control |= LPC_SYSTICK_CTRL_ENABLE;
+}
+/* Stop the system tick timer */
+void systick_stop(void)
+{
+ struct lpc_system_tick* systick = LPC_SYSTICK;
+ systick->control &= ~(LPC_SYSTICK_CTRL_ENABLE);
+ systick_running = 0;
+ systick->value = 0;
+}
+/* Reset the system tick timer, making it count down from the reload value again */
+void systick_reset(void)
+{
+ struct lpc_system_tick* systick = LPC_SYSTICK;
+ systick->value = 0;
+}
+
+uint32_t systick_get_timer_val(void)
+{
+ struct lpc_system_tick* systick = LPC_SYSTICK;
+ return systick->value;
+}
+/* Get the "timer wrapped" indicator.
+ * Note : the first to call this function will get the right information.
+ * All subsequent calls will get wrong indication.
+ * Thus this function is not exported to user space, user should compare global
+ * ticks values or tick counter values. */
+static uint32_t systick_counted_to_zero(void)
+{
+ struct lpc_system_tick* systick = LPC_SYSTICK;
+ return (systick->control & LPC_SYSTICK_CTRL_COUNTFLAG);
+}
+
+
+uint32_t systick_get_tick_count(void)
+{
+ return global_wrapping_system_ticks;
+}
+
+/***************************************************************************** */
+/* Power up the system tick timer.
+ * ms is the interval between system tick timer interrupts. If set to 0, the default
+ * value is used, which should provide a 1ms period.
+ */
+void systick_timer_on(uint32_t ms)
+{
+ struct lpc_system_tick* systick = LPC_SYSTICK;
+ uint32_t reload; /* The reload value for the counter */
+
+ /* Set the reload value */
+ if (ms != 0) {
+ reload = ((get_main_clock() / 1000) * ms) - 1;
+ } else {
+ reload = (get_main_clock() / 1000) - 1;
+ ms = 1;
+ }
+ systick->reload_val = reload;
+ tick_ms = ms;
+
+ /* Start counting from the reload value, writting anything would do ... */
+ systick->value = 0;
+
+ /* And enable counter interrupt */
+ systick->control = (LPC_SYSTICK_CTRL_TICKINT | LPC_SYSTICK_CTRL_CLKSOURCE);
+ systick_running = 0;
+
+ NVIC_SetPriority(SYSTICK_IRQ, ((1 << LPC_NVIC_PRIO_BITS) - 1));
+}
+
+/* Removes the main clock from the selected timer block */
+void systick_timer_off(void)
+{
+ struct lpc_system_tick* systick = LPC_SYSTICK;
+ systick->control = 0;
+ systick->reload_val = 0;
+ tick_ms = 0;
+ systick_running = 0;
+}
+
+
+/***************************************************************************** */
+/* Sleeping functions */
+
+/* Set the sleep countdown value
+ * A sleep will end when this value reaches 0
+ * Note that calls to this function while a sleep() has been initiated will change the
+ * sleep duration ....
+ */
+void set_sleep(uint32_t ticks)
+{
+ sleep_count = ticks;
+}
+/* Return current sleep count_down counter */
+uint32_t get_sleep(void)
+{
+ return sleep_count;
+}
+
+/* Actual sleep function, checks that system tick counter is configured to generate
+ * an interrupt to move sleep_count down to 0
+ */
+#define SYSTICK_CAN_SLEEP (LPC_SYSTICK_CTRL_TICKINT | LPC_SYSTICK_CTRL_ENABLE)
+uint32_t sleep(void)
+{
+ struct lpc_system_tick* systick = LPC_SYSTICK;
+ if ((systick->control & SYSTICK_CAN_SLEEP) != SYSTICK_CAN_SLEEP) {
+ return -1;
+ }
+ do { } while (sleep_count != 0);
+ return 0;
+}
+
+/* This msleep sleeps less than the required amount of time as it forgets about
+ * the already elapsed time of the systick timer since last tick. */
+void msleep(uint32_t ms)
+{
+ uint32_t ticks = 0;
+
+ if (tick_ms == 0) {
+ systick_timer_on(1);
+ ticks = ms;
+ } else {
+ ticks = ms / tick_ms;
+ }
+ set_sleep(ticks);
+ if (systick_running == 0) {
+ systick_start();
+ }
+ sleep();
+}
+
+void usleep(uint32_t us)
+{
+ struct lpc_system_tick* systick = LPC_SYSTICK;
+ uint32_t start = systick->value; /* Grab the starting (call time) value now */
+ uint32_t count = 0;
+ uint32_t end = 0;
+
+ end = systick_counted_to_zero(); /* erase loop indicator */
+ if (us > 1000) {
+ msleep(us / 1000);
+ us = us % 1000;
+ } else {
+ if (systick_running == 0) {
+ if (tick_ms == 0) {
+ systick_timer_on(1);
+ }
+ systick_start();
+ }
+ }
+ count = get_main_clock() / (1000 * 1000) * us;
+ if (count > start) {
+ end = (systick->reload_val - (count - start));
+ do { } while (systick_counted_to_zero() == 0); /* Wait for timer loop */
+ do { } while (systick->value > end); /* Wait for sleep duration */
+ } else {
+ end = start - count;
+ /* Wait for sleep duration.
+ * If the counter looped, it means we already waited too much */
+ do { } while ((systick->value > end) && (systick_counted_to_zero() == 0));
+ }
+}
+
/* Cortex-M0 System Timer */
/***************************************************************************** */
/* Cortex-M0 System Timer Registers */
-struct system_tick_regs {
- volatile uint32_t ctrl; /* 0x000 : SysTick Control and Status Register (R/W) */
- volatile uint32_t load; /* 0x004 : SysTick Reload Value Register (R/W) */
- volatile uint32_t val; /* 0x008 : SysTick Current Value Register (R/W) */
- volatile const uint32_t calib; /* 0x00C : SysTick Calibration Register (R/ ) */
+struct lpc_system_tick {
+ volatile uint32_t control; /* 0x000 : SysTick Control and Status Register (R/W) */
+ volatile uint32_t reload_val; /* 0x004 : SysTick Reload Value Register (R/W) */
+ volatile uint32_t value; /* 0x008 : SysTick Current Value Register (R/W) */
+ volatile const uint32_t calibration; /* 0x00C : SysTick Calibration Register (R/ ) */
};
-#define LPC_SYSTICK ((struct system_tick_regs *) LPC_SYSTICK_BASE) /* SysTick configuration struct */
+#define LPC_SYSTICK ((struct lpc_system_tick*) LPC_SYSTICK_BASE) /* SysTick configuration struct */
/* SysTick Control / Status Register Definitions */
-#define SysTick_CTRL_COUNTFLAG_Msk (1UL << 16) /* SysTick CTRL: COUNTFLAG Mask */
-#define SysTick_CTRL_CLKSOURCE_Msk (1UL << 2) /* SysTick CTRL: CLKSOURCE Mask */
-#define SysTick_CTRL_TICKINT_Msk (1UL << 1) /* SysTick CTRL: TICKINT Mask */
-#define SysTick_CTRL_ENABLE_Msk (1UL << 0) /* SysTick CTRL: ENABLE Mask */
+#define LPC_SYSTICK_CTRL_COUNTFLAG (1UL << 16) /* SysTick CTRL: COUNTFLAG Mask */
+#define LPC_SYSTICK_CTRL_CLKSOURCE (1UL << 2) /* SysTick CTRL: CLKSOURCE Mask */
+#define LPC_SYSTICK_CTRL_TICKINT (1UL << 1) /* SysTick CTRL: TICKINT Mask */
+#define LPC_SYSTICK_CTRL_ENABLE (1UL << 0) /* SysTick CTRL: ENABLE Mask */
/* SysTick Reload Register Definitions */
-#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL) /* SysTick LOAD: RELOAD Mask */
+#define LPC_SYSTICK_LOAD_RELOAD (0xFFFFFFUL) /* SysTick LOAD: RELOAD Mask */
/* SysTick Current Register Definitions */
-#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL) /* SysTick VAL: CURRENT Mask */
+#define LPC_SYSTICK_VAL_CURRENT (0xFFFFFFUL) /* SysTick VAL: CURRENT Mask */
/* SysTick Calibration Register Definitions */
-#define SysTick_CALIB_NOREF_Msk (1UL << 31) /* SysTick CALIB: NOREF Mask */
-#define SysTick_CALIB_SKEW_Msk (1UL << 30) /* SysTick CALIB: SKEW Mask */
-#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL) /* SysTick CALIB: TENMS Mask */
+#define LPC_SYSTICK_CALIB_NOREF (1UL << 31) /* SysTick CALIB: NOREF Mask */
+#define LPC_SYSTICK_CALIB_SKEW (1UL << 30) /* SysTick CALIB: SKEW Mask */
+#define LPC_SYSTICK_CALIB_TENMS (0xFFFFFFUL) /* SysTick CALIB: TENMS Mask */
void clkout_off(void);
-/* This "msleep" is a simple wait routine which is close to a millisecond sleep
- * whith a CPU Clock of 24MHz, but has no exact relation to time.
- * It is highly dependent to CPU clock speed anyway.
- * Note : This is an active sleep !
+
+/***************************************************************************** */
+/* System Tick Timer */
+/***************************************************************************** */
+
+/* Start the system tick timer */
+void systick_start(void);
+/* Stop the system tick timer */
+void systick_stop(void);
+/* Reset the system tick timer, making it count down from the reload value again */
+void systick_reset(void);
+/* Get system tick timer current value */
+uint32_t systick_get_timer_val(void);
+
+/* Get the system tick */
+uint32_t systick_get_tick_count(void);
+
+/* Power up the system tick timer.
+ * ms is the interval between system tick timer interrupts. If set to 0, the default
+ * value is used, which should provide a 1ms period.
+ */
+void systick_timer_on(uint32_t ms);
+
+/* Removes the main clock from the selected timer block */
+void systick_timer_off(void);
+
+
+/* Sleeping functions */
+
+/* Set the sleep countdown value
+ * A sleep will end when this value reaches 0
+ * Note that calls to this function while a sleep() has been initiated will change the
+ * sleep duration ....
*/
-static inline void msleep(uint32_t ms)
-{
- volatile uint32_t dec = ms * 2667;
- while (dec--);
-}
-
-/* Something that's not too far from a microsecond sleep at 24MHz CPU Clock
- * Note : This is an active sleep !
+void set_sleep(uint32_t ticks);
+/* Return current sleep count_down counter */
+uint32_t get_sleep(void);
+
+/* Actual sleep function, checks that system tick counter is configured to generate
+ * an interrupt to move sleep_count down to 0
*/
-static inline void usleep(uint32_t us)
-{
- volatile uint32_t dec = us * 2;
- while (dec--);
-}
+uint32_t sleep(void);
+
+void msleep(uint32_t ms);
+void usleep(uint32_t ms);
+
#endif /* CORE_SYSTEM_H */