diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..2be11d6 --- /dev/null +++ b/Makefile @@ -0,0 +1,38 @@ + +CC = gcc + +DEL = rm -rf + +CFLGAS += -std=c99 +CFLAGS += -D TEST +CFLAGS += -g + +OBJ_PATH = . +BIN_PATH = . +SRC_PATH = . + +IDIR = . +INC += -I$(IDIR) + +TARGET = $(BIN_PATH)/test + +C_SRCS += examples/test_linux.c +C_SRCS += multi_timer.c +OBJ := $(patsubst %.c,%.o,$(filter %.c,$(addprefix $(SRC_PATH)/,$(C_SRCS)))) +TARGET_OBJ := $(addprefix $(OBJ_PATH)/,$(OBJ)) + +$(warning "TARGET_OBJ=$(TARGET_OBJ)") + +.PHONY : clean all + +all: $(TARGET) + +$(TARGET) : $(TARGET_OBJ) + $(CC) $(INC) $(CFLAGS) $^ -o $@ + +$(TARGET_OBJ): %.o : %.c + $(CC) $(INC) $(CFLAGS) -c $< -o $@ + +clean: + -$(DEL) $(OBJ_PATH)/$(OBJ) + -$(DEL) $(TARGET) diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 124562d..72625fc --- a/README.md +++ b/README.md @@ -4,23 +4,35 @@ MultiTimer 是一个软件定时器扩展模块,可无限扩展你所需的定时器任务,取代传统的标志位判断方式, 更优雅更便捷地管理程序的时间触发时序。 ## 使用方法 -1.先申请一个定时器管理handle +1.在multi_timer.h中配置定时器tick时钟频率(即1个tick代表N毫秒钟) + +``` +/* +It means 1 tick for 1ms. +Your can configurate for your tick time such as 5ms/10ms and so on. +*/ +#define CFG_TIMER_1_TICK_N_MS 1 +``` + +2.先申请一个定时器管理handle ``` struct Timer timer1; ``` -2.初始化定时器对象,注册定时器回调处理函数,设置延迟启动时间(ms),循环定时触发时间 + +3.初始化定时器对象,注册定时器回调处理函数,设置延迟启动时间(ms),循环定时触发时间 ``` -timer_init(struct Timer* handle, void(*timeout_cb)(), uint32_t timeout, uint32_t repeat); +timer_init(struct Timer* handle, void(*timeout_cb)(void *arg), uint32_t timeout, uint32_t repeat); ``` -3.启动定时器 +4.启动定时器 ``` timer_start(&timer1); ``` -4.设置1ms的硬件定时器循环调用 *timer_ticks()* 以提供时间基准 + +5.设置1ms的硬件定时器循环调用 *timer_ticks()* 以提供时间基准 ``` void HAL_SYSTICK_Callback(void) @@ -29,8 +41,7 @@ void HAL_SYSTICK_Callback(void) } ``` - -5.在主循环调用定时器后台处理函数 +6.在主循环调用定时器后台处理函数 ``` int main() @@ -43,20 +54,31 @@ int main() } ``` +## 功能限制 +1.定时器的时钟频率直接影响定时器的精确度,尽可能采用1ms/5ms/10ms这几个精度较高的tick; + +2.定义应用定时器时,超时时间应合理设置,不应过大或过小,否则可能导致定时器超时时间不精准; + +3.定时器的回调函数内不应执行耗时操作,否则可能因占用过长的时间,导致其他定时器无法正常超时;一般来说,若干个tick的时间是可以接受的; + +4.由于定时器的回调函数是在timer_loop内执行的,需要注意栈空间的使用不能过大,否则可能会导致栈溢出。 + ## Examples +见example目录下的测试代码,main.c为普通平台测试demo,test_linux.c为linux平台的测试demo。 + ``` #include "multi_timer.h" struct Timer timer1; struct Timer timer2; -void timer1_callback() +void timer1_callback(void *arg) { printf("timer1 timeout!\r\n"); } -void timer2_callback() +void timer2_callback(void *arg) { printf("timer2 timeout!\r\n"); } diff --git a/examples/main.c b/examples/main.c old mode 100644 new mode 100755 index 6d51773..e5a7c03 --- a/examples/main.c +++ b/examples/main.c @@ -3,28 +3,29 @@ struct Timer timer1; struct Timer timer2; -void timer1_callback() +void timer1_callback(void *arg) { - printf("timer1 timeout!\r\n"); + printf("timer1 timeout! arg: %p\r\n", arg); } -void timer2_callback() +void timer2_callback(void *arg) { - printf("timer2 timeout!\r\n"); + printf("timer2 timeout! arg: %p\r\n", arg); } -int main() +int main(void) { - timer_init(&timer1, timer1_callback, 1000, 1000); //1s loop + timer_init(&timer1, timer1_callback, 1000, 1000, NULL); //1s loop timer_start(&timer1); - timer_init(&timer2, timer2_callback, 50, 0); //50ms delay + timer_init(&timer2, timer2_callback, 50, 0, NULL); //50ms delay timer_start(&timer2); - while(1) { - + while(1) { timer_loop(); } + + return 0; } void HAL_SYSTICK_Callback(void) diff --git a/examples/test_linux.c b/examples/test_linux.c old mode 100644 new mode 100755 index 85ff2ae..d9618e3 --- a/examples/test_linux.c +++ b/examples/test_linux.c @@ -1,15 +1,52 @@ #include #include #include +#include +#include +#include + #include "../multi_timer.h" +#define LOG_TIMESTAMP_FORMAT "[%Y-%m-%d %H:%M:%S]" +#define LOG_TIMESTAMP_LEN 21 +#define MS_TIMESTAMP_LEN 5 + +int32_t printf_timestamp(const char *msg, ...) +{ + char content[1024] = {0}; + time_t time_write; + struct tm tm_Log; + struct timeval t; + uint32_t len = 0; + va_list vl_list; + + time_write = time(NULL); + localtime_r(&time_write, &tm_Log); + strftime((char *)&content[len], sizeof(content) - len - 1, LOG_TIMESTAMP_FORMAT, &tm_Log); + len = strlen(&content[len]); + + /* .msec] */ + gettimeofday(&t, NULL); + len--; // min a ']' char + snprintf((char*)&content[len], sizeof(content) - len, ".%03d]", (int)(t.tv_usec / 1000)); + len += MS_TIMESTAMP_LEN; + + va_start(vl_list, msg); + vsnprintf((char *)&content[len], sizeof(content) - len - 1, (const char *)msg, vl_list); + va_end(vl_list); + + printf("%s", content); + + return 0; +} + void signalHandler(int signo) { switch(signo) { case SIGALRM: timer_ticks(); - //printf("Caught the SIGALRM signal!\n"); + //printf_timestamp("Caught the SIGALRM signal every 1ms !\n"); break; } } @@ -17,36 +54,39 @@ void signalHandler(int signo) struct Timer timer1; struct Timer timer2; -void timer1_callback() +void timer1_callback(void *arg) { - printf("timer1 timeout!\r\n"); + printf_timestamp("timer1 timeout! arg: %p\r\n", arg); } -void timer2_callback() +void timer2_callback(void *arg) { - printf("timer2 timeout!\r\n"); + printf_timestamp("timer2 timeout! arg: %p\r\n", arg); } int main(void) { + printf_timestamp("%s start ...\r\n", __func__); + signal(SIGALRM, signalHandler); struct itimerval new_value, old_value; - new_value.it_value.tv_sec = 1; - new_value.it_value.tv_usec = 0; + new_value.it_value.tv_sec = 0; // should be 0x00 !!! + new_value.it_value.tv_usec = 1; // non-zero is OK !!! new_value.it_interval.tv_sec = 0; - new_value.it_interval.tv_usec = 1000; + new_value.it_interval.tv_usec = 1000 * CFG_TIMER_1_TICK_N_MS; // 1ms setitimer(ITIMER_REAL, &new_value, &old_value); - timer_init(&timer1, timer1_callback, 4000, 1000); // start timer after 4s + timer_init(&timer1, timer1_callback, 4000, 1000, &timer1); // start timer after 4s timer_start(&timer1); - timer_init(&timer2, timer2_callback, 0, 2000); + timer_init(&timer2, timer2_callback, 0, 5000, &timer2); // start timer right now timer_start(&timer2); - while(1) - { + while(1) { + /* Maybe some sleep time is needed to avoid running CPU all the time. */ timer_loop(); } + return 0; } diff --git a/makefile b/makefile deleted file mode 100644 index aa54ccd..0000000 --- a/makefile +++ /dev/null @@ -1,31 +0,0 @@ - -CC = gcc - -CFLGAS += -std=c99 -CFLAGS += -D TEST -CFLAGS += -g - -OBJ_PATH = . -BIN_PATH = . -SRC_PATH = . - -IDIR = . -INC += -I$(IDIR) - -TARGET = $(BIN_PATH)/test - -C_SRCS += ./examples/test_linux.c -C_SRCS += ./multi_timer.c -OBJ := $(patsubst %.c,%.o,$(filter %.c,$(C_SRCS))) - - -$(TARGET) : $(OBJ_PATH)/$(OBJ) - $(CC) $(INC) $(CFLAGS) $(OBJ) -o $(TARGET) - -$(OBJ): %.o : %.c - $(CC) $(INC) $(CFLAGS) -c $< -o $@ - -.PHONY : clean -clean: - @-rm *.exe - @-rm *.o diff --git a/multi_timer.c b/multi_timer.c old mode 100644 new mode 100755 index 8b48f2c..334d643 --- a/multi_timer.c +++ b/multi_timer.c @@ -3,27 +3,37 @@ * All rights reserved */ +#include #include "multi_timer.h" //timer handle list head. static struct Timer* head_handle = NULL; //Timer ticks +//static uint32_t _timer_ticks = (1 << 32)- 1000; // only for test tick clock overflow static uint32_t _timer_ticks = 0; /** * @brief Initializes the timer struct handle. * @param handle: the timer handle strcut. * @param timeout_cb: timeout callback. + * @param timeout: delay to start the timer. * @param repeat: repeat interval time. + * @param arg: the input argument for timeout_cb fucntion. * @retval None */ -void timer_init(struct Timer* handle, void (*timeout_cb)(), uint32_t timeout, uint32_t repeat) +void timer_init(struct Timer* handle, void (*timeout_cb)(void *arg), \ + uint32_t timeout, uint32_t repeat, void *arg) { // memset(handle, sizeof(struct Timer), 0); - handle->timeout_cb = timeout_cb; - handle->timeout = _timer_ticks + timeout; - handle->repeat = repeat; + handle->timeout_cb = timeout_cb; + handle->timeout = timeout; + handle->repeat = repeat; + handle->cur_ticks = _timer_ticks; + handle->cur_expired_time = handle->timeout; + handle->arg = arg; + //printf("cur_ticks: %u, cur_expired_time: %u, _timer_ticks: %u, timeout: %u\r\n", + // handle->cur_ticks, handle->cur_expired_time, _timer_ticks, timeout); } /** @@ -34,36 +44,40 @@ void timer_init(struct Timer* handle, void (*timeout_cb)(), uint32_t timeout, ui int timer_start(struct Timer* handle) { struct Timer* target = head_handle; - while(target) - { - if(target == handle) + + while(target) { + if(target == handle) { return -1; //already exist. + } target = target->next; } handle->next = head_handle; head_handle = handle; + return 0; } /** * @brief Stop the timer work, remove the handle off work list. * @param handle: target handle strcut. - * @retval None + * @retval 0: succeed. -1: timer not exist. */ -void timer_stop(struct Timer* handle) +int timer_stop(struct Timer* handle) { struct Timer** curr; - for(curr = &head_handle; *curr;) - { + + for(curr = &head_handle; *curr;) { struct Timer* entry = *curr; - if(entry == handle) - { + if(entry == handle) { *curr = entry->next; - // free(entry); - } - else + //free(entry); + return 0; // found specified timer + } else { curr = &entry->next; + } } + + return 0; } /** @@ -71,33 +85,34 @@ void timer_stop(struct Timer* handle) * @param None. * @retval None */ -void timer_loop() +void timer_loop(void) { struct Timer* target; - for(target = head_handle; target; target = target->next) - { - if(_timer_ticks >= target->timeout) - { - if(target->repeat == 0) - { + + for(target = head_handle; target; target = target->next) { + /* + More detail on tick-clock overflow, please see https://blog.csdn.net/szullc/article/details/115332326 + */ + if(_timer_ticks - target->cur_ticks >= target->cur_expired_time) { + //printf("cur_ticks: %u, cur_expired_time: %u, _timer_ticks: %u\r\n", + // target->cur_ticks, target->cur_expired_time, _timer_ticks); + if(target->repeat == 0) { timer_stop(target); - } - else - { - target->timeout = _timer_ticks + target->repeat; - } - target->timeout_cb(); + } else { + target->cur_ticks = _timer_ticks; + target->cur_expired_time = target->repeat; + } + target->timeout_cb(target->arg); } } } /** - * @brief background ticks, timer repeat invoking interval 1ms. + * @brief background ticks, timer repeat invoking interval nms. * @param None. * @retval None. */ -void timer_ticks() +void timer_ticks(void) { - _timer_ticks++; + _timer_ticks += CFG_TIMER_1_TICK_N_MS; } - diff --git a/multi_timer.h b/multi_timer.h old mode 100644 new mode 100755 index edc3e38..cb3e533 --- a/multi_timer.h +++ b/multi_timer.h @@ -6,28 +6,68 @@ #ifndef _MULTI_TIMER_H_ #define _MULTI_TIMER_H_ -#include "stdint.h" -#include "stddef.h" +#include +#include + +/* +It means 1 tick for 1ms. +Your can configurate for your tick time such as 5ms/10ms and so on. +*/ +#define CFG_TIMER_1_TICK_N_MS 1 typedef struct Timer { - uint32_t timeout; - uint32_t repeat; - void (*timeout_cb)(void); - struct Timer* next; -}Timer; + uint32_t cur_ticks; /* Record current timer start tick */ + uint32_t cur_expired_time; /* Record current timer expired time */ + uint32_t timeout; /* Delay (xx ms) time to start tiemr */ + uint32_t repeat; /* Timer interval expired time (xx ms) */ + void * arg; /* Input argument for timeout_cb function */ + void (*timeout_cb)(void *arg); /* Timer expired callback function */ + struct Timer* next; /* Pointer to next timer */ +} Timer; #ifdef __cplusplus extern "C" { #endif -void timer_init(struct Timer* handle, void(*timeout_cb)(), uint32_t timeout, uint32_t repeat); +/** + * @brief Initializes the timer struct handle. + * @param handle: the timer handle strcut. + * @param timeout_cb: timeout callback. + * @param timeout: delay to start the timer. + * @param repeat: repeat interval time. + * @param arg: the input argument for timeout_cb fucntion. + * @retval None + */ +void timer_init(struct Timer* handle, void(*timeout_cb)(void *arg), \ + uint32_t timeout, uint32_t repeat, void *arg); + +/** + * @brief Start the timer work, add the handle into work list. + * @param btn: target handle strcut. + * @retval 0: succeed. -1: already exist. + */ int timer_start(struct Timer* handle); -void timer_stop(struct Timer* handle); + +/** + * @brief Stop the timer work, remove the handle off work list. + * @param handle: target handle strcut. + * @retval 0: succeed. -1: timer not exist. + */ +int timer_stop(struct Timer* handle); + +/** + * @brief background ticks, timer repeat invoking interval nms. + * @param None. + * @retval None. + */ void timer_ticks(void); -void timer_loop(void); -// void timer_again(struct Timer* handle); -// void timer_set_repeat(struct Timer* handle, uint32_t repeat); +/** + * @brief main loop. + * @param None. + * @retval None + */ +void timer_loop(void); #ifdef __cplusplus }