From 8254d40c42bb44f0cd54b60685bd50d18f90fe9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D1=81=D0=BB=D0=BE=D0=B2=20=D0=9D=D0=B8=D0=BA?= =?UTF-8?q?=D0=B8=D1=82=D0=B0?= Date: Sat, 28 Sep 2013 11:42:56 +0400 Subject: [PATCH 1/9] Update tmgr library (need testing) Signed-off-by: Maslov Nikita --- include/lib/tasks.h | 61 ------------------ include/lib/tmgr.h | 28 ++++++++ src/lib/ulc/tmgr.c | 151 ++++++++++++++++---------------------------- 3 files changed, 82 insertions(+), 158 deletions(-) delete mode 100644 include/lib/tasks.h create mode 100644 include/lib/tmgr.h diff --git a/include/lib/tasks.h b/include/lib/tasks.h deleted file mode 100644 index be8c45f..0000000 --- a/include/lib/tasks.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef TMGR_H -#define TMGR_H - -/** @defgroup Defines section - * @{ - */ - -#define TIME_OVER 1 // do not change. hm... doesn't matter :) - -/** @} - */ - -/** @defgroup Config section - * @{ - */ -#ifndef TASKS_CONFIG -// Config: if defined, you can add only 1 handler for 1 function-listener -// Optimises the work of the task manager if you use it for delayed looped run -#define REM_HANDLER_IS_DEFINED - -#endif -/** @} - */ - - -/** @defgroup About variables - * @{ - */ - -// Number of registered handlers - -// Typedef for handler data structure -typedef struct hndlr{ - void (*handler)(void); // address of handler function (void) - unsigned int uptime; // uptime to run - // private: address of the next structure in list - struct hndlr * next; -} handler_t; - -/** @} - */ - -/** @defgroup About functions - * @{ - */ - -// Returns 0 in normal case, or 1 (TIME_OVER constant) if system uptime is above than handler's start time -int tmgr_register(handler_t * data); // NOTE: It's better if data structure declared in global scope - // And, DO NOT CHANGE STRUCTURES OF REGISTERED HANDLERS! - // TODO: remove this limitation - -void tmgr_tick(void); // add this function to SysTick timer / another simple timer / just in forever loop :) - -void sleep_ticks(unsigned int ticks); // sleep for n timer ticks - -unsigned int tmgr_get_uptime(); -/** @} - */ - - -#endif // TASKS_H diff --git a/include/lib/tmgr.h b/include/lib/tmgr.h new file mode 100644 index 0000000..f636180 --- /dev/null +++ b/include/lib/tmgr.h @@ -0,0 +1,28 @@ +#ifndef INCLUDE_LIB_TMGR_H +#define INCLUDE_LIB_TMGR_H + +#ifndef NULL + #define NULL 0 +#endif + +/** + * TODO: insert platform-dependent includes + */ + +typedef int32_t uptime_t; +typedef int32_t delay_t; + +typedef struct { + uptime_t time; + void *func(void); + task_t * next; +} task_t; + +void tmgr_tick(void); +void tmgr_timer(void); +void tmgr_register(task_t * task); + +uptime_t tmgr_get_uptime(void); +void tmgr_delay(uptime_t time); + +#endif diff --git a/src/lib/ulc/tmgr.c b/src/lib/ulc/tmgr.c index 67074ce..9b47e2a 100644 --- a/src/lib/ulc/tmgr.c +++ b/src/lib/ulc/tmgr.c @@ -1,123 +1,80 @@ -#include +#include +static uptime_t sys_uptime = 0; -static volatile unsigned int uptime = 0; -static volatile unsigned int rate = 0; -static int num_handlers = 0; -static handler_t * first = 0; +static task_t *fast_queue = NULL; +static task_t *main_queue = NULL; -void tmgr_msleep(unsigned int time) -{ - unsigned int end = uptime + time; - while ( uptime < end );; -} +/** + * @section Task Manager core + * @{ + */ -void tmgr_set_rate(unsigned int nrate) +void tmgr_timer(void) { - rate = nrate; - /* TODO: Reschedule pending tasks based on new rate */ + sys_uptime++; } - -unsigned int tmgr_get_uptime() +// TODO: make it atomic +void tmgr_register(task_t * task) { - return uptime; + task->next = fast_queue; + fast_queue = task; } -int tmgr_register(handler_t * data) +void tmgr_tick(void) { - // 0. The most simplest case - too late to run handler - if(data->uptime < uptime) return TIME_OVER; - // 1. The simplest case - no handlers. So, let's add the first one - if(num_handlers == 0) - { - first = data; - } - // 2. The second case - there is only 1 handler. - else if(num_handlers == 1) - { - #ifdef REM_HANDLER_IF_DEFINED - if(first->handler == data->handler) // if this handler has been registered before - { - first->uptime = data->uptime; - return; - } - #endif - if(first->uptime > data->uptime) first->next = data; - else - { - data->next = first; - first = data; - } - } - // 3. There are many handlers - run sorting algorythm - else + // 1. Check fast queue + if(fast_queue != NULL) { - int i = num_handlers; - handler_t * current = first; - handler_t * prev = 0; + // Attach fast queue to main queue + task_t * fast = fast_queue, + * current = main_queue, + ** last = &main_queue; - while(i != 0) + fast_queue = NULL; + + while(fast->next != NULL) { - #ifdef REM_HANDLER_IF_DEFINED - if(current->handler == data->handler) // remove this handler if registered - { - if(prev == 0) first = first->next; - else prev->next = current->next; - num_handlers--; - } - #endif - if(current->uptime <= data->uptime) + while(current != NULL && current->time < fast->time) { - data->next = current; - if(prev == 0) first = data; - else prev->next = data; - i++; - break; + current = current->next; + last = &(last->next); } - prev = current; - current = current->next; - i--; + + fast->next = current; + *last = fast; + + fast = fast->next; } - if(i==0) prev->next = data; - #ifdef REM_HANDLER_IF_DEFINED - else + } + + // 2. Run main queue + if(main_queue != NULL) + { + while(main_queue != NULL && main_queue->time <= sys_uptime) { - while(i != 0) - { - if(current->handler == data->handler) - { - prev->next = current->next; - num_handlers--; - break; - } - prev = current; - current = current->next; - i--; - } + main_queue->func(); + main_queue = main_queue->next; } - #endif } +} - num_handlers++; - return 0; +/** + * @} + * + * @section Task Manager Uptime functions + * @{ + */ + +uptime_t tmgr_get_uptime(void) +{ + return sys_uptime; } -void tmgr_tick(void) +void tmgr_delay(uptime_t time) { - int i = num_handlers; - handler_t * current = first; - while(i != 0) - { - if(current->uptime == uptime) - { - current->handler(); - } - else if(current->uptime < uptime) break; + time += sys_uptime; - current = current->next; - i--; - } - num_handlers -= i; - uptime++; + while(time > sys_uptime); } From ae06f4fc89dbc327988080d7d282575189532b21 Mon Sep 17 00:00:00 2001 From: Maslov Nikita Date: Sat, 28 Sep 2013 22:01:46 +0400 Subject: [PATCH 2/9] Fix of wrong fast_queue pointer Signed-off-by: Maslov Nikita --- include/lib/tmgr.h | 18 ++++++++++--- src/lib/ulc/tmgr.c | 66 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 73 insertions(+), 11 deletions(-) diff --git a/include/lib/tmgr.h b/include/lib/tmgr.h index f636180..f14bd6e 100644 --- a/include/lib/tmgr.h +++ b/include/lib/tmgr.h @@ -1,6 +1,11 @@ #ifndef INCLUDE_LIB_TMGR_H #define INCLUDE_LIB_TMGR_H +#ifdef CONFIG_ARCH_NATIVE + #include + #include +#endif + #ifndef NULL #define NULL 0 #endif @@ -9,13 +14,15 @@ * TODO: insert platform-dependent includes */ +/** + * TODO: uptime_t should be platform-dependent (or application-dependent) + */ typedef int32_t uptime_t; -typedef int32_t delay_t; -typedef struct { +typedef struct struct_task_t{ uptime_t time; - void *func(void); - task_t * next; + void (*func)(void); + struct struct_task_t * next; } task_t; void tmgr_tick(void); @@ -25,4 +32,7 @@ void tmgr_register(task_t * task); uptime_t tmgr_get_uptime(void); void tmgr_delay(uptime_t time); +void tmgr_set_fq(int32_t fq); +int32_t tmgr_get_fq(void); + #endif diff --git a/src/lib/ulc/tmgr.c b/src/lib/ulc/tmgr.c index 9b47e2a..533d3d3 100644 --- a/src/lib/ulc/tmgr.c +++ b/src/lib/ulc/tmgr.c @@ -1,6 +1,7 @@ #include -static uptime_t sys_uptime = 0; +static volatile uptime_t sys_uptime = 0; +static volatile int32_t sys_fq = 0; static task_t *fast_queue = NULL; static task_t *main_queue = NULL; @@ -28,18 +29,18 @@ void tmgr_tick(void) if(fast_queue != NULL) { // Attach fast queue to main queue - task_t * fast = fast_queue, - * current = main_queue, - ** last = &main_queue; + task_t *fast = fast_queue, + *current = main_queue, + **last = &main_queue; fast_queue = NULL; - while(fast->next != NULL) + while(fast != NULL) { while(current != NULL && current->time < fast->time) { current = current->next; - last = &(last->next); + last = &((*last)->next); } fast->next = current; @@ -72,9 +73,60 @@ uptime_t tmgr_get_uptime(void) return sys_uptime; } -void tmgr_delay(uptime_t time) +void tmgr_delay_ticks(uptime_t time) { time += sys_uptime; while(time > sys_uptime); } + +/** + * @} + */ + +/** + * @section Time conversion functions + * @{ + */ +void tmgr_set_fq(int32_t fq) +{ + sys_fq = fq; +} + +int32_t tmgr_get_fq(void) +{ + return sys_fq; +} + +int32_t tmgr_ticks_to_us(int32_t ticks) +{ + return 1000000U * ticks / sys_fq; +} + +int32_t tmgr_ticks_to_ms(int32_t ticks) +{ + return 1000 * ticks / sys_fq; +} + +int32_t tmgr_ticks_to_s(int32_t ticks) +{ + return ticks / sys_fq; +} + +int32_t tmgr_us_to_ticks(int32_t us) +{ + return us * sys_fq / 1000000U; +} + +int32_t tmgr_ms_to_ticks(int32_t ms) +{ + return ms * sys_fq / 1000; +} + +int32_t tmgr_s_to_ticks(int32_t s) +{ + return s * sys_fq; +} +/** + * @} + */ From baa298a95958cda3e40e46be33d9665d455a8b72 Mon Sep 17 00:00:00 2001 From: Nikita 'webconn' Maslov Date: Sat, 19 Oct 2013 07:18:35 +0400 Subject: [PATCH 3/9] Add cross-platform include/lib/inttypes.h Signed-off-by: Nikita 'webconn' Maslov --- include/lib/inttypes.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 include/lib/inttypes.h diff --git a/include/lib/inttypes.h b/include/lib/inttypes.h new file mode 100644 index 0000000..01e8203 --- /dev/null +++ b/include/lib/inttypes.h @@ -0,0 +1,22 @@ +#ifndef _ANTARES_LIB_INTTYPES_H +#define _ANTARES_LIB_INTTYPES_H + +/** + * Platform-dependent inttypes.h + * This file collects data about integer sizes + * of current platform by including platform-dependent files + * + * TODO: fill this file with extra includes + */ + +#if defined(CONFIG_ARCH_AVR) + #include +#elif defined(CONFIG_ARCH_ARM) + #if defined(CONFIG_STM32F1X) + #include + #endif +#elif defined(CONFIG_ARCH_NATIVE) + #include +#endif + +#endif From 174d15e83be4df8871baa577add68f922385d027 Mon Sep 17 00:00:00 2001 From: Nikita 'webconn' Maslov Date: Sat, 19 Oct 2013 07:19:18 +0400 Subject: [PATCH 4/9] Rework tmgr library (issue #16) NEED TESTING Features of reworked tmgr library: * Cross-platform, tiny task manager/scheduler * Two modes of operation: uptime-based (faster, but limited in device uptime) and countdown-based (a bit slower, but uptime-independent) * In uptime-based mode, the information about system uptime is available and could be used in projects (for debugging, logging etc.) * Time conversion functions (s/ms/us to timer ticks and vice versa) * Basic delay implementation Signed-off-by: Nikita 'webconn' Maslov --- include/lib/tmgr.h | 54 +++++++++++++++++++------------- src/lib/kcnf | 27 ++++++++++++++-- src/lib/ulc/kcnf | 28 +++++++++++++++++ src/lib/ulc/tmgr.c | 77 +++++++++++++++++++++++++++++++++++----------- 4 files changed, 144 insertions(+), 42 deletions(-) create mode 100644 src/lib/ulc/kcnf diff --git a/include/lib/tmgr.h b/include/lib/tmgr.h index f14bd6e..2f36e2f 100644 --- a/include/lib/tmgr.h +++ b/include/lib/tmgr.h @@ -1,38 +1,48 @@ #ifndef INCLUDE_LIB_TMGR_H #define INCLUDE_LIB_TMGR_H -#ifdef CONFIG_ARCH_NATIVE - #include - #include -#endif +#include "inttypes.h" #ifndef NULL #define NULL 0 #endif -/** - * TODO: insert platform-dependent includes - */ - -/** - * TODO: uptime_t should be platform-dependent (or application-dependent) - */ -typedef int32_t uptime_t; +#if defined(CONFIG_LIB_TMGR_TIME_16) +typedef int16_t tmgr_time_t; +#elif defined(CONFIG_LIB_TMGR_TIME_32) +typedef int32_t tmgr_time_t; +#elif defined(CONFIG_LIB_TMGR_TIME_64) +typedef int64_t tmgr_time_t; +#else + // TODO: format errror message + #error "Wrong tmgr_time_t size (must be 16, 32 or 64 bits)" +#endif -typedef struct struct_task_t{ - uptime_t time; +typedef struct struct_tmgr_task_t{ + tmgr_time_t time; void (*func)(void); - struct struct_task_t * next; -} task_t; + struct struct_tmgr_task_t * next; +} tmgr_task_t; void tmgr_tick(void); void tmgr_timer(void); -void tmgr_register(task_t * task); +void tmgr_register(tmgr_task_t * task, void (*function)(void), tmgr_time_t delay); -uptime_t tmgr_get_uptime(void); -void tmgr_delay(uptime_t time); - -void tmgr_set_fq(int32_t fq); -int32_t tmgr_get_fq(void); +#ifdef CONFIG_LIB_TMGR_UPTIME +tmgr_time_t tmgr_get_uptime(void); +#endif +void tmgr_delay(tmgr_time_t time); + +void tmgr_set_fq(tmgr_time_t fq); +tmgr_time_t tmgr_get_fq(void); + +#ifdef CONFIG_LIB_TMGR_EXTRA +tmgr_time_t tmgr_ticks_to_us(tmgr_time_t ticks); +tmgr_time_t tmgr_ticks_to_ms(tmgr_time_t ticks); +tmgr_time_t tmgr_ticks_to_s(tmgr_time_t ticks); +tmgr_time_t tmgr_us_to_ticks(tmgr_time_t us); +tmgr_time_t tmgr_ms_to_ticks(tmgr_time_t ms); +tmgr_time_t tmgr_s_to_ticks(tmgr_time_t s); +#endif #endif diff --git a/src/lib/kcnf b/src/lib/kcnf index 4ee5168..b121490 100644 --- a/src/lib/kcnf +++ b/src/lib/kcnf @@ -27,8 +27,31 @@ if LIB_ANTARES_CORE help Enables initcall debugging to system console - config LIB_TMGR - bool "Simple cron and uptime counter [NEEDS REWORK]" + menuconfig LIB_PRINTK + bool "printk and dbg() macro support [NEEDS REWORK]" + + if LIB_PRINTK + + config LIB_PRINTK_TIMESTAMP + depends on LIB_TMGR_UPTIME && LIB_PRINTK + bool "Add an uptime timestamp to printks" + + config LIB_PRINTK_PREFIX + depends on LIB_PRINTK + bool "Add a prefix to all printks" + + config LIB_PRINTK_PREFIX_V + depends on LIB_PRINTK_PREFIX + string "Prefix" + + endif + + menuconfig LIB_TMGR + bool "Simple cron and uptime counter [NEEDS TESTING]" + + if LIB_TMGR + source "antares/src/lib/ulc/kcnf" + endif endif diff --git a/src/lib/ulc/kcnf b/src/lib/ulc/kcnf new file mode 100644 index 0000000..33c9b05 --- /dev/null +++ b/src/lib/ulc/kcnf @@ -0,0 +1,28 @@ +choice + prompt "Cron method" + + config LIB_TMGR_UPTIME + bool "Uptime" + + config LIB_TMGR_COUNTDOWN + bool "Countdown" + +endchoice + +choice + prompt "Time variable size" + + config LIB_TMGR_TIME_16 + bool "16 bit" + + config LIB_TMGR_TIME_32 + bool "32 bit" + + config LIB_TMGR_TIME_64 + bool "64 bit" + +endchoice + +config LIB_TMGR_EXTRA +bool "Build extra time conversion functions" +default "y" diff --git a/src/lib/ulc/tmgr.c b/src/lib/ulc/tmgr.c index 533d3d3..394e161 100644 --- a/src/lib/ulc/tmgr.c +++ b/src/lib/ulc/tmgr.c @@ -1,10 +1,14 @@ #include -static volatile uptime_t sys_uptime = 0; -static volatile int32_t sys_fq = 0; +static volatile tmgr_time_t sys_uptime = 0; +static volatile int32_t sys_fq = 1; -static task_t *fast_queue = NULL; -static task_t *main_queue = NULL; +static tmgr_task_t *fast_queue = NULL; +static tmgr_task_t *main_queue = NULL; + +#ifdef CONFIG_LIB_TMGR_COUNTDOWN +static tmgr_time_t delay; +#endif /** * @section Task Manager core @@ -13,14 +17,35 @@ static task_t *main_queue = NULL; void tmgr_timer(void) { +#if defined(CONFIG_LIB_TMGR_UPTIME) sys_uptime++; +#elif defined(CONFIG_LIB_TMGR_COUNTDOWN) + // Implement delay feature + if(delay > 0) + delay--; + + tmgr_task_t * queue = main_queue; + while(queue != NULL) + { + queue->time --; + queue = queue->next; + } +#endif } // TODO: make it atomic -void tmgr_register(task_t * task) +void tmgr_register(tmgr_task_t * task, void (*function)(void), tmgr_time_t delay) { +#if defined(CONFIG_LIB_TMGR_UPTIME) + task->time = sys_uptime + delay; +#elif defined(CONFIG_LIB_TMGR_COUNTDOWN) + task->time = delay; +#endif + task->func = function; + // these lines MUST be atomic { task->next = fast_queue; fast_queue = task; + // } } void tmgr_tick(void) @@ -29,7 +54,8 @@ void tmgr_tick(void) if(fast_queue != NULL) { // Attach fast queue to main queue - task_t *fast = fast_queue, + // TODO: from this moment to `fast_queue = NULL` lines must be atomic! + tmgr_task_t *fast = fast_queue, *current = main_queue, **last = &main_queue; @@ -37,7 +63,11 @@ void tmgr_tick(void) while(fast != NULL) { + #if defined(CONFIG_LIB_TMGR_UPTIME) while(current != NULL && current->time < fast->time) + #elif defined(CONFIG_LIB_TMGR_COUNTDOWN) + while(current != NULL && current->time > fast->time) + #endif { current = current->next; last = &((*last)->next); @@ -53,7 +83,11 @@ void tmgr_tick(void) // 2. Run main queue if(main_queue != NULL) { - while(main_queue != NULL && main_queue->time <= sys_uptime) + #if defined(CONFIG_LIB_TMGR_UPTIME) + while(main_queue != NULL && main_queue->time <= sys_uptime) + #elif defined(CONFIG_LIB_TMGR_COUNTDOWN) + while(main_queue != NULL && main_queue->time <= 0) + #endif { main_queue->func(); main_queue = main_queue->next; @@ -68,65 +102,72 @@ void tmgr_tick(void) * @{ */ -uptime_t tmgr_get_uptime(void) +tmgr_time_t tmgr_get_uptime(void) { return sys_uptime; } -void tmgr_delay_ticks(uptime_t time) +void tmgr_delay(tmgr_time_t time) { +#if defined(CONFIG_LIB_TMGR_UPTIME) time += sys_uptime; - while(time > sys_uptime); +#elif defined(CONFIG_LIB_TMGR_COUNTDOWN) + delay = time; + while(delay > 0); +#endif } /** * @} */ +#ifdef CONFIG_LIB_TMGR_EXTRA /** * @section Time conversion functions * @{ */ -void tmgr_set_fq(int32_t fq) +void tmgr_set_fq(tmgr_time_t fq) { sys_fq = fq; } -int32_t tmgr_get_fq(void) +tmgr_time_t tmgr_get_fq(void) { return sys_fq; } -int32_t tmgr_ticks_to_us(int32_t ticks) +tmgr_time_t tmgr_ticks_to_us(tmgr_time_t ticks) { return 1000000U * ticks / sys_fq; } -int32_t tmgr_ticks_to_ms(int32_t ticks) +tmgr_time_t tmgr_ticks_to_ms(tmgr_time_t ticks) { return 1000 * ticks / sys_fq; } -int32_t tmgr_ticks_to_s(int32_t ticks) +tmgr_time_t tmgr_ticks_to_s(tmgr_time_t ticks) { return ticks / sys_fq; } -int32_t tmgr_us_to_ticks(int32_t us) +tmgr_time_t tmgr_us_to_ticks(tmgr_time_t us) { return us * sys_fq / 1000000U; } -int32_t tmgr_ms_to_ticks(int32_t ms) +tmgr_time_t tmgr_ms_to_ticks(tmgr_time_t ms) { return ms * sys_fq / 1000; } -int32_t tmgr_s_to_ticks(int32_t s) +tmgr_time_t tmgr_s_to_ticks(tmgr_time_t s) { return s * sys_fq; } + +#endif /** * @} */ From 0d7cdb92d636fab22ec2e4e23a9dc51b65543d97 Mon Sep 17 00:00:00 2001 From: Nikita 'webconn' Maslov Date: Thu, 31 Oct 2013 22:51:02 +0400 Subject: [PATCH 5/9] tmgr: Add docs and fix coding style issues Signed-off-by: Nikita 'webconn' Maslov --- doc/en/tmgr.txt | 192 ++++++++++++++++++++++++++++++++++++++ doc/ru/tmgr.txt | 223 +++++++++++++++++++++++++++++++++++++++++++++ include/lib/tmgr.h | 33 +++---- src/lib/ulc/kcnf | 6 +- src/lib/ulc/tmgr.c | 164 +++++++++++++-------------------- 5 files changed, 495 insertions(+), 123 deletions(-) create mode 100644 doc/en/tmgr.txt create mode 100644 doc/ru/tmgr.txt diff --git a/doc/en/tmgr.txt b/doc/en/tmgr.txt new file mode 100644 index 0000000..36f63a8 --- /dev/null +++ b/doc/en/tmgr.txt @@ -0,0 +1,192 @@ +tmgr: Simple task manager and scheduler +--------------------------------------- + +Description +----------- + +Task scheduler should be easily used if your program could be logically divided +into different threads or delayed function calls. In this case scheduler allows +you to work with tasks in the easiest way. + +Features +-------- + +* Easy in understanding and usage + +* Cross-platform (written fully in C without assembler magic) + +* Easy to adjust (for example, set required size of time typedef) + + +How to use? +----------- + +1. Initialization +----------------- + +Operation of scheduler consists of two periodic function calls: +tmgr_tick() and tmgr_timer(). + +tmgr_timer() function should be called in system timer interrupt hander which +counts time quantums. Optimal time quantum size is 1 ms (author's opinion). +In this case, frequency of timer interrupt handler calls is 1 kHz. +tmgr_timer() function just counts system time, so it is fast and safe for +interrupt context. + +tmgr_tick() function should be called in main program loop. In this function +context all user's tasks will be executed, so it's not recommended to call this +function in interrupt handles. + +Also you need to set system timer frequency (for time conversion functions) using +tmgr_set_fq() call like this: + + tmgr_set_fq(1000); + +It means than system timer runs at 1 kHz frequency (interrupt handler called +1000 times in second). Initialization of system timer depends on current +platform, so tmgr library will _not_ initialize it. It is an user's problem. + +tmgr_set_fq() receives as an argument one value of tmgr_time_t type (signed integer, +size of this type should be set in menuconfig by user). + +A sample program that correctly runs scheduler base: + +#include + +void timer_interrupt() +{ + /* This function means an abstract timer interrupt handler */ + tmgr_timer(); +} + +int main(void) +{ + /* Platform-dependent initialization of system timer */ + timer_init(1000); + /* Timer runs at 1000 Hz (time quantum is 1 ms) */ + /* Giving this value to library */ + tmgr_set_fq(1000); + + /* Run tick function in infinite loop */ + while (1) + tmgr_tick(); + + return 0; +} + +2. How to use scheduler +----------------------- + +It's all quietly simple. Scheduler need to know what function do you want to +run (prototype of task function: void task(void);) and the value of delay. Also +it needs a piece of memory to save the task description, so one variable +of tmgr_task_t type in _static_ memory is required. This type is a structure, +but you shouldn't change any value in it manually. + +To add your task in tmgr queue, just call: + + tmgr_register(&my_task, my_func_name, delay); + +my_task is a static memory variable of tmgr_task_t type, my_func_name is +the name of function you want to run, delay (obviously) is a delay value +(in system timer's ticks). + +In real application, you should want to calculate the delay in s, ms etc. +To convert value from "natural" units to ticks, there are few macroses: + + tmgr_us_to_ticks(us) + tmgr_ms_to_ticks(ms) + tmgr_s_to_ticks(s) + +There are also macroses for convertions from ticks to natural units: + + tmgr_ticks_to_us(ticks) + tmgr_ticks_to_ms(ticks) + tmgr_ticks_to_s(ticks) + +Time values (ticks or natural) are stored in tmgr_time_t variables. + +So, this is correct way to run my_func with 500 ms delay: + + static tmgr_task_t my_task; + tmgr_register(&my_task, my_func, tmgr_ms_to_ticks(500)); + + +3. Modes of operation +--------------------- + +There are two modes of operation: uptime counter and countdown mode. + +3.1. Uptime counter +------------------- + +In this mode the base of time counting is system uptime value. You can get +the current value of system uptime by calling + + tmgr_get_uptime(). + +Notice. This function is available only in uptime counter mode. + +This function returns the value of tmgr_time_t type (signed integer). + +This mode of operation is a little faster that countdown (because in the +timer interrupt handler only one action is performed - increment of uptime +value and in main loop, task runs if its "launch time" is equal to system +uptime), but it has an obvious limitation related with uptime variable +overflow. + +Uptime is stored in tmgr_time_t variable. By adjusting its size (tmgr_time_t), +you can move the uptime limits. Available sizes are 16, 32 and 64 bits. + +The maximal time of stable operation in uptime counter move vs. tmgr_time_t size +(system timers frequency = 1000 Hz): + + 16 bits - 32.767 s + 32 bits - 2147483.65 s ~ 596 h ~ 25 days + 64 bits - 10^11 days + +3.2. Countdown mode +------------------- + +This mode is a little slower that uptime counter, because in system timer +interrupt handler tmgr_timer() decrements values of each task's timer +separately (and it need to perform N operations if there are N tasks +in queue), but the limitation of system uptime is removed. + +In this mode you also can set tmgr_time_t size as you want. + +3.3. Summary +------------ + +If your arch can quickly perform operations with large integers or the +real uptime of your device will not exceed the stability threshold, we +recommend you to use uptime counter mode because it is faster and has +more features (uptime value is useful for debugging, for example). +The usage of countdown mode is justified if you will not run large +number of tasks or if you create a device that will run without +time limits. + +Notice. The usage of tmgr library is equal in both modes (excepts +tmgr_get_uptime() that is only first mode's feature), so you can change +the mode of operation if you need almost without any change of source code. + + +4. Extra features +----------------- + +tmgr_delay(delay) - performs a simple delay (waiting in loop). To set delay +in natural units, use time convertion macroses. + + tmgr_delay(tmgr_ms_to_ticks(500)); + +means 500 ms delay. + +Function receives one tmgr_time_t value. + +Notice. We DO NOT RECOMMEND to use this function, especially in task +context, because it can break down the tasks queue (tasks could not +run when delay is performed). Try to split your function into several +subtasks. + +tmgr_get_fq() function just returns the value of system timer +frequency which was set before. (in tmgr_time_t type) diff --git a/doc/ru/tmgr.txt b/doc/ru/tmgr.txt new file mode 100644 index 0000000..d6a1f60 --- /dev/null +++ b/doc/ru/tmgr.txt @@ -0,0 +1,223 @@ +tmgr: Простейший планировщик задач +---------------------------------- + +Описание +-------- + +Планировщик задач удобно использовать в том случае, если ваша программа логично +разбивается нa несколько отдельных потоков или циклично выполняемых блоков. +В таком случае планировщик предоставляет возможность сделать работу с потоками +максимально прозрачной. + +Особенности +----------- + +* Максимальная простота обращения с точки зрения пользователя + +* Кроссплатформенность (реализация полностью на Си, без ассемблера) + +* Конфигурируемость (например, размер переменной для хранения времени + устанавливается пользователем) + +Как использовать? +----------------- + +1. Инициализация +---------------- + +Работа планировщика организуется циклическими вызовами двух функций: +tmgr_tick() и tmgr_timer(). + +Функцию tmgr_timer() следует вызывать в обработчике прерывания таймера, который +отсчитывает кванты времени планировщика. Оптимальным размером кванта времени +автор считает 1 мс (соответственно, частота вызова функции tmgr_timer() будет +1 кГц). Эта функция обеспечивает только отсчёт времени (в зависимости от режима +работы планировщика) и минимально загружает систему в контексте прерывания. + +Функцию tmgr_tick() следует вызывать в основном цикле программы. В рамках вызова +этой функции будет происходить выполение задач пользователя, так что её вызов +в контексте прерывания не рекомендуется. + +Для функции конвертирования времени требуется передать библиотеке значение частоты +системного таймера (в Гц). Это делается вызовом функции tmgr_set_fq(). Например, + + tmgr_set_fq(1000); + +означает, что системный таймер будет вызывать прерывание с частотой 1 кГц. +Замечание: Планировщик _не инициализирует_ таймер. Инициализация системного +таймера и организация вызовов функций планировщика лежит на пользователе. + +Функция tmgr_set_fq() принимает в качестве аргумента одно значение типа tmgr_time_t +(целый знаковый, размер которого можно установить в меню конфигурации menuconig). + +Пример программы, обеспечивающей корректную работу таймера: + +#include + +void timer_interrupt() +{ + /* Прерывание системного таймера (абстрактно) */ + tmgr_timer(); +} + +int main(void) +{ + /* Инициализация системного таймера, платформо-зависима */ + timer_init(1000); + /* Пусть таймер работает на частоте 1 кГц (квант времени 1 мс) */ + /* Передаём это значение планировщику */ + tmgr_set_fq(1000); + + /* Вызов основной функции планировщика в бесконечном цикле */ + while (1) + tmgr_tick(); + + return 0; +} + +2. Работа с планировщиком +------------------------- + +Всё максимально просто. Планировщику требуется знать, какую функцию вы хотите +запустить (функция не должна ничего принимать и возвращать: void foo(void);) и +c какой задержкой. От вас также потребуется одна переменная типа tmgr_task_t +(на самом деле, это структура, но вся работа с ней происходит внутри библиотеки +и вам не нужно обращаться к её элементам) в _статической_ памяти. + +Для того, чтобы поместить задачу в очередь планировщика, в нужном месте кода +сделайте вызов: + + tmgr_register(&my_task, my_func_name, delay); + +Здесь my_task - это переменная типа tmgr_task_t (в статической памяти), +my_func_name - имя функции, которую потребуется выполнить, delay - задержка +запуска. + +В общем случае задержка запуска считается в тиках системного таймера. Для того, +чтобы выразить задержку в нормальных единицах времени, следует использовать +макросы конвертирования времени планировщика: + + tmgr_us_to_ticks(us) + tmgr_ms_to_ticks(ms) + tmgr_s_to_ticks(s) + +Существуют также и макросы обратного конвертирования: + + tmgr_ticks_to_us(ticks) + tmgr_ticks_to_ms(ticks) + tmgr_ticks_to_s(ticks) + +Время (число тиков, а также нормальных единиц) определяется типом данных +tmgr_time_t (целый знаковый, размер устанавливается пользователем в меню +конфигурации menuconfig). Таким образом, все функции конвертирования времени +принимают и возвращают tmgr_time_t. + +Таким образом, вызов функции my_func с задержкой 500 мс будет выглядеть так: + + static tmgr_task_t my_task; + tmgr_register(&my_task, my_func, tmgr_ms_to_ticks(500)); + + +3. Режимы работы планировщика +----------------------------- + +Планировщик имеет два режима работы: прямого и обратного отсчёта времени +(соответственно, uptime counter и countdown) + +3.1. Прямой отсчёт времени (uptime) +----------------------------------- + +В этом режиме работы основанием отсчёта времени является значение "времени +работы" (uptime) программы. При этом пользователь может в любой момент узнать +текущее значение uptime, вызвав функцию + + tmgr_get_uptime(). + +Замечание. Функция tmgr_get_update() доступна только в этом режиме работы. + +Функция возвращает тип tmgr_time_t (целочисленный знаковый, размер которого +устанавливается пользователем в меню конфигурации menuconfig). + +Такой метод работы быстрее (в прерывании происходит только инкрементирование +значения переменной uptime, в основном цикле программы вызов задачи происходит +при совпадении текущего значения uptime с подсчитанным при регистрации). Однако, +он имеет очевидное ограничение на общее время работы устройства (возможность +переполнения переменной, считающий uptime). + +Отсчитанное время хранится в переменной типа tmgr_time_t. Это целый знаковый тип, +размер которого можно установить в меню конфигурации menuconfig. +Возможно использование 16, 32 и 64-битной переменной. + +Предельное время стабильной работы в uptime-режиме в зависимости от размера +переменной (частота системного таймера - 1 кГц): + + 16 бит - 32.767 секунд. + 32 бит - 2147483.65 с ~ 596 ч ~ 25 суток + 64 бит - 10^11 суток + +3.2. Обратный отсчёт времени (countdown) +---------------------------------------- + +Этот режим работы немного медленней, чем uptime, так как суть его работы +заключается в декрементировании значений таймера каждой задачи в отдельности, +зато отсутствуют ограничения на время работы. + +В этом режиме также нужно установить размер переменной времени (по вашему +усмотрению). + +3.3. Вывод +---------- + +Если целевая архитектура позволяет достаточно быстро работать с 64-битными +переменными, или время непрерывной работы вашего устройства не превысит 25 +суток, то есть смысл использовать первый режим работы как более быстрый и +предоставляющий больше возможностей. Если же устройство должно проработать +непрерывно достаточно долго, но нет возможности использовать 64-битные +переменные, то можно выбрать режим countdown (правда, при увеличении количества +задач в очереди время работы в прерывании системного таймера будет линейно расти, +так что стоит серьёзно подумать о выборе режима). + +Замечание. Общий принцип работы с планировщиком одинаков в обоих режимах. +(Единственное отличие - отсутствие функции tmgr_get_uptime() во втором режиме). +Таким образом, в большинстве случаев можно практически без изменений в коде +программы изменить способ функционирования планировщика. + + +4. Описание параметров планировщика (menuconfig) +------------------------------------------------ + +Параметров всего два. + +4.1. Mode of operation +---------------------- +Режим работы таймера. Выбирается из двух (Uptime и Countdown), описание выше. + +4.2. Time variable size +----------------------- +Размер переменной для хранения времени. Описание выше (Режимы работы). + + +5. Дополнительные возможности +----------------------------- + +У планировщика есть функция tmgr_delay(delay). Она обеспечивает простую +задержку работы программы на нужный промежуток времени. В качестве единственного +аргумента функция принимает число тиков системного таймера, которые нужно +прождать. (С помощью функций конвертирования времени можно записывать время +задержки в стандартных единицах). Например, вызов + + tmgr_delay(tmgr_ms_to_ticks(500)); + +обеспечит остановку работы программы на 500 мс. + +Принимаемый параметр имеет тип tmgr_time_t, неоднократно описанный выше. + +Замечание. Использование этой функции с планировщиком НЕ РЕКОМЕНДУЕТСЯ. +Особенно в рамках вызываемых задач. Всё дело в том, что во время задержки +прерывается нормальное выполнение задач в планировщике (данный способ +задержки просто в цикле ждёт нужный момент времени, "подвешивая" +процессор.) + +Функцию tmgr_get_fq() можно использовать для того, чтобы в любой момент +времени получить установленное ранее значение частоты системного таймера. +Возвращаемый тип - tmgr_time_t. diff --git a/include/lib/tmgr.h b/include/lib/tmgr.h index 2f36e2f..39b05e2 100644 --- a/include/lib/tmgr.h +++ b/include/lib/tmgr.h @@ -2,10 +2,8 @@ #define INCLUDE_LIB_TMGR_H #include "inttypes.h" - -#ifndef NULL - #define NULL 0 -#endif +#include +#include #if defined(CONFIG_LIB_TMGR_TIME_16) typedef int16_t tmgr_time_t; @@ -14,14 +12,20 @@ typedef int32_t tmgr_time_t; #elif defined(CONFIG_LIB_TMGR_TIME_64) typedef int64_t tmgr_time_t; #else - // TODO: format errror message - #error "Wrong tmgr_time_t size (must be 16, 32 or 64 bits)" +#error "tmgr: Wrong tmgr_time_t size (must be 16, 32 or 64 bits)" #endif -typedef struct struct_tmgr_task_t{ - tmgr_time_t time; - void (*func)(void); - struct struct_tmgr_task_t * next; +#define tmgr_ticks_to_us(ticks) (1000000U * (ticks) / tmgr_get_fq()) +#define tmgr_ticks_to_ms(ticks) (1000 * (ticks) / tmgr_get_fq()) +#define tmgr_ticks_to_s(ticks) ((ticks) / tmgr_get_fq()) +#define tmgr_us_to_ticks(us) ((us) * tmgr_get_fq() / 1000000U) +#define tmgr_ms_to_ticks(ms) ((ms) * tmgr_get_fq() / 1000) +#define tmgr_s_to_ticks(s) ((s) * tmgr_get_fq()) + +typedef struct struct_tmgr_task_t { + tmgr_time_t time; + void (*func)(void); + struct struct_tmgr_task_t * next; } tmgr_task_t; void tmgr_tick(void); @@ -36,13 +40,4 @@ void tmgr_delay(tmgr_time_t time); void tmgr_set_fq(tmgr_time_t fq); tmgr_time_t tmgr_get_fq(void); -#ifdef CONFIG_LIB_TMGR_EXTRA -tmgr_time_t tmgr_ticks_to_us(tmgr_time_t ticks); -tmgr_time_t tmgr_ticks_to_ms(tmgr_time_t ticks); -tmgr_time_t tmgr_ticks_to_s(tmgr_time_t ticks); -tmgr_time_t tmgr_us_to_ticks(tmgr_time_t us); -tmgr_time_t tmgr_ms_to_ticks(tmgr_time_t ms); -tmgr_time_t tmgr_s_to_ticks(tmgr_time_t s); -#endif - #endif diff --git a/src/lib/ulc/kcnf b/src/lib/ulc/kcnf index 33c9b05..5797984 100644 --- a/src/lib/ulc/kcnf +++ b/src/lib/ulc/kcnf @@ -1,5 +1,5 @@ choice - prompt "Cron method" + prompt "Mode of operation" config LIB_TMGR_UPTIME bool "Uptime" @@ -22,7 +22,3 @@ choice bool "64 bit" endchoice - -config LIB_TMGR_EXTRA -bool "Build extra time conversion functions" -default "y" diff --git a/src/lib/ulc/tmgr.c b/src/lib/ulc/tmgr.c index 394e161..a50a07e 100644 --- a/src/lib/ulc/tmgr.c +++ b/src/lib/ulc/tmgr.c @@ -18,81 +18,79 @@ static tmgr_time_t delay; void tmgr_timer(void) { #if defined(CONFIG_LIB_TMGR_UPTIME) - sys_uptime++; + sys_uptime++; #elif defined(CONFIG_LIB_TMGR_COUNTDOWN) - // Implement delay feature - if(delay > 0) - delay--; - - tmgr_task_t * queue = main_queue; - while(queue != NULL) - { - queue->time --; - queue = queue->next; - } + /* Implement delay feature */ + if (delay > 0) + delay--; + + tmgr_task_t * queue = main_queue; + while (queue != NULL) { + queue->time --; + queue = queue->next; + } #endif } -// TODO: make it atomic void tmgr_register(tmgr_task_t * task, void (*function)(void), tmgr_time_t delay) { #if defined(CONFIG_LIB_TMGR_UPTIME) - task->time = sys_uptime + delay; + task->time = sys_uptime + delay; #elif defined(CONFIG_LIB_TMGR_COUNTDOWN) - task->time = delay; + task->time = delay; #endif - task->func = function; - // these lines MUST be atomic { - task->next = fast_queue; - fast_queue = task; - // } + task->func = function; + + /* these lines are atomic */ + ANTARES_DISABLE_IRQS(); + task->next = fast_queue; + fast_queue = task; + ANTARES_ENABLE_IRQS(); } void tmgr_tick(void) { - // 1. Check fast queue - if(fast_queue != NULL) - { - // Attach fast queue to main queue - // TODO: from this moment to `fast_queue = NULL` lines must be atomic! - tmgr_task_t *fast = fast_queue, - *current = main_queue, - **last = &main_queue; - - fast_queue = NULL; - - while(fast != NULL) - { - #if defined(CONFIG_LIB_TMGR_UPTIME) - while(current != NULL && current->time < fast->time) - #elif defined(CONFIG_LIB_TMGR_COUNTDOWN) - while(current != NULL && current->time > fast->time) - #endif - { - current = current->next; - last = &((*last)->next); - } - - fast->next = current; - *last = fast; - - fast = fast->next; + /* Check fast queue */ + if (fast_queue != NULL) { + /* Attach fast queue to main queue */ + ANTARES_DISABLE_IRQS(); + tmgr_task_t *fast = fast_queue, + *current = main_queue, + **last = &main_queue; + + fast_queue = NULL; + ANTARES_ENABLE_IRQS(); + + while(fast != NULL) + { + #if defined(CONFIG_LIB_TMGR_UPTIME) + while(current != NULL && current->time < fast->time) + #elif defined(CONFIG_LIB_TMGR_COUNTDOWN) + while(current != NULL && current->time > fast->time) + #endif + { + current = current->next; + last = &((*last)->next); + } + + fast->next = current; + *last = fast; + + fast = fast->next; + } } - } - - // 2. Run main queue - if(main_queue != NULL) - { - #if defined(CONFIG_LIB_TMGR_UPTIME) - while(main_queue != NULL && main_queue->time <= sys_uptime) - #elif defined(CONFIG_LIB_TMGR_COUNTDOWN) - while(main_queue != NULL && main_queue->time <= 0) - #endif - { - main_queue->func(); - main_queue = main_queue->next; + + /* Run main queue tasks */ + if (main_queue != NULL) { + #if defined(CONFIG_LIB_TMGR_UPTIME) + while (main_queue != NULL && main_queue->time <= sys_uptime) { + #elif defined(CONFIG_LIB_TMGR_COUNTDOWN) + while (main_queue != NULL && main_queue->time <= 0) { + #endif + main_queue->func(); + main_queue = main_queue->next; + } } - } } /** @@ -104,17 +102,17 @@ void tmgr_tick(void) tmgr_time_t tmgr_get_uptime(void) { - return sys_uptime; + return sys_uptime; } void tmgr_delay(tmgr_time_t time) { #if defined(CONFIG_LIB_TMGR_UPTIME) - time += sys_uptime; - while(time > sys_uptime); + time += sys_uptime; + while (time > sys_uptime); #elif defined(CONFIG_LIB_TMGR_COUNTDOWN) - delay = time; - while(delay > 0); + delay = time; + while (delay > 0); #endif } @@ -122,52 +120,20 @@ void tmgr_delay(tmgr_time_t time) * @} */ -#ifdef CONFIG_LIB_TMGR_EXTRA /** * @section Time conversion functions * @{ */ void tmgr_set_fq(tmgr_time_t fq) { - sys_fq = fq; + sys_fq = fq; } tmgr_time_t tmgr_get_fq(void) { - return sys_fq; -} - -tmgr_time_t tmgr_ticks_to_us(tmgr_time_t ticks) -{ - return 1000000U * ticks / sys_fq; -} - -tmgr_time_t tmgr_ticks_to_ms(tmgr_time_t ticks) -{ - return 1000 * ticks / sys_fq; -} - -tmgr_time_t tmgr_ticks_to_s(tmgr_time_t ticks) -{ - return ticks / sys_fq; + return sys_fq; } -tmgr_time_t tmgr_us_to_ticks(tmgr_time_t us) -{ - return us * sys_fq / 1000000U; -} - -tmgr_time_t tmgr_ms_to_ticks(tmgr_time_t ms) -{ - return ms * sys_fq / 1000; -} - -tmgr_time_t tmgr_s_to_ticks(tmgr_time_t s) -{ - return s * sys_fq; -} - -#endif /** * @} */ From ca4876c96313be65d05828ec6c8d090577206cb2 Mon Sep 17 00:00:00 2001 From: Nikita 'webconn' Maslov Date: Fri, 1 Nov 2013 07:22:05 +0400 Subject: [PATCH 6/9] tmgr: add help lines in kcnf Signed-off-by: Nikita 'webconn' Maslov --- src/lib/ulc/kcnf | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib/ulc/kcnf b/src/lib/ulc/kcnf index 5797984..04bfafa 100644 --- a/src/lib/ulc/kcnf +++ b/src/lib/ulc/kcnf @@ -1,5 +1,8 @@ choice prompt "Mode of operation" + help + Select scheduler mode of operation; + for further information see doc/en/tmgr.txt config LIB_TMGR_UPTIME bool "Uptime" @@ -11,6 +14,8 @@ endchoice choice prompt "Time variable size" + help + Size of tmgr_time_t type (signed integer) config LIB_TMGR_TIME_16 bool "16 bit" From c925746c6fcd2210b23b2427cbead352142ad2b6 Mon Sep 17 00:00:00 2001 From: Nikita 'webconn' Maslov Date: Tue, 7 Jan 2014 23:09:46 +0400 Subject: [PATCH 7/9] tmgr: Full rework of timers library Now structure is a bit closer to Linux kernel timers. See docs for further information. Signed-off-by: Nikita 'webconn' Maslov --- doc/en/tmgr.txt | 159 +++++++++++++++++++++------------------ doc/ru/tmgr.txt | 158 ++++++++++++++++++++------------------- include/lib/tmgr.h | 60 ++++++++++----- src/lib/ulc/tmgr.c | 181 ++++++++++++++++++++++++++++----------------- 4 files changed, 319 insertions(+), 239 deletions(-) diff --git a/doc/en/tmgr.txt b/doc/en/tmgr.txt index 36f63a8..4004874 100644 --- a/doc/en/tmgr.txt +++ b/doc/en/tmgr.txt @@ -15,8 +15,6 @@ Features * Cross-platform (written fully in C without assembler magic) -* Easy to adjust (for example, set required size of time typedef) - How to use? ----------- @@ -25,15 +23,15 @@ How to use? ----------------- Operation of scheduler consists of two periodic function calls: -tmgr_tick() and tmgr_timer(). +tmgr_process() and tmgr_interrupt(). -tmgr_timer() function should be called in system timer interrupt hander which +tmgr_interrupt() function should be called in system timer interrupt hander which counts time quantums. Optimal time quantum size is 1 ms (author's opinion). In this case, frequency of timer interrupt handler calls is 1 kHz. -tmgr_timer() function just counts system time, so it is fast and safe for +tmgr_interrupt() function just counts system time, so it is fast and safe for interrupt context. -tmgr_tick() function should be called in main program loop. In this function +tmgr_process() function should be called in main program loop. In this function context all user's tasks will be executed, so it's not recommended to call this function in interrupt handles. @@ -46,23 +44,23 @@ It means than system timer runs at 1 kHz frequency (interrupt handler called 1000 times in second). Initialization of system timer depends on current platform, so tmgr library will _not_ initialize it. It is an user's problem. -tmgr_set_fq() receives as an argument one value of tmgr_time_t type (signed integer, +tmgr_set_fq() receives as an argument one value of tmgr_uptime_t type (signed integer, size of this type should be set in menuconfig by user). A sample program that correctly runs scheduler base: #include -void timer_interrupt() +void SYSTEM_TIMER_IRQ() { /* This function means an abstract timer interrupt handler */ - tmgr_timer(); + tmgr_interrupt(); } int main(void) { /* Platform-dependent initialization of system timer */ - timer_init(1000); + SYSTEM_TIMER_INIT(1000); /* Timer runs at 1000 Hz (time quantum is 1 ms) */ /* Giving this value to library */ tmgr_set_fq(1000); @@ -77,21 +75,70 @@ int main(void) 2. How to use scheduler ----------------------- -It's all quietly simple. Scheduler need to know what function do you want to -run (prototype of task function: void task(void);) and the value of delay. Also -it needs a piece of memory to save the task description, so one variable -of tmgr_task_t type in _static_ memory is required. This type is a structure, -but you shouldn't change any value in it manually. +First, you need a handler function. Its prototype is: + + void my_handler(void *data); + +You see that function accepts a void pointer. It is a +field for user information (if you want to control the context of +function running). You can keep it as is, or use this feature to give +something you need for your handler. (For example, set mode of operation). +This parameter is stored in timer structure, you set it once when you +initialize or re-initialize timer. + +Note that the value should be stored in static memory, because there are +no guarantees that there will be a right information in the stack when the +handler runs. + +Next, scheduler need to know about the value of delay. Also +it needs a piece of memory to save the task description. + +To keep timer data, scheduler needs a piece of _static_ memory. You can +init the memory yourself, but there is a macros to save your time: + + TMGR_DEFINE_TIMER(timer_name, handler, data, expires); + +If you want to define it as static, just write + + static TMGR_DEFINE_TIMER(timer_name, handler, data, expires); + +in the right place. + +'expires' value here is an uptime timestamp when the handler need to be +called. + +After all that, call this to add timer (launch it; really add to queue): + + tmgr_add_timer(&timer_name); + +To make a "delayed call" (ex. for 1000 ticks), set 'expires' value as + + TMGR_DELAYED(1000) +or + tmgr_get_uptime() + 1000 + +, which is the same. + +To setup (reinit) defined timer, you can use macros -To add your task in tmgr queue, just call: + TMGR_SETUP_TIMER(timer_name, handler, data, expires); - tmgr_register(&my_task, my_func_name, delay); +It just updates the fields of timer structure. NOTE: Don't do it if +you have added this timer and not sure that it's not in the queue. -my_task is a static memory variable of tmgr_task_t type, my_func_name is -the name of function you want to run, delay (obviously) is a delay value -(in system timer's ticks). +To remove timer from the queue, use -In real application, you should want to calculate the delay in s, ms etc. + tmgr_del_timer(&timer_name); + +After it, you can fully reinit your timer. + +To modify uptime trigger ('expires' value), use + + tmgr_mod_timer(&timer_name, new_expires); + +just if you timer is in queue. + +In real application, you should want to calculate the time in s, ms etc. To convert value from "natural" units to ticks, there are few macroses: tmgr_us_to_ticks(us) @@ -104,72 +151,38 @@ There are also macroses for convertions from ticks to natural units: tmgr_ticks_to_ms(ticks) tmgr_ticks_to_s(ticks) -Time values (ticks or natural) are stored in tmgr_time_t variables. - -So, this is correct way to run my_func with 500 ms delay: +Time values (ticks or natural) are stored in tmgr_uptime_t variables. - static tmgr_task_t my_task; - tmgr_register(&my_task, my_func, tmgr_ms_to_ticks(500)); +So, this is correct way to run my_func() with 500 ms delay and without params: + static TMGR_DEFINE_TIMER(my_timer, my_func, NULL, + TMGR_DELAYED(tmgr_ms_to_ticks(500))); + tmgr_add_timer(&my_timer); -3. Modes of operation ---------------------- -There are two modes of operation: uptime counter and countdown mode. - -3.1. Uptime counter -------------------- +3. Operation +------------ -In this mode the base of time counting is system uptime value. You can get +The base of time counting is system uptime value. You can get the current value of system uptime by calling tmgr_get_uptime(). -Notice. This function is available only in uptime counter mode. +This function returns the value of tmgr_uptime_t type (signed integer). -This function returns the value of tmgr_time_t type (signed integer). +In the timer interrupt handler only one action is performed - increment of uptime +value and in main loop, task runs if its "expire time" is equal to system +uptime. -This mode of operation is a little faster that countdown (because in the -timer interrupt handler only one action is performed - increment of uptime -value and in main loop, task runs if its "launch time" is equal to system -uptime), but it has an obvious limitation related with uptime variable -overflow. +Uptime is stored in tmgr_uptime_t variable. By adjusting its size (tmgr_uptime_t), +you can move the uptime limits. Available sizes are 32 and 64 bits. -Uptime is stored in tmgr_time_t variable. By adjusting its size (tmgr_time_t), -you can move the uptime limits. Available sizes are 16, 32 and 64 bits. - -The maximal time of stable operation in uptime counter move vs. tmgr_time_t size +The maximal time of stable operation in uptime counter move vs. tmgr_uptime_t size (system timers frequency = 1000 Hz): - 16 bits - 32.767 s 32 bits - 2147483.65 s ~ 596 h ~ 25 days 64 bits - 10^11 days -3.2. Countdown mode -------------------- - -This mode is a little slower that uptime counter, because in system timer -interrupt handler tmgr_timer() decrements values of each task's timer -separately (and it need to perform N operations if there are N tasks -in queue), but the limitation of system uptime is removed. - -In this mode you also can set tmgr_time_t size as you want. - -3.3. Summary ------------- - -If your arch can quickly perform operations with large integers or the -real uptime of your device will not exceed the stability threshold, we -recommend you to use uptime counter mode because it is faster and has -more features (uptime value is useful for debugging, for example). -The usage of countdown mode is justified if you will not run large -number of tasks or if you create a device that will run without -time limits. - -Notice. The usage of tmgr library is equal in both modes (excepts -tmgr_get_uptime() that is only first mode's feature), so you can change -the mode of operation if you need almost without any change of source code. - 4. Extra features ----------------- @@ -181,12 +194,12 @@ in natural units, use time convertion macroses. means 500 ms delay. -Function receives one tmgr_time_t value. +Function receives one tmgr_uptime_t value. Notice. We DO NOT RECOMMEND to use this function, especially in task context, because it can break down the tasks queue (tasks could not -run when delay is performed). Try to split your function into several +run when delay is performed). Try to split your function onto several subtasks. tmgr_get_fq() function just returns the value of system timer -frequency which was set before. (in tmgr_time_t type) +frequency which was set before. (in tmgr_uptime_t type) diff --git a/doc/ru/tmgr.txt b/doc/ru/tmgr.txt index d6a1f60..a335c7e 100644 --- a/doc/ru/tmgr.txt +++ b/doc/ru/tmgr.txt @@ -16,8 +16,6 @@ tmgr: Простейший планировщик задач * Кроссплатформенность (реализация полностью на Си, без ассемблера) -* Конфигурируемость (например, размер переменной для хранения времени - устанавливается пользователем) Как использовать? ----------------- @@ -26,15 +24,15 @@ tmgr: Простейший планировщик задач ---------------- Работа планировщика организуется циклическими вызовами двух функций: -tmgr_tick() и tmgr_timer(). +tmgr_process() и tmgr_interrupt(). -Функцию tmgr_timer() следует вызывать в обработчике прерывания таймера, который +Функцию tmgr_interrupt() следует вызывать в обработчике прерывания таймера, который отсчитывает кванты времени планировщика. Оптимальным размером кванта времени автор считает 1 мс (соответственно, частота вызова функции tmgr_timer() будет 1 кГц). Эта функция обеспечивает только отсчёт времени (в зависимости от режима работы планировщика) и минимально загружает систему в контексте прерывания. -Функцию tmgr_tick() следует вызывать в основном цикле программы. В рамках вызова +Функцию tmgr_process() следует вызывать в основном цикле программы. В рамках вызова этой функции будет происходить выполение задач пользователя, так что её вызов в контексте прерывания не рекомендуется. @@ -54,23 +52,23 @@ tmgr_tick() и tmgr_timer(). #include -void timer_interrupt() +void SYSTEM_TIMER_IRQ() { /* Прерывание системного таймера (абстрактно) */ - tmgr_timer(); + tmgr_interrupt(); } int main(void) { /* Инициализация системного таймера, платформо-зависима */ - timer_init(1000); + SYSTEM_TIMER_INIT(1000); /* Пусть таймер работает на частоте 1 кГц (квант времени 1 мс) */ /* Передаём это значение планировщику */ tmgr_set_fq(1000); /* Вызов основной функции планировщика в бесконечном цикле */ while (1) - tmgr_tick(); + tmgr_process(); return 0; } @@ -78,20 +76,70 @@ int main(void) 2. Работа с планировщиком ------------------------- -Всё максимально просто. Планировщику требуется знать, какую функцию вы хотите -запустить (функция не должна ничего принимать и возвращать: void foo(void);) и -c какой задержкой. От вас также потребуется одна переменная типа tmgr_task_t -(на самом деле, это структура, но вся работа с ней происходит внутри библиотеки -и вам не нужно обращаться к её элементам) в _статической_ памяти. +Для начала вам понадобится функция-обработчик, которая будет вызвана таймером. +Прототип функции: -Для того, чтобы поместить задачу в очередь планировщика, в нужном месте кода -сделайте вызов: + void my_handler(void *data); - tmgr_register(&my_task, my_func_name, delay); +Она принимает в качестве аргумента один указатель на void. Это можно +использовать для передачи какой-либо пользовательской информации обработчику +(например, для того, чтобы управлять контекстом выполнения задачи). Можно +оставить всё как есть, установив при инициализации таймера NULL в качестве +параметра. Этот параметр хранится в структуре таймера, задаётся при +(ре-)инициализации таймера. -Здесь my_task - это переменная типа tmgr_task_t (в статической памяти), -my_func_name - имя функции, которую потребуется выполнить, delay - задержка -запуска. +Важно, чтобы данные, указатель на которые передаётся функции, хранились в +статической памяти. Никаких гарантий, что, если вы храните их на стеке +(в локальной переменной), в момент вызова обработчика в стеке будет именно +нужное значение. + +Планировщику также требуется знать, когда запускать обработчик. + +Для хранения данных таймера, планировщику требуется немного _статической_ +памяти. Можно объявить одну переменную типа tmgr_timer_t (структура) вручную, +а можно использовать встроенные макросы, облегчающие жизнь: + + TMGR_DEFINE_TIMER(timer_name, handler, data, expires); + +Переменная будет называться timer_name, handler - название функции-обработчика, +data - указатель на данные, expires - таймстемп, в какое время (аптайм) +выполнить обработчик. + +Если переменная должна быть static, просто допишите + + static TMGR_DEFINE_TIMER(timer_name, handler, data, expires); + +После всей инициализации, для того, чтобы запустить таймер (добавить в очередь), +запускаем: + + tmgr_add_timer(&timer_name); + +Для того, чтобы вручную не считать время для "запуска с задержкой", в качестве +'expires' можно записать так (сейчас, для примера, задержка на 1000 тиков): + + TMGR_DELAYED(1000) +или + tmgr_get_uptime() + 1000 + +, что, в сущности, одно и то же. + +Чтобы изменить таймер (пока он не в очереди), можно использовать макрос + + TMGR_SETUP_TIMER(timer_name, handler, data, expires); + +Он просто обновляет значения в полях структуры. + +Для того, чтобы удалить таймер из очереди, используем + + tmgr_del_timer(&timer_name); + +После этого его можно спокойно перенастроить. + +Для того, чтобы просто обновить время запуска, можно использовать + + tmgr_mod_timer(&timer_name, new_expires); + +, если таймер сейчас в очереди. В общем случае задержка запуска считается в тиках системного таймера. Для того, чтобы выразить задержку в нормальных единицах времени, следует использовать @@ -108,93 +156,49 @@ my_func_name - имя функции, которую потребуется вы tmgr_ticks_to_s(ticks) Время (число тиков, а также нормальных единиц) определяется типом данных -tmgr_time_t (целый знаковый, размер устанавливается пользователем в меню +tmgr_uptime_t (целый знаковый, размер устанавливается пользователем в меню конфигурации menuconfig). Таким образом, все функции конвертирования времени -принимают и возвращают tmgr_time_t. +принимают и возвращают tmgr_uptime_t. Таким образом, вызов функции my_func с задержкой 500 мс будет выглядеть так: - static tmgr_task_t my_task; - tmgr_register(&my_task, my_func, tmgr_ms_to_ticks(500)); - + static TMGR_DEFINE_TIMER(my_timer, my_func, NULL, + TMGR_DELAYED(tmgr_ms_to_ticks(500))); + tmgr_add_timer(&my_timer); -3. Режимы работы планировщика ------------------------------ - -Планировщик имеет два режима работы: прямого и обратного отсчёта времени -(соответственно, uptime counter и countdown) -3.1. Прямой отсчёт времени (uptime) ------------------------------------ +3. Режим работы планировщика +---------------------------- -В этом режиме работы основанием отсчёта времени является значение "времени +Основанием отсчёта времени является значение "времени работы" (uptime) программы. При этом пользователь может в любой момент узнать текущее значение uptime, вызвав функцию tmgr_get_uptime(). -Замечание. Функция tmgr_get_update() доступна только в этом режиме работы. - -Функция возвращает тип tmgr_time_t (целочисленный знаковый, размер которого +Функция возвращает тип tmgr_uptime_t (целочисленный знаковый, размер которого устанавливается пользователем в меню конфигурации menuconfig). Такой метод работы быстрее (в прерывании происходит только инкрементирование значения переменной uptime, в основном цикле программы вызов задачи происходит -при совпадении текущего значения uptime с подсчитанным при регистрации). Однако, -он имеет очевидное ограничение на общее время работы устройства (возможность -переполнения переменной, считающий uptime). +при совпадении текущего значения uptime с установленным при добавлении). Отсчитанное время хранится в переменной типа tmgr_time_t. Это целый знаковый тип, размер которого можно установить в меню конфигурации menuconfig. -Возможно использование 16, 32 и 64-битной переменной. +Возможно использование 32 и 64-битной переменной. Предельное время стабильной работы в uptime-режиме в зависимости от размера переменной (частота системного таймера - 1 кГц): - 16 бит - 32.767 секунд. 32 бит - 2147483.65 с ~ 596 ч ~ 25 суток 64 бит - 10^11 суток -3.2. Обратный отсчёт времени (countdown) ----------------------------------------- - -Этот режим работы немного медленней, чем uptime, так как суть его работы -заключается в декрементировании значений таймера каждой задачи в отдельности, -зато отсутствуют ограничения на время работы. - -В этом режиме также нужно установить размер переменной времени (по вашему -усмотрению). - -3.3. Вывод ----------- - -Если целевая архитектура позволяет достаточно быстро работать с 64-битными -переменными, или время непрерывной работы вашего устройства не превысит 25 -суток, то есть смысл использовать первый режим работы как более быстрый и -предоставляющий больше возможностей. Если же устройство должно проработать -непрерывно достаточно долго, но нет возможности использовать 64-битные -переменные, то можно выбрать режим countdown (правда, при увеличении количества -задач в очереди время работы в прерывании системного таймера будет линейно расти, -так что стоит серьёзно подумать о выборе режима). - -Замечание. Общий принцип работы с планировщиком одинаков в обоих режимах. -(Единственное отличие - отсутствие функции tmgr_get_uptime() во втором режиме). -Таким образом, в большинстве случаев можно практически без изменений в коде -программы изменить способ функционирования планировщика. - - 4. Описание параметров планировщика (menuconfig) ------------------------------------------------ -Параметров всего два. - -4.1. Mode of operation ----------------------- -Режим работы таймера. Выбирается из двух (Uptime и Countdown), описание выше. - -4.2. Time variable size +Time variable size ----------------------- -Размер переменной для хранения времени. Описание выше (Режимы работы). +Размер переменной для хранения времени. (см. Режим работы). 5. Дополнительные возможности diff --git a/include/lib/tmgr.h b/include/lib/tmgr.h index 39b05e2..0b83ebf 100644 --- a/include/lib/tmgr.h +++ b/include/lib/tmgr.h @@ -5,14 +5,18 @@ #include #include -#if defined(CONFIG_LIB_TMGR_TIME_16) -typedef int16_t tmgr_time_t; -#elif defined(CONFIG_LIB_TMGR_TIME_32) -typedef int32_t tmgr_time_t; +/* TODO: remove it ASAP */ +#ifndef ANTARES_ENABLE_IRQS +#define ANTARES_ENABLE_IRQS() +#define ANTARES_DISABLE_IRQS() +#endif + +#if defined(CONFIG_LIB_TMGR_TIME_32) +typedef int32_t tmgr_uptime_t; #elif defined(CONFIG_LIB_TMGR_TIME_64) -typedef int64_t tmgr_time_t; +typedef int64_t tmgr_uptime_t; #else -#error "tmgr: Wrong tmgr_time_t size (must be 16, 32 or 64 bits)" +#error "tmgr: Wrong tmgr_uptime_t size (must be 32 or 64 bits)" #endif #define tmgr_ticks_to_us(ticks) (1000000U * (ticks) / tmgr_get_fq()) @@ -22,22 +26,38 @@ typedef int64_t tmgr_time_t; #define tmgr_ms_to_ticks(ms) ((ms) * tmgr_get_fq() / 1000) #define tmgr_s_to_ticks(s) ((s) * tmgr_get_fq()) -typedef struct struct_tmgr_task_t { - tmgr_time_t time; - void (*func)(void); - struct struct_tmgr_task_t * next; -} tmgr_task_t; +/* Timer initializer macros. + * Creates right structure for timer and fill it + */ +#define TMGR_TIMER_INIT(_function, _data, _expires) { .expires = (_expires), .func = (_function), .data = (_data) } +#define TMGR_DEFINE_TIMER(_name, _function, _data, _expires) tmgr_timer_t _name = TMGR_TIMER_INIT(_function, _data, _expires) +#define TMGR_SETUP_TIMER(_timer, _function, _data, _expires) _timer.func = (_function); _timer.data = (_data); _timer.expires = (_expires) -void tmgr_tick(void); -void tmgr_timer(void); -void tmgr_register(tmgr_task_t * task, void (*function)(void), tmgr_time_t delay); +#define TMGR_DELAYED(delay) (tmgr_get_uptime() + delay) -#ifdef CONFIG_LIB_TMGR_UPTIME -tmgr_time_t tmgr_get_uptime(void); -#endif -void tmgr_delay(tmgr_time_t time); +/* Timers are making doubly-linked list, it's good for + * fast search and removal of timer object. + */ +typedef struct struct_tmgr_timer_t { + tmgr_uptime_t expires; + void (*func)(uint8_t *); + uint8_t *data; + + struct struct_tmgr_timer_t *next; + struct struct_tmgr_timer_t *prev; +} tmgr_timer_t; + +void tmgr_process(void); +void tmgr_interrupt(void); + +void tmgr_add_timer(tmgr_timer_t *timer); +void tmgr_mod_timer(tmgr_timer_t *timer, tmgr_uptime_t expires); +void tmgr_del_timer(tmgr_timer_t *timer); + +tmgr_uptime_t tmgr_get_uptime(void); +void tmgr_delay(tmgr_uptime_t time); -void tmgr_set_fq(tmgr_time_t fq); -tmgr_time_t tmgr_get_fq(void); +void tmgr_set_fq(tmgr_uptime_t fq); +tmgr_uptime_t tmgr_get_fq(void); #endif diff --git a/src/lib/ulc/tmgr.c b/src/lib/ulc/tmgr.c index a50a07e..1369077 100644 --- a/src/lib/ulc/tmgr.c +++ b/src/lib/ulc/tmgr.c @@ -1,96 +1,144 @@ #include -static volatile tmgr_time_t sys_uptime = 0; +static volatile tmgr_uptime_t sys_uptime = 0; static volatile int32_t sys_fq = 1; +static volatile uint8_t tick_parsed = 0; -static tmgr_task_t *fast_queue = NULL; -static tmgr_task_t *main_queue = NULL; +/* Dirty-hack, but saves memory and runs on each platworm. + * We will use pointers to the two previous vars for + * "flag" pointers to check if timer list element is in + * main queue or in fast queue (or NULL if is not in queue) + */ +#define IN_MAIN (tmgr_timer_t *) (&sys_uptime) +#define IN_FAST (tmgr_timer_t *) (&sys_fq) -#ifdef CONFIG_LIB_TMGR_COUNTDOWN -static tmgr_time_t delay; -#endif +static volatile tmgr_timer_t *fast_queue = NULL; +static volatile tmgr_timer_t *main_queue = NULL; /** * @section Task Manager core * @{ */ -void tmgr_timer(void) +void tmgr_interrupt(void) { -#if defined(CONFIG_LIB_TMGR_UPTIME) sys_uptime++; -#elif defined(CONFIG_LIB_TMGR_COUNTDOWN) - /* Implement delay feature */ - if (delay > 0) - delay--; - - tmgr_task_t * queue = main_queue; - while (queue != NULL) { - queue->time --; - queue = queue->next; - } -#endif + tick_parsed = 0; } -void tmgr_register(tmgr_task_t * task, void (*function)(void), tmgr_time_t delay) +void tmgr_process(void) { -#if defined(CONFIG_LIB_TMGR_UPTIME) - task->time = sys_uptime + delay; -#elif defined(CONFIG_LIB_TMGR_COUNTDOWN) - task->time = delay; -#endif - task->func = function; - - /* these lines are atomic */ + if (tick_parsed) /* this is to save time for interrupts if we had just parsed queues */ + return; + + /* Full function is atomic; accept it as it is */ ANTARES_DISABLE_IRQS(); - task->next = fast_queue; - fast_queue = task; - ANTARES_ENABLE_IRQS(); -} -void tmgr_tick(void) -{ /* Check fast queue */ if (fast_queue != NULL) { - /* Attach fast queue to main queue */ - ANTARES_DISABLE_IRQS(); - tmgr_task_t *fast = fast_queue, - *current = main_queue, - **last = &main_queue; + + + /* Save fast_queue list and make it ready for next timers. + * Also, prepare main_queue for insertions. + */ + tmgr_timer_t *fast = (tmgr_timer_t *) fast_queue, + *current = (tmgr_timer_t *) main_queue; fast_queue = NULL; - ANTARES_ENABLE_IRQS(); - - while(fast != NULL) - { - #if defined(CONFIG_LIB_TMGR_UPTIME) - while(current != NULL && current->time < fast->time) - #elif defined(CONFIG_LIB_TMGR_COUNTDOWN) - while(current != NULL && current->time > fast->time) - #endif - { + + while (fast != NULL) { + while (current != NULL && current->expires < fast->expires) current = current->next; - last = &((*last)->next); + + if (!current) { /* only element in main queue */ + fast->prev = IN_MAIN; + main_queue = fast; + } else if (!current->prev) { /* if next elem in queue is root */ + current->prev = fast; + main_queue = fast; + } else { + current->prev->next = fast; + fast->prev = current->prev; + current->prev = fast; } - fast->next = current; - *last = fast; - - fast = fast->next; + fast = fast->next; /* shift fast queue, now current element detached from fast queue */ + if (current) + current->prev->next = current; + } } /* Run main queue tasks */ if (main_queue != NULL) { - #if defined(CONFIG_LIB_TMGR_UPTIME) - while (main_queue != NULL && main_queue->time <= sys_uptime) { - #elif defined(CONFIG_LIB_TMGR_COUNTDOWN) - while (main_queue != NULL && main_queue->time <= 0) { - #endif - main_queue->func(); + while (main_queue != NULL && main_queue->expires <= sys_uptime) { + void (*func)(uint8_t *) = main_queue->func; + uint8_t *data = main_queue->data; main_queue = main_queue->next; + if (main_queue) + main_queue->prev = IN_MAIN; /* dummy pointer-flag */ + + ANTARES_ENABLE_IRQS(); /* run of timer function is no atomic, save interrupts! */ + func(data); + ANTARES_DISABLE_IRQS(); } } + ANTARES_ENABLE_IRQS(); +} + + +void tmgr_add_timer(tmgr_timer_t *timer) +{ + if (!timer) + return; + + /* these lines are atomic */ + /* TODO!!! REPLACE WITH ANTARES_ATOMIC() {} TO SAVE STATE! */ + ANTARES_DISABLE_IRQS(); + timer->next = (tmgr_timer_t *) fast_queue; + timer->prev = IN_FAST; /* set flag that this timer in fast queue */ + + if (fast_queue) + fast_queue->prev = timer; + + fast_queue = timer; + ANTARES_ENABLE_IRQS(); +} + +void tmgr_del_timer(tmgr_timer_t *timer) +{ + if (!timer) + return; + + /* these lines are atomic */ + /* TODO!!! REPLACE WITH ANTARES_ATOMIC TO SAVE STATE! */ + ANTARES_DISABLE_IRQS(); + if (timer->prev == IN_MAIN) { + main_queue = timer->next; + if (main_queue) + main_queue->prev = IN_MAIN; + } else if (timer->prev == IN_FAST) { + fast_queue = timer->next; + if (fast_queue) + fast_queue->prev = IN_FAST; + } else if (timer->prev) { + timer->prev->next = timer->next; + } + ANTARES_ENABLE_IRQS(); +} + +void tmgr_mod_timer(tmgr_timer_t *timer, tmgr_uptime_t expires) +{ + if (!timer) + return; + + /* these lines are atomic */ + /* TODO!!! REPLACE WITH ANTARES_ATOMIC() {} TO SAVE STATE! */ + ANTARES_DISABLE_IRQS(); + tmgr_del_timer(timer); + timer->expires = expires; + tmgr_add_timer(timer); + ANTARES_ENABLE_IRQS(); } /** @@ -100,20 +148,15 @@ void tmgr_tick(void) * @{ */ -tmgr_time_t tmgr_get_uptime(void) +tmgr_uptime_t tmgr_get_uptime(void) { return sys_uptime; } -void tmgr_delay(tmgr_time_t time) +void tmgr_delay(tmgr_uptime_t time) { -#if defined(CONFIG_LIB_TMGR_UPTIME) time += sys_uptime; while (time > sys_uptime); -#elif defined(CONFIG_LIB_TMGR_COUNTDOWN) - delay = time; - while (delay > 0); -#endif } /** @@ -124,12 +167,12 @@ void tmgr_delay(tmgr_time_t time) * @section Time conversion functions * @{ */ -void tmgr_set_fq(tmgr_time_t fq) +void tmgr_set_fq(int32_t fq) { sys_fq = fq; } -tmgr_time_t tmgr_get_fq(void) +int32_t tmgr_get_fq(void) { return sys_fq; } From f7055c8d690063446809011f9969d87f245fef5e Mon Sep 17 00:00:00 2001 From: Nikita 'webconn' Maslov Date: Tue, 7 Jan 2014 23:18:01 +0400 Subject: [PATCH 8/9] tmgr: Fix unnecessary config in kcnf Remove Mode of Operation config entry. Signed-off-by: Nikita 'webconn' Maslov --- src/lib/ulc/kcnf | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/lib/ulc/kcnf b/src/lib/ulc/kcnf index 04bfafa..fb8c6f7 100644 --- a/src/lib/ulc/kcnf +++ b/src/lib/ulc/kcnf @@ -1,25 +1,8 @@ -choice - prompt "Mode of operation" - help - Select scheduler mode of operation; - for further information see doc/en/tmgr.txt - - config LIB_TMGR_UPTIME - bool "Uptime" - - config LIB_TMGR_COUNTDOWN - bool "Countdown" - -endchoice - choice prompt "Time variable size" help Size of tmgr_time_t type (signed integer) - config LIB_TMGR_TIME_16 - bool "16 bit" - config LIB_TMGR_TIME_32 bool "32 bit" From 2b9771fcf0b1df934cf387246510005e263a8d55 Mon Sep 17 00:00:00 2001 From: Nikita 'webconn' Maslov Date: Wed, 8 Jan 2014 19:54:20 +0400 Subject: [PATCH 9/9] tmgr: Fix queue operation and remove tmgr_set_fq() Now queue works more correct. Also, tmgr_set_fq() functions removed (you still need to set system timer frequency in menuconfig (see docs) Signed-off-by: Nikita 'webconn' Maslov --- doc/en/tmgr.txt | 13 +------ doc/ru/tmgr.txt | 12 +----- include/lib/tmgr.h | 17 ++++----- src/lib/ulc/kcnf | 4 ++ src/lib/ulc/tmgr.c | 91 ++++++++++++++++++++-------------------------- 5 files changed, 56 insertions(+), 81 deletions(-) diff --git a/doc/en/tmgr.txt b/doc/en/tmgr.txt index 4004874..5b1233d 100644 --- a/doc/en/tmgr.txt +++ b/doc/en/tmgr.txt @@ -36,17 +36,11 @@ context all user's tasks will be executed, so it's not recommended to call this function in interrupt handles. Also you need to set system timer frequency (for time conversion functions) using -tmgr_set_fq() call like this: +menuconfig entry "Scheduler timer frequency" (in Hz). - tmgr_set_fq(1000); - -It means than system timer runs at 1 kHz frequency (interrupt handler called -1000 times in second). Initialization of system timer depends on current +Initialization of system timer depends on current platform, so tmgr library will _not_ initialize it. It is an user's problem. -tmgr_set_fq() receives as an argument one value of tmgr_uptime_t type (signed integer, -size of this type should be set in menuconfig by user). - A sample program that correctly runs scheduler base: #include @@ -61,9 +55,6 @@ int main(void) { /* Platform-dependent initialization of system timer */ SYSTEM_TIMER_INIT(1000); - /* Timer runs at 1000 Hz (time quantum is 1 ms) */ - /* Giving this value to library */ - tmgr_set_fq(1000); /* Run tick function in infinite loop */ while (1) diff --git a/doc/ru/tmgr.txt b/doc/ru/tmgr.txt index a335c7e..293a6d8 100644 --- a/doc/ru/tmgr.txt +++ b/doc/ru/tmgr.txt @@ -37,17 +37,12 @@ tmgr_process() и tmgr_interrupt(). в контексте прерывания не рекомендуется. Для функции конвертирования времени требуется передать библиотеке значение частоты -системного таймера (в Гц). Это делается вызовом функции tmgr_set_fq(). Например, +системного таймера (в Гц). Это делается в меню настройки menuconfig, параметр +Scheduler timer frequency. - tmgr_set_fq(1000); - -означает, что системный таймер будет вызывать прерывание с частотой 1 кГц. Замечание: Планировщик _не инициализирует_ таймер. Инициализация системного таймера и организация вызовов функций планировщика лежит на пользователе. -Функция tmgr_set_fq() принимает в качестве аргумента одно значение типа tmgr_time_t -(целый знаковый, размер которого можно установить в меню конфигурации menuconig). - Пример программы, обеспечивающей корректную работу таймера: #include @@ -62,9 +57,6 @@ int main(void) { /* Инициализация системного таймера, платформо-зависима */ SYSTEM_TIMER_INIT(1000); - /* Пусть таймер работает на частоте 1 кГц (квант времени 1 мс) */ - /* Передаём это значение планировщику */ - tmgr_set_fq(1000); /* Вызов основной функции планировщика в бесконечном цикле */ while (1) diff --git a/include/lib/tmgr.h b/include/lib/tmgr.h index 0b83ebf..cd1cfb2 100644 --- a/include/lib/tmgr.h +++ b/include/lib/tmgr.h @@ -19,12 +19,14 @@ typedef int64_t tmgr_uptime_t; #error "tmgr: Wrong tmgr_uptime_t size (must be 32 or 64 bits)" #endif -#define tmgr_ticks_to_us(ticks) (1000000U * (ticks) / tmgr_get_fq()) -#define tmgr_ticks_to_ms(ticks) (1000 * (ticks) / tmgr_get_fq()) -#define tmgr_ticks_to_s(ticks) ((ticks) / tmgr_get_fq()) -#define tmgr_us_to_ticks(us) ((us) * tmgr_get_fq() / 1000000U) -#define tmgr_ms_to_ticks(ms) ((ms) * tmgr_get_fq() / 1000) -#define tmgr_s_to_ticks(s) ((s) * tmgr_get_fq()) +#define tmgr_ticks_to_us(ticks) (1000000U * (ticks) / CONFIG_LIB_TMGR_FQ) +#define tmgr_ticks_to_ms(ticks) (1000 * (ticks) / CONFIG_LIB_TMGR_FQ) +#define tmgr_ticks_to_s(ticks) ((ticks) / CONFIG_LIB_TMGR_FQ) +#define tmgr_us_to_ticks(us) ((us) * CONFIG_LIB_TMGR_FQ / 1000000U) +#define tmgr_ms_to_ticks(ms) ((ms) * CONFIG_LIB_TMGR_FQ / 1000) +#define tmgr_s_to_ticks(s) ((s) * CONFIG_LIB_TMGR_FQ) + +#define tmgr_get_fq() CONFIG_LIB_TMGR_FQ /* Timer initializer macros. * Creates right structure for timer and fill it @@ -57,7 +59,4 @@ void tmgr_del_timer(tmgr_timer_t *timer); tmgr_uptime_t tmgr_get_uptime(void); void tmgr_delay(tmgr_uptime_t time); -void tmgr_set_fq(tmgr_uptime_t fq); -tmgr_uptime_t tmgr_get_fq(void); - #endif diff --git a/src/lib/ulc/kcnf b/src/lib/ulc/kcnf index fb8c6f7..217572e 100644 --- a/src/lib/ulc/kcnf +++ b/src/lib/ulc/kcnf @@ -1,3 +1,7 @@ +config LIB_TMGR_FQ + int "Scheduler timer frequecy (in Hz)" + default 1000 + choice prompt "Time variable size" help diff --git a/src/lib/ulc/tmgr.c b/src/lib/ulc/tmgr.c index 1369077..17d7864 100644 --- a/src/lib/ulc/tmgr.c +++ b/src/lib/ulc/tmgr.c @@ -1,7 +1,6 @@ #include static volatile tmgr_uptime_t sys_uptime = 0; -static volatile int32_t sys_fq = 1; static volatile uint8_t tick_parsed = 0; /* Dirty-hack, but saves memory and runs on each platworm. @@ -10,7 +9,7 @@ static volatile uint8_t tick_parsed = 0; * main queue or in fast queue (or NULL if is not in queue) */ #define IN_MAIN (tmgr_timer_t *) (&sys_uptime) -#define IN_FAST (tmgr_timer_t *) (&sys_fq) +#define IN_FAST (tmgr_timer_t *) (&tick_parsed) static volatile tmgr_timer_t *fast_queue = NULL; static volatile tmgr_timer_t *main_queue = NULL; @@ -36,45 +35,63 @@ void tmgr_process(void) /* Check fast queue */ if (fast_queue != NULL) { - - /* Save fast_queue list and make it ready for next timers. * Also, prepare main_queue for insertions. */ tmgr_timer_t *fast = (tmgr_timer_t *) fast_queue, - *current = (tmgr_timer_t *) main_queue; + *current = (tmgr_timer_t *) main_queue, + *previous = NULL; fast_queue = NULL; while (fast != NULL) { - while (current != NULL && current->expires < fast->expires) + while (current != NULL && current->expires < fast->expires) { + previous = current; current = current->next; + } - if (!current) { /* only element in main queue */ - fast->prev = IN_MAIN; - main_queue = fast; - } else if (!current->prev) { /* if next elem in queue is root */ - current->prev = fast; + if (!previous) { /* if new element is root */ main_queue = fast; - } else { - current->prev->next = fast; - fast->prev = current->prev; - current->prev = fast; - } + fast->prev = IN_MAIN; + + fast = fast->next; /* shift fast queue */ - fast = fast->next; /* shift fast queue, now current element detached from fast queue */ - if (current) - current->prev->next = current; + main_queue->next = current; + if (current) + current->prev = (tmgr_timer_t *) main_queue; + + } else { /* new element is NOT root */ + previous->next = fast; + fast->prev = previous; + + fast = fast->next; /* shift fast queue */ + + if (current) { /* if our element is NOT last */ + previous->next->next = current; + current->prev = previous->next; + } else { /* if our element IS last */ + previous->next->next = NULL; + } + + } + + current = (tmgr_timer_t *) main_queue; + previous = NULL; } } /* Run main queue tasks */ if (main_queue != NULL) { while (main_queue != NULL && main_queue->expires <= sys_uptime) { - void (*func)(uint8_t *) = main_queue->func; - uint8_t *data = main_queue->data; + tmgr_timer_t *current = (tmgr_timer_t *) main_queue; main_queue = main_queue->next; + + void (*func)(uint8_t *) = current->func; + uint8_t *data = current->data; + current->prev = NULL; + current->next = NULL; + if (main_queue) main_queue->prev = IN_MAIN; /* dummy pointer-flag */ @@ -141,13 +158,6 @@ void tmgr_mod_timer(tmgr_timer_t *timer, tmgr_uptime_t expires) ANTARES_ENABLE_IRQS(); } -/** - * @} - * - * @section Task Manager Uptime functions - * @{ - */ - tmgr_uptime_t tmgr_get_uptime(void) { return sys_uptime; @@ -156,27 +166,6 @@ tmgr_uptime_t tmgr_get_uptime(void) void tmgr_delay(tmgr_uptime_t time) { time += sys_uptime; - while (time > sys_uptime); -} - -/** - * @} - */ - -/** - * @section Time conversion functions - * @{ - */ -void tmgr_set_fq(int32_t fq) -{ - sys_fq = fq; -} - -int32_t tmgr_get_fq(void) -{ - return sys_fq; + while (time > sys_uptime) + asm volatile ("nop"); } - -/** - * @} - */