Test application for UV, IR, Light and Temperature sensors
[lpc82x] / core / systick.c
1 /****************************************************************************
2  *  core/systick.c
3  *
4  * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  *************************************************************************** */
22 /***************************************************************************** */
23 /*               System Tick Timer                                             */
24 /***************************************************************************** */
26 /* Driver for the internal systick timer of the LPC82x.
27  * Refer to the LPC82x documentation (UM10800.pdf) for more information
28  */
30 #include "core/system.h"
31 #include "core/systick.h"
32 #include "lib/errno.h"
35 /* Static variables */
36 static volatile uint32_t sleep_count = 0;
37 static volatile uint32_t tick_ms = 0;
38 static volatile uint32_t systick_running = 0;
39 static volatile uint32_t tick_reload = 0;
40 static uint32_t usleep_us_count = 0;
42 /* Wraps every 50 days or so with a 1ms tick */
43 static volatile uint32_t global_wrapping_system_ticks = 0;
44 /* The systick cycles run at get_main_clock(), and would wrap more often! */
45 static volatile uint32_t global_wrapping_system_clock_cycles = 0;
48 struct systick_callback {
49         void (*callback) (uint32_t);
50         uint16_t period;
51         uint16_t countdown;
52 };
53 static volatile struct systick_callback cbs[MAX_SYSTICK_CALLBACKS] = {};
55 /* System Tick Timer Interrupt Handler */
56 void SysTick_Handler(void)
57 {
58         int i = 0;
59         global_wrapping_system_ticks++;
60         global_wrapping_system_clock_cycles += tick_reload;
61         if (sleep_count != 0) {
62                 sleep_count--;
63         }
64         for (i = 0; i < MAX_SYSTICK_CALLBACKS; i++) {
65                 if (cbs[i].callback != NULL) {
66                         cbs[i].countdown--;
67                         if (cbs[i].countdown == 0) {
68                                 cbs[i].countdown = cbs[i].period;
69                                 cbs[i].callback(global_wrapping_system_ticks);
70                         }
71                 }
72         }
73 }
76 /* Register a callback to be called every 'period' system ticks.
77  * returns the callback number if registration was OK.
78  * returns negative value on error.
79  * The callback will get the "global_wrapping_system_ticks" as argument, which wraps every 50 days
80  *   or so with a 1ms tick
81  */
82 int add_systick_callback(void (*callback) (uint32_t), uint16_t period)
83 {
84         int i = 0;
85         if (period == 0) {
86                 return -EINVAL;
87         }
88         for (i = 0; i < MAX_SYSTICK_CALLBACKS; i++) {
89                 if (cbs[i].callback == NULL) {
90                         cbs[i].callback = callback;
91                         cbs[i].period = period;
92                         cbs[i].countdown = period;
93                         return i;
94                 }
95         }
96         return -EBUSY;
97 }
99 int remove_systick_callback(void (*callback) (uint32_t))
101         int i = 0;
102         for (i = 0; i < MAX_SYSTICK_CALLBACKS; i++) {
103                 if (cbs[i].callback == callback) {
104                         cbs[i].callback = NULL;
105                         return 0;
106                 }
107         }
108         return -EINVAL;
111 /***************************************************************************** */
112 /* systick timer control function */
114 /* Start the system tick timer
115  * Starting the systick timer also resets the internal tick counters.
116  * If you need a value that goes beyond one start/stop cycle and accross resets,
117  *    then it's up to you to keep track of this using systick_get_tick_count() and/or
118  *    systick_get_clock_cycles().
119  */
120 void systick_start(void)
122         struct lpc_system_tick* systick = LPC_SYSTICK;
123         systick->value = 0;
124         systick_running = 1;
125         global_wrapping_system_ticks = 0;
126         global_wrapping_system_clock_cycles = tick_reload;
127         systick->control |= LPC_SYSTICK_CTRL_ENABLE;
129 /* Stop the system tick timer */
130 void systick_stop(void)
132         struct lpc_system_tick* systick = LPC_SYSTICK;
133         systick->control &= ~(LPC_SYSTICK_CTRL_ENABLE);
134         systick_running = 0;
135         systick->value = 0;
137 /* Reset the system tick timer, making it count down from the reload value again
138  * Reseting the systick timer also resets the internal tick counters.
139  * If you need a value that goes beyond one start/stop cycle and accross resets,
140  *    then it's up to you to keep track of this using systick_get_tick_count() and/or
141  *    systick_get_clock_cycles().
142  */
143 void systick_reset(void)
145         struct lpc_system_tick* systick = LPC_SYSTICK;
146         systick->value = 0;
147         global_wrapping_system_ticks = 0;
148         global_wrapping_system_clock_cycles = tick_reload;
151 /* Get system tick timer current value (counts at get_main_clock() !)
152  * systick_get_timer_val returns a value between 0 and systick_get_timer_reload_val()
153  */
154 uint32_t systick_get_timer_val(void)
156         struct lpc_system_tick* systick = LPC_SYSTICK;
157         return systick->value;
159 /* Get system tick timer reload value */
160 uint32_t systick_get_timer_reload_val(void)
162         return tick_reload;
165 /* Check if systick is running (return 1) or not (return 0) */
166 uint32_t is_systick_running(void)
168         return systick_running;
171 /* Get the system tick period in ms
172  * A vaue of 0 means the system tick timer has not been configured.
173  * Note : calls to msleep() or usleep() will configure the system tick timer
174  *        with a value of 1ms if it was not configured yet.
175  */
176 uint32_t systick_get_tick_ms_period(void)
178         return tick_ms;
181 /* Get the "timer wrapped" indicator.
182  * Used in usleep() function.
183  * Note : the first to call this function will get the right information.
184  * All subsequent calls will get wrong indication.
185  * Thus this function is not exported to user space, user should compare global
186  * ticks values or tick counter values. */
187 static uint32_t systick_counted_to_zero(void)
189         struct lpc_system_tick* systick = LPC_SYSTICK;
190         return (systick->control & LPC_SYSTICK_CTRL_COUNTFLAG);
194 /* Get the number of system ticks ... since last wrapping of the counter, which
195  * is about 50 days with a 1ms system tick. */
196 uint32_t systick_get_tick_count(void)
198         return global_wrapping_system_ticks;
201 /* Get the number of clock cycles ... since last wrapping of the counter. */
202 uint32_t systick_get_clock_cycles(void)
204         struct lpc_system_tick* systick = LPC_SYSTICK;
205         /* global_wrapping_system_clock_cycles has been initialised to reload value, thus there is
206          * no need to add it here, making the call quicker */
207         return global_wrapping_system_clock_cycles - systick->value;
210 /***************************************************************************** */
211 /* Power up the system tick timer.
212  * ms is the interval between system tick timer interrupts. If set to 0, the default
213  *     value is used, which should provide a 1ms period.
214  */
215 void systick_timer_on(uint32_t ms)
217         struct lpc_system_tick* systick = LPC_SYSTICK;
218         uint32_t reload; /* The reload value for the counter */
220         /* Set the reload value */
221         if (ms != 0) {
222                 reload = ((get_main_clock() / 1000) * ms) - 1;
223         } else {
224                 reload = (get_main_clock() / 1000) - 1;
225                 ms = 1;
226         }
227         /* For the LPC82x the system tick clock is fixed to half the frequency of the system clock */
228         reload = reload >> 1; /* Divide by 2 */
229         systick->reload_val = (reload & 0xffffff);
230         tick_ms = ms;
231         tick_reload = systick->reload_val;
233         /* Start counting from the reload value, writting anything would do ... */
234         systick->value = reload;
235         /* Consider we already counted one cycle, making further reading of this count easier */
236         global_wrapping_system_clock_cycles = tick_reload;
238         /* And enable counter interrupt */
239         systick->control = LPC_SYSTICK_CTRL_TICKINT;
240         systick_running = 0;
242         /* Perform this division now for the usleep function. */
243         usleep_us_count = get_main_clock() / (1000 * 1000);
244         /* For the LPC82x the system tick clock is fixed to half the frequency of the system clock */
245         usleep_us_count = (usleep_us_count >> 1); /* Divide by two */
247         /* FIXME : document this */
248         NVIC_SetPriority(SYSTICK_IRQ, ((1 << LPC_NVIC_PRIO_BITS) - 1));
251 /* Removes the main clock from the selected timer block */
252 void systick_timer_off(void)
254         struct lpc_system_tick* systick = LPC_SYSTICK;
255         systick->control = 0;
256         systick->reload_val = 0;
257         tick_ms = 0;
258         tick_reload = 0;
259         systick_running = 0;
263 /***************************************************************************** */
264 /* Sleeping functions */
266 /* Set the sleep countdown value
267  * A sleep will end when this value reaches 0
268  * Note that calls to this function while a sleep() has been initiated will change the
269  *   sleep duration ....
270  */
271 static inline void set_sleep(uint32_t ticks)
273         sleep_count = ticks;
276 /* Actual sleep function, checks that system tick counter is configured to generate
277  * an interrupt and to move sleep_count down to 0
278  */
279 #define SYSTICK_CAN_SLEEP   (LPC_SYSTICK_CTRL_TICKINT | LPC_SYSTICK_CTRL_ENABLE)
280 static uint32_t sleep(void)
282         struct lpc_system_tick* systick = LPC_SYSTICK;
283         if ((systick->control & SYSTICK_CAN_SLEEP) != SYSTICK_CAN_SLEEP) {
284                 return -1;
285         }
286         do { } while (sleep_count != 0);
287         return 0;
290 /* This msleep sleeps less than the required amount of time as it forgets about
291  * the already elapsed time of the systick timer since last tick. */
292 void msleep(uint32_t ms)
294         uint32_t ticks = 0;
296         if (tick_ms == 0) {
297                 systick_timer_on(1);
298                 ticks = ms;
299         } else {
300                 ticks = ms / tick_ms;
301         }
302         set_sleep(ticks);
303         if (systick_running == 0) {
304                 systick_start();
305         }
306         sleep();
309 /* This usleep function tries to sleep at most the required amount of time.
310  * The setup is so long that it cannot sleep for less than 10us when running at 48MHz
311  */
312 void usleep(uint32_t us)
314         struct lpc_system_tick* systick = LPC_SYSTICK;
315         uint32_t start = systick->value; /* Grab the starting (call time) value now */
316         uint32_t count = 0;
317         uint32_t end = 0;
319         end = systick_counted_to_zero(); /* erase loop indicator */
320         if (us > 1000) {
321                 msleep(us / 1000);
322                 us = us % 1000;
323         } else {
324                 if (systick_running == 0) {
325                         if (tick_ms == 0) {
326                                 systick_timer_on(1);
327                         }
328                         systick_start();
329                 }
330         }
331         count = usleep_us_count * us;
332          /* Remember that systick is a decrementing counter */
333         if (count > start) {
334                 end = (systick->reload_val - (count - start));
335                 do { } while (systick_counted_to_zero() == 0); /* Wait for timer loop */
336                 do { } while (systick->value > end); /* Wait for remaining part of sleep duration */
337         } else {
338                 end = start - count;
339                 /* Wait for sleep duration.
340                  * If the counter looped, it means we already waited too much */
341                 do { } while ((systick->value > end) && (systick_counted_to_zero() == 0));
342         }
345 void usleep_short(uint32_t us)
347         struct lpc_system_tick* systick = LPC_SYSTICK;
348         uint32_t start = systick->value; /* Grab the starting (call time) value now */
349         uint32_t count = usleep_us_count * us;
350         uint32_t end = systick_counted_to_zero(); /* Erase loop indicator */
352          /* Remember that systick is a decrementing counter */
353         if (count > start) {
354                 end = (systick->reload_val - (count - start));
355                 do { } while (systick_counted_to_zero() == 0); /* Wait for timer loop */
356                 do { } while (systick->value > end); /* Wait for remaining part of sleep duration */
357         } else {
358                 end = start - count;
359                 /* Wait for sleep duration.
360                  * If the counter looped, it means we already waited too much */
361                 do { } while ((systick->value > end) && (systick_counted_to_zero() == 0));
362         }