├── .gitignore ├── .gitlab-ci.yml ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── README.md ├── src ├── stimer.c └── stimer.h └── test ├── CMakeLists.txt └── test_stimer.c /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | stages: 2 | - build 3 | - test 4 | 5 | image: odurc/c-dev-tools 6 | 7 | variables: 8 | GIT_SUBMODULE_STRATEGY: recursive 9 | BUILD_DIR: "build" 10 | CTEST_OUTPUT_ON_FAILURE: "True" 11 | 12 | build: 13 | stage: build 14 | variables: 15 | BUILD_TYPE: Debug 16 | BUILD_OPTIONS: "-DENABLE_TESTS=Yes -DENABLE_LINTER=Yes -DENABLE_COVERAGE=Yes" 17 | script: 18 | - mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR} 19 | - cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} ${BUILD_OPTIONS} .. 20 | - make -j$(nproc) 21 | artifacts: 22 | expire_in: 1 day 23 | paths: 24 | - ${BUILD_DIR} 25 | 26 | coverage: 27 | stage: test 28 | script: 29 | - cd ${BUILD_DIR} 30 | - make test 31 | - make coverage 32 | - mv coverage .. 33 | coverage: '/Percentage Coverage: (\d+.\d+%)$/' 34 | artifacts: 35 | reports: 36 | coverage_report: 37 | coverage_format: cobertura 38 | path: coverage/cobertura.xml 39 | 40 | memcheck: 41 | stage: test 42 | script: 43 | - cd ${BUILD_DIR} 44 | - make test 45 | - ctest -T memcheck 46 | 47 | pages: 48 | script: 49 | - make doc -C ${BUILD_DIR} 50 | - mv ${BUILD_DIR}/doc/html/ public/ 51 | artifacts: 52 | paths: 53 | - public 54 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "cmake"] 2 | path = cmake 3 | url = https://gitlab.com/odurc/cmake-modules.git 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.14) 2 | 3 | # project definitions 4 | project(stimer VERSION 1.3.0 5 | DESCRIPTION "Simple Timer Library" 6 | LANGUAGES C) 7 | set(CMAKE_C_STANDARD 99) 8 | 9 | # add local cmake modules to be loaded by include() 10 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 11 | 12 | # add options 13 | option(ENABLE_TESTS "Build and run tests" OFF) 14 | option(ENABLE_LINTER "Enable linter - static code check" OFF) 15 | option(ENABLE_COVERAGE "Enable coverage" OFF) 16 | option(GENERATE_DOC "Generate documentation" OFF) 17 | 18 | # define default build type 19 | if (NOT CMAKE_BUILD_TYPE) 20 | set(CMAKE_BUILD_TYPE "Release") 21 | endif(NOT CMAKE_BUILD_TYPE) 22 | 23 | # add default compile options 24 | add_compile_options( 25 | -Wall # enable all warnings 26 | -ffunction-sections # create separate section for functions 27 | -fdata-sections # create separate section for data 28 | ) 29 | 30 | # ffunction-sections and fdata-sections allow later the linker to remove all unreferenced 31 | # functions and data, see: https://gcc.gnu.org/onlinedocs/gnat_ugn/Compilation-options.html 32 | 33 | # add compile options for debug 34 | add_compile_options( 35 | $<$:-O0> # no optimization 36 | $<$:-DDEBUG> # define DEBUG macro 37 | ) 38 | 39 | # add compile options for release 40 | add_compile_options( 41 | $<$:-O3> # enable high-level of optimization 42 | ) 43 | 44 | # add default link options 45 | add_link_options( 46 | -Wl,--gc-sections # remove unreferenced sections 47 | ) 48 | 49 | # add link options for release 50 | add_link_options( 51 | $<$:-Wl,-s> # strip all symbols 52 | ) 53 | 54 | # add coverage flag for compiling and linking 55 | # see: https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html 56 | if(ENABLE_COVERAGE) 57 | message(STATUS "Coverage enabled") 58 | add_compile_options(--coverage) 59 | add_link_options(--coverage) 60 | 61 | # coverage depends on unit tests 62 | set(ENABLE_TESTS Yes) 63 | 64 | # create target for coverage (i.e.: `make coverage`) 65 | set(GCOVR_OUT_DIR coverage) 66 | set(GCOVR_HTML_OUT ${GCOVR_OUT_DIR}/coverage.html) 67 | set(GCOVR_XML_OUT ${GCOVR_OUT_DIR}/cobertura.xml) 68 | set(GCOVR_FILTER ${PROJECT_SOURCE_DIR}/src) 69 | add_custom_target(coverage 70 | COMMAND rm -rf ${GCOVR_OUT_DIR} && mkdir -p ${GCOVR_OUT_DIR} 71 | COMMAND gcovr --html-details ${GCOVR_HTML_OUT} --xml-pretty -x ${GCOVR_XML_OUT} 72 | --filter ${GCOVR_FILTER} --root ${CMAKE_CURRENT_BINARY_DIR} 73 | COMMAND ctest -T coverage 74 | COMMENT "Generating coverage summary") 75 | endif() 76 | 77 | # source code 78 | file(GLOB SRC "src/*.c") 79 | 80 | # build static library 81 | set(STIMER_LIBRARY_NAME ${CMAKE_PROJECT_NAME}) 82 | add_library(${STIMER_LIBRARY_NAME} STATIC ${SRC}) 83 | 84 | # set up doxygen and create command to generate documentation 85 | find_package(Doxygen REQUIRED) 86 | if(DOXYGEN_FOUND) 87 | set(DOXYGEN_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc) 88 | set(DOXYGEN_USE_MDFILE_AS_MAINPAGE ${CMAKE_CURRENT_SOURCE_DIR}/README.md) 89 | set(DOXYGEN_JAVADOC_AUTOBRIEF YES) 90 | set(DOXYGEN_OPTIMIZE_OUTPUT_FOR_C YES) 91 | set(DOXYGEN_GENERATE_LATEX NO) 92 | 93 | doxygen_add_docs(doc ${CMAKE_CURRENT_SOURCE_DIR}/README.md ${CMAKE_CURRENT_SOURCE_DIR}/src 94 | COMMENT "Generating API documentation with Doxygen") 95 | 96 | # build documentation together with source code in case GENERATE_DOC is set by the user 97 | if(GENERATE_DOC) 98 | # add `doc` target as dependency of the project target to force build 99 | add_dependencies(${CMAKE_PROJECT_NAME} doc) 100 | endif() 101 | elseif(GENERATE_DOC) 102 | message(WARNING "Doxygen was not found, cannot generate documentation") 103 | endif() 104 | 105 | include(CTest) 106 | 107 | # configure valgrind options (overwrite default) 108 | set(VALGRIND_COMMAND_OPTIONS "--leak-check=full --show-leak-kinds=all --error-exitcode=1") 109 | 110 | # configure linter 111 | if(ENABLE_LINTER) 112 | find_program(CLANG_TIDY clang-tidy) 113 | if (CLANG_TIDY) 114 | message(STATUS "Clang-Tidy enabled") 115 | set(CMAKE_C_CLANG_TIDY clang-tidy;) 116 | endif() 117 | 118 | find_program(CPPCHECK cppcheck) 119 | if (CPPCHECK) 120 | message(STATUS "Cppcheck enabled") 121 | set(CMAKE_C_CPPCHECK cppcheck;) 122 | endif() 123 | endif() 124 | 125 | # unit testing 126 | if (BUILD_TESTING AND ENABLE_TESTS) 127 | message(STATUS "Tests enabled") 128 | find_package(CMocka REQUIRED) 129 | include(AddCMockaTest) 130 | include(AddMockedTest) 131 | add_subdirectory(test) 132 | endif() 133 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Ricardo Crudo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | stimer 2 | ====== 3 | 4 | This is a simple software timer library implemented in C. It is designed to use only static memory 5 | allocation which makes its use interesting for microcontrollers. The goal of the library is to offer 6 | an easy way to create and manage multiple time events for reference usage, it's not designed to 7 | provide accurate time counting. 8 | 9 | 10 | Features 11 | --- 12 | 13 | * oneshot and loop timer modes 14 | * optional callback function for time overflow 15 | * no external dependency 16 | * static memory allocation 17 | * easy to use and setup 18 | 19 | 20 | How to install 21 | --- 22 | 23 | There is no installation, simply copy the content of `src` directory to your work directory and 24 | adjust your build file or IDE as necessary. 25 | 26 | 27 | How to use 28 | --- 29 | 30 | The function `stimer_tick()` must be called from an interrupt service routine (ISR). The ISR 31 | provides a clock tick for the library, the minimum increment of time it will be able to count. 32 | Ideally, your implementation should use a hardware timer ISR exclusively for this purpose to prevent 33 | time overhead processing secondary code. 34 | 35 | :warning: Be careful when using callback functions since `stimer_tick()` is called from an ISR the 36 | callback will also called from the same ISR. Read more on the API docs. 37 | 38 | To see more details on how to use the library, please check the online [API documentation][]. 39 | 40 | Configuration 41 | --- 42 | 43 | The configuration of the library is done by setting 'define' macros in the header file, 44 | under the configuration section. 45 | 46 | There are two configuration macros that can be tweaked: *STIMER_TICK_PERIOD* and 47 | *STIMER_MAX_INSTANCES*. The macro *STIMER_TICK_PERIOD* must be set to the same period of the ISR 48 | used for `stimer_tick()`. On the other hand, *STIMER_MAX_INSTANCES* macro should be adjusted to the 49 | amount of timers used in your implementation. 50 | 51 | 52 | License 53 | --- 54 | 55 | MIT 56 | 57 | [API documentation]: https://odurc.gitlab.io/stimer 58 | -------------------------------------------------------------------------------- /src/stimer.c: -------------------------------------------------------------------------------- 1 | /* 2 | * stimer - Simple Timer Library 3 | * https://gitlab.com/odurc/stimer 4 | * 5 | * Copyright (c) 2022 Ricardo Crudo 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | /* 27 | **************************************************************************************************** 28 | * INCLUDE FILES 29 | **************************************************************************************************** 30 | */ 31 | 32 | #include "stimer.h" 33 | 34 | 35 | /* 36 | **************************************************************************************************** 37 | * INTERNAL MACROS 38 | **************************************************************************************************** 39 | */ 40 | 41 | 42 | /* 43 | **************************************************************************************************** 44 | * INTERNAL CONSTANTS 45 | **************************************************************************************************** 46 | */ 47 | 48 | 49 | /* 50 | **************************************************************************************************** 51 | * INTERNAL DATA TYPES 52 | **************************************************************************************************** 53 | */ 54 | 55 | struct stimer_t { 56 | uint32_t time, counter; 57 | uint8_t overflow; 58 | int enabled; 59 | stimer_mode_t mode; 60 | void (*callback)(void *arg); 61 | void *arg; 62 | }; 63 | 64 | 65 | /* 66 | **************************************************************************************************** 67 | * INTERNAL GLOBAL VARIABLES 68 | **************************************************************************************************** 69 | */ 70 | 71 | stimer_t g_timers[STIMER_MAX_INSTANCES]; 72 | 73 | 74 | /* 75 | **************************************************************************************************** 76 | * INTERNAL FUNCTIONS 77 | **************************************************************************************************** 78 | */ 79 | 80 | static inline stimer_t* stimer_take(void) 81 | { 82 | static unsigned int stimer_counter; 83 | 84 | // first time timers are requested 85 | if (stimer_counter < STIMER_MAX_INSTANCES) 86 | { 87 | stimer_t *timer = &g_timers[stimer_counter++]; 88 | return timer; 89 | } 90 | 91 | // iterate all array searching for a free spot 92 | // a timer is considered free when enabled is negative 93 | for (int i = 0; i < STIMER_MAX_INSTANCES; i++) 94 | { 95 | stimer_t *timer = &g_timers[i]; 96 | if (timer->enabled < 0) 97 | return timer; 98 | } 99 | 100 | return 0; 101 | } 102 | 103 | static inline void stimer_give(stimer_t *timer) 104 | { 105 | if (timer) 106 | timer->enabled = -1; 107 | } 108 | 109 | 110 | /* 111 | **************************************************************************************************** 112 | * GLOBAL FUNCTIONS 113 | **************************************************************************************************** 114 | */ 115 | 116 | stimer_t* stimer_create(stimer_mode_t mode, void (*callback)(void *arg)) 117 | { 118 | stimer_t *timer = stimer_take(); 119 | 120 | if (timer) 121 | { 122 | timer->enabled = 0; 123 | timer->overflow = 0; 124 | timer->mode = mode; 125 | timer->callback = callback; 126 | timer->arg = timer; 127 | } 128 | 129 | return timer; 130 | } 131 | 132 | void stimer_destroy(stimer_t *timer) 133 | { 134 | stimer_give(timer); 135 | } 136 | 137 | void stimer_set_time(stimer_t *timer, uint32_t time_ms) 138 | { 139 | timer->time = time_ms; 140 | timer->counter = (time_ms * 1000) / STIMER_TICK_PERIOD; 141 | } 142 | 143 | void stimer_start(stimer_t *timer) 144 | { 145 | timer->enabled = 1; 146 | } 147 | 148 | void stimer_stop(stimer_t *timer) 149 | { 150 | timer->enabled = 0; 151 | } 152 | 153 | void stimer_reset(stimer_t *timer) 154 | { 155 | timer->enabled = 0; 156 | timer->overflow = 0; 157 | stimer_set_time(timer, timer->time); 158 | } 159 | 160 | uint8_t stimer_overflow(stimer_t *timer) 161 | { 162 | uint8_t overflow = timer->overflow; 163 | timer->overflow = 0; 164 | 165 | // disable and reload timer 166 | if (timer->mode == STIMER_ONE_SHOT && overflow) 167 | { 168 | timer->enabled = 0; 169 | stimer_set_time(timer, timer->time); 170 | } 171 | 172 | return overflow; 173 | } 174 | 175 | void stimer_argument(stimer_t *timer, void *arg) 176 | { 177 | timer->arg = arg ? arg : timer; 178 | } 179 | 180 | void stimer_tick(void) 181 | { 182 | for (int i = 0; i < STIMER_MAX_INSTANCES; i++) 183 | { 184 | stimer_t *timer = &g_timers[i]; 185 | 186 | if (timer->enabled != 1) 187 | continue; 188 | 189 | if (timer->counter > 0) 190 | timer->counter--; 191 | 192 | if (timer->counter == 0) 193 | { 194 | if (timer->overflow < UINT8_MAX) 195 | timer->overflow++; 196 | 197 | if (timer->callback) 198 | { 199 | if (timer->mode == STIMER_ONE_SHOT) 200 | timer->enabled = 0; 201 | 202 | timer->callback(timer->arg); 203 | timer->overflow = 0; 204 | } 205 | 206 | // always reload the timer 207 | stimer_set_time(timer, timer->time); 208 | } 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /src/stimer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * stimer - Simple Timer Library 3 | * https://gitlab.com/odurc/stimer 4 | * 5 | * Copyright (c) 2022 Ricardo Crudo 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | /** 27 | * @file stimer.h 28 | * @author Ricardo Crudo 29 | * @brief Simple Timer Library 30 | */ 31 | 32 | #ifndef STIMER_H 33 | #define STIMER_H 34 | 35 | #ifdef __cplusplus 36 | extern "C" 37 | { 38 | #endif 39 | 40 | 41 | /* 42 | **************************************************************************************************** 43 | * INCLUDE FILES 44 | **************************************************************************************************** 45 | */ 46 | 47 | #include 48 | 49 | 50 | /* 51 | **************************************************************************************************** 52 | * MACROS 53 | **************************************************************************************************** 54 | */ 55 | 56 | // library version 57 | #define STIMER_VERSION "1.2.1" 58 | 59 | // macro to set no callback mode 60 | #define STIMER_NO_CALLBACK 0 61 | 62 | // macros to convert time units 63 | #define STIMER_SEC_TO_MS(t) ((uint32_t)(t)*(uint32_t)1000) 64 | #define STIMER_MIN_TO_MS(t) ((uint32_t)(t)*(uint32_t)60000) 65 | #define STIMER_HOUR_TO_MS(t) ((uint32_t)(t)*(uint32_t)3600000) 66 | 67 | 68 | /* 69 | **************************************************************************************************** 70 | * CONFIGURATION 71 | **************************************************************************************************** 72 | */ 73 | 74 | /** @def STIMER_TICK_PERIOD 75 | * Define the tick period in us 76 | */ 77 | #define STIMER_TICK_PERIOD 1000 78 | 79 | /** @def STIMER_MAX_INSTANCES 80 | * Define the maximum of timers instances 81 | */ 82 | #define STIMER_MAX_INSTANCES 10 83 | 84 | 85 | /* 86 | **************************************************************************************************** 87 | * DATA TYPES 88 | **************************************************************************************************** 89 | */ 90 | 91 | /** 92 | * @struct stimer_t 93 | * An opaque structure of a stimer object 94 | */ 95 | typedef struct stimer_t stimer_t; 96 | 97 | /** 98 | * @enum stimer_mode_t 99 | * Timer operating modes 100 | */ 101 | typedef enum stimer_mode_t { 102 | STIMER_ONE_SHOT, ///< one shot mode (stop after first overflow) 103 | STIMER_LOOP ///< loop mode (keep running after overflow) 104 | } stimer_mode_t; 105 | 106 | 107 | /* 108 | **************************************************************************************************** 109 | * FUNCTION PROTOTYPES 110 | **************************************************************************************************** 111 | */ 112 | 113 | /** 114 | * @defgroup stimer_funcs Timer Functions 115 | * Set of functions to operate timers. 116 | * @{ 117 | */ 118 | 119 | /** 120 | * Create stimer object 121 | * 122 | * When creating a timer the mode must be passed as the first argument and it must be one of the 123 | * values defined by stimer_mode_t enumeration. The second argument must be either a function 124 | * pointer or the macro STIMER_NO_CALLBACK. In case a callback function is provided, it will be 125 | * called when the timer overflow happens. Timers without callback only stop counting either after 126 | * stimer_stop() or stimer_overflow() is called. 127 | * 128 | * By default the callback provides the timer object as argument. A custom argument can be defined 129 | * using the function stimer_argument(). Be careful using callback functions since `stimer_tick()` 130 | * is called from an ISR the callback will also called from the same ISR. If the provided callback 131 | * function blocks or lingers to finish this will affect the timer counting and possibly create 132 | * undesired behavior in your software. 133 | * 134 | * @param[in] mode must be either STIMER_ONE_SHOT or STIMER_LOOP 135 | * @param[in] callback a function callback pointer or the macro STIMER_NO_CALLBACK 136 | * 137 | * @return pointer to stimer object or NULL if no more timers are available 138 | */ 139 | stimer_t* stimer_create(stimer_mode_t mode, void (*callback)(void *arg)); 140 | 141 | /** 142 | * Destroy stimer object 143 | * 144 | * @param[in] timer stimer object pointer 145 | */ 146 | void stimer_destroy(stimer_t *timer); 147 | 148 | /** 149 | * Set the timer time 150 | * 151 | * Use this function, after create stimer object, to set the time which the timer 152 | * must overflow. The time parameter is expected to be in milliseconds. The time 153 | * conversion macros can be used to convert from hour, minute or second. 154 | * 155 | * @param[in] timer stimer object pointer 156 | * @param[in] time_ms time in milliseconds 157 | */ 158 | void stimer_set_time(stimer_t *timer, uint32_t time_ms); 159 | 160 | /** 161 | * Start the timer 162 | * 163 | * This function also restarts a stopped timer. 164 | * 165 | * @param[in] timer stimer object pointer 166 | */ 167 | void stimer_start(stimer_t *timer); 168 | 169 | /** 170 | * Stop the timer 171 | * 172 | * The timer counter is NOT reseted when stopped. The counting will continue from 173 | * the last value when the timer is started again. 174 | * 175 | * @param[in] timer stimer object pointer 176 | */ 177 | void stimer_stop(stimer_t *timer); 178 | 179 | /** 180 | * Reset timer 181 | * 182 | * The timer is automatically stopped when reseted. 183 | * 184 | * @param[in] timer stimer object pointer 185 | */ 186 | void stimer_reset(stimer_t *timer); 187 | 188 | /** 189 | * Check timer overflow 190 | * 191 | * Note that once this function is called the overflow counter is zeroed. This is, 192 | * a second subsequent call will return zero. This function may also be called from the 193 | * callback function in case it is defined. This function is not thread safe. 194 | * 195 | * @param[in] timer stimer object pointer 196 | * 197 | * @return number of times the timer overflow 198 | */ 199 | uint8_t stimer_overflow(stimer_t *timer); 200 | 201 | /** 202 | * Set the timer argument 203 | * 204 | * This function is only useful when a callback is provided during the timer creation. 205 | * In case a custom argument is required when the callback is evoked, this function has 206 | * to be used to replace the original argument, which is the timer object itself. 207 | * 208 | * @param[in] timer stimer object pointer 209 | * @param[in] arg the custom argument of the callback function 210 | */ 211 | void stimer_argument(stimer_t *timer, void *arg); 212 | 213 | /** 214 | * The tick function 215 | * 216 | * This function must be used to define the clock of the timers. It must be called 217 | * from an interrupt service routine (ISR). The period of the interruption must be set 218 | * using the STIMER_TICK_PERIOD macro. 219 | */ 220 | void stimer_tick(void); 221 | 222 | /** 223 | * @} 224 | */ 225 | 226 | /* 227 | **************************************************************************************************** 228 | * CONFIGURATION ERRORS 229 | **************************************************************************************************** 230 | */ 231 | 232 | #if !defined(STIMER_MAX_INSTANCES) || !defined(STIMER_TICK_PERIOD) 233 | #error "STIMER_MAX_INSTANCES and STIMER_TICK_PERIOD macros must be defined" 234 | #endif 235 | 236 | #if STIMER_TICK_PERIOD <= 0 || STIMER_TICK_PERIOD > 1000 237 | #error "STIMER_TICK_PERIOD macro value must be set between 1 and 1000" 238 | #endif 239 | 240 | #ifdef __cplusplus 241 | } 242 | #endif 243 | 244 | // STIMER_H 245 | #endif 246 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(Threads REQUIRED) 2 | add_mocked_test(stimer LINK_LIBRARIES ${STIMER_LIBRARY_NAME} Threads::Threads) 3 | -------------------------------------------------------------------------------- /test/test_stimer.c: -------------------------------------------------------------------------------- 1 | /* 2 | **************************************************************************************************** 3 | * INCLUDE FILES 4 | **************************************************************************************************** 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "stimer.h" 17 | 18 | /* 19 | **************************************************************************************************** 20 | * INTERNAL MACROS 21 | **************************************************************************************************** 22 | */ 23 | 24 | 25 | /* 26 | **************************************************************************************************** 27 | * INTERNAL CONSTANTS 28 | **************************************************************************************************** 29 | */ 30 | 31 | 32 | /* 33 | **************************************************************************************************** 34 | * INTERNAL DATA TYPES 35 | **************************************************************************************************** 36 | */ 37 | 38 | typedef struct { 39 | stimer_t *timer; 40 | bool callback_flag; 41 | pthread_t tick_thread; 42 | bool running; 43 | } test_state_t; 44 | 45 | /* 46 | **************************************************************************************************** 47 | * INTERNAL GLOBAL VARIABLES 48 | **************************************************************************************************** 49 | */ 50 | 51 | 52 | /* 53 | **************************************************************************************************** 54 | * INTERNAL FUNCTIONS 55 | **************************************************************************************************** 56 | */ 57 | 58 | static void* timer_tick_thread(void *arg) 59 | { 60 | test_state_t *test_state = arg; 61 | test_state->running = true; 62 | 63 | while (usleep(1000) == 0 && test_state->running) 64 | { 65 | stimer_tick(); 66 | } 67 | 68 | return 0; 69 | } 70 | 71 | static void process_timer_event(void *arg) 72 | { 73 | test_state_t *data = arg; 74 | 75 | assert_int_equal(stimer_overflow(data->timer), 1); 76 | stimer_reset(data->timer); 77 | 78 | assert_false(data->callback_flag); 79 | data->callback_flag = true; 80 | } 81 | 82 | // this run once before all group tests 83 | static int group_setup(void **state) 84 | { 85 | test_state_t *test_state = calloc(1, sizeof(test_state_t)); 86 | *state = test_state; 87 | 88 | // create a thread to use as timer tick 89 | int ret = pthread_create(&test_state->tick_thread, NULL, timer_tick_thread, test_state); 90 | assert_int_equal(ret, 0); 91 | 92 | return 0; 93 | } 94 | 95 | // this run once after all group tests 96 | static int group_teardown(void **state) 97 | { 98 | test_state_t *test_state = *state; 99 | test_state->running = false; 100 | pthread_join(test_state->tick_thread, NULL); 101 | free(test_state); 102 | 103 | return 0; 104 | } 105 | 106 | static void test_timer_create(void **state) 107 | { 108 | test_state_t *test_state = *state; 109 | stimer_t *timers[STIMER_MAX_INSTANCES]; 110 | 111 | // create as many timers as possible 112 | for (int i = 0; i < STIMER_MAX_INSTANCES; i++) 113 | { 114 | timers[i] = stimer_create(STIMER_ONE_SHOT, STIMER_NO_CALLBACK); 115 | assert_non_null(timers[i]); 116 | } 117 | 118 | // should return null as all timers have been used 119 | assert_null(stimer_create(STIMER_ONE_SHOT, STIMER_NO_CALLBACK)); 120 | 121 | // destroy all created timers 122 | for (int i = 0; i < STIMER_MAX_INSTANCES; i++) 123 | { 124 | stimer_destroy(timers[i]); 125 | } 126 | 127 | // create a new timer again 128 | test_state->timer = stimer_create(STIMER_LOOP, STIMER_NO_CALLBACK); 129 | assert_non_null(test_state->timer); 130 | } 131 | 132 | static void test_timer_100ms(void **state) 133 | { 134 | test_state_t *test_state = *state; 135 | stimer_t *timer = test_state->timer; 136 | 137 | const int period_ms = 50; 138 | stimer_set_time(timer, period_ms); 139 | stimer_start(timer); 140 | 141 | // timer must not overflow as it was just created 142 | assert_int_equal(stimer_overflow(timer), 0); 143 | 144 | // wait time enough until timer overflows 145 | usleep(period_ms * 1000 * 1.15); 146 | 147 | // timer should overflow just once 148 | assert_int_equal(stimer_overflow(timer), 1); 149 | 150 | stimer_stop(timer); 151 | } 152 | 153 | static void test_timer_callback(void **state) 154 | { 155 | test_state_t *test_state = *state; 156 | stimer_t *timer = test_state->timer; 157 | test_state->callback_flag = false; 158 | 159 | // destroy previous timer and create a new one with callback 160 | stimer_destroy(timer); 161 | timer = stimer_create(STIMER_ONE_SHOT, process_timer_event); 162 | assert_non_null(timer); 163 | 164 | // add custom argument to timer 165 | stimer_argument(timer, test_state); 166 | 167 | stimer_set_time(timer, 10); 168 | stimer_start(timer); 169 | usleep(50000); 170 | 171 | // callback flag shall be set to true in case it worked 172 | assert_true(test_state->callback_flag); 173 | } 174 | 175 | 176 | /* 177 | **************************************************************************************************** 178 | * MAIN FUNCTION 179 | **************************************************************************************************** 180 | */ 181 | 182 | int main(void) 183 | { 184 | const struct CMUnitTest tests[] = { 185 | cmocka_unit_test(test_timer_create), 186 | cmocka_unit_test(test_timer_100ms), 187 | cmocka_unit_test(test_timer_callback), 188 | }; 189 | 190 | return cmocka_run_group_tests(tests, group_setup, group_teardown); 191 | } 192 | --------------------------------------------------------------------------------