System tick support Used to implement precise sleeping functions. Adds msleep and...
authorNathael Pajani <nathael.pajani@ed3l.fr>
Fri, 13 Dec 2013 10:16:15 +0000 (11:16 +0100)
committerNathael Pajani <nathael.pajani@ed3l.fr>
Tue, 8 Nov 2022 16:03:04 +0000 (17:03 +0100)
core/fault_handlers.c
core/system.c
core/systick.c [new file with mode: 0644]
include/core/lpc_regs_12xx.h
include/core/system.h

index a2ee78f..d1bfe25 100644 (file)
@@ -47,10 +47,6 @@ void PendSV_Handler(void)
 {
        fault_info(__FUNCTION__, sizeof(__FUNCTION__));
 }
-void SysTick_Handler(void)
-{
-       fault_info(__FUNCTION__, sizeof(__FUNCTION__));
-}
 
 
 
index 2c65624..3db2330 100644 (file)
@@ -300,3 +300,36 @@ void system_set_default_pins(void)
        set_ssp_pins();
        set_adc_pins();
 }
+
+/* FIXME: We should add some system-wide way to "reserve" a pin.
+     This would not prevent anyone from configuring the same pin for two diferent
+     functions.
+ */
+
+
+/***************************************************************************** */
+/* Note that if the systick core functions are used these will be overridden */
+
+/* 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 !
+ */
+static void def_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 !
+ */
+static inline void def_usleep(uint32_t us)
+{
+       volatile uint32_t dec = us * 2;
+       while (dec--);
+}
+
+void msleep(uint32_t ms) __attribute__ ((weak, alias ("def_msleep")));
+void usleep(uint32_t ms) __attribute__ ((weak, alias ("def_usleep")));
+
diff --git a/core/systick.c b/core/systick.c
new file mode 100644 (file)
index 0000000..cd6f2e2
--- /dev/null
@@ -0,0 +1,223 @@
+/****************************************************************************
+ *  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));
+       }
+}
+
index 59748f8..186e176 100644 (file)
@@ -293,30 +293,30 @@ struct syst_ctrl_block_regs {
 /*                    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 */
 
 
 
index 17b7834..be2e939 100644 (file)
@@ -103,24 +103,51 @@ void clkout_on(uint32_t src, uint32_t div);
 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 */