From: Nathael Pajani Date: Fri, 13 Dec 2013 10:16:15 +0000 (+0100) Subject: System tick support Used to implement precise sleeping functions. Adds msleep and... X-Git-Url: http://git.techno-innov.fr/?a=commitdiff_plain;h=8ad6958305f66e3d3cd5b3f654f86bd4fc3028f3;p=soft%2Flpc122x%2Fcore System tick support Used to implement precise sleeping functions. Adds msleep and usleep functions --- diff --git a/core/fault_handlers.c b/core/fault_handlers.c index a2ee78f..d1bfe25 100644 --- a/core/fault_handlers.c +++ b/core/fault_handlers.c @@ -47,10 +47,6 @@ void PendSV_Handler(void) { fault_info(__FUNCTION__, sizeof(__FUNCTION__)); } -void SysTick_Handler(void) -{ - fault_info(__FUNCTION__, sizeof(__FUNCTION__)); -} diff --git a/core/system.c b/core/system.c index 2c65624..3db2330 100644 --- a/core/system.c +++ b/core/system.c @@ -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 index 0000000..cd6f2e2 --- /dev/null +++ b/core/systick.c @@ -0,0 +1,223 @@ +/**************************************************************************** + * drivers/systick.c + * + * Copyright 2012 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 . + * + *************************************************************************** */ + + + +/***************************************************************************** */ +/* System Tick Timer */ +/***************************************************************************** */ + +#include +#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)); + } +} + diff --git a/include/core/lpc_regs_12xx.h b/include/core/lpc_regs_12xx.h index 59748f8..186e176 100644 --- a/include/core/lpc_regs_12xx.h +++ b/include/core/lpc_regs_12xx.h @@ -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 */ diff --git a/include/core/system.h b/include/core/system.h index 17b7834..be2e939 100644 --- a/include/core/system.h +++ b/include/core/system.h @@ -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 */