1 概述
在單片機系統中,會有幾個硬件定時器。在程序須要延時或者定時功能的時候須要使用定時器,可是在整個單片機裏面定時器的數量是有限的。不可能每個功能都去使用一個硬件定時器。對於時間要求不是很嚴格的場合,能夠多個應用層功能共享使用一個定時器。因此這篇文章是基於一個硬件定時器寫的一個軟件定時器框架,能夠移植到任何帶有32位,16位,8位定時器的單片機中。本程序在GD32F350上面已經測試完成。有須要的朋友們能夠移植到本身的平臺上。這個定時器能夠用在低功耗場景下(不能頻繁中斷),定時器不會頻繁產生中斷,根據具體時間計算產生中斷的間隔。不會大量佔用CPU資源。git
能夠在gitee上下載源碼: https://gitee.com/huohongpeng/software-timerredis
2 定時器實現
soft_timer.h框架
#ifdef __cplusplus extern "C" { #endif #ifndef __SOFT_TIMER_H__ #define __SOFT_TIMER_H__ #include <stdint.h> typedef void (*sw_func_cb_t)(void *cb_data); #define SW_TIMER_HANDLE_MAGIC (0xdcba0000) /* * Step1: Call sw_timer_alloc() to allocate a software timer handle. * Step2: Call sw_timer_start() to register the callback function and start the timer. * Step3: Call sw_timer_stop() to stop timer , and you can call sw_timer_start to restart * the timer. * Step4: Call sw_timer_free() to free the allocated timer if it's no longer in use. */ int sw_timer_alloc(uint32_t *handle); int sw_timer_free(uint32_t handle); int sw_timer_start(uint32_t handle, uint32_t timeout_ms, sw_func_cb_t func, void *cb_data, uint8_t repeat); int sw_timer_stop(uint32_t handle); /* * Call by hardware timer interrupt server */ void sw_timer_timeout_irq(void); #endif #ifdef __cplusplus } #endif
soft_timer.c測試
/* * Copyright (C) 2020, 2020 huohongpeng * Author: huohongpeng <1045338804@qq.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Change logs: * Date Author Notes * 2020-09-18 huohongpeng First add * */ #ifdef __cplusplus extern "C" { #endif #include "soft_timer.h" #include <string.h> #include <platform_usart.h> /* * Software timer max number * User to configurate [1-65535] */ #define SW_TIMER_MAX_NUMBER 100 /* * Hardware timer max count value * Note : depend on hardware platform, user to overwrite */ #define __HW_TIME_MAX_COUNT (0xffff) /* * Hardware timer how many count value equal 1ms * Note : depend on hardware platform, user to overwrite */ #define __HW_TIME_COUNT_PER_MS 10 #include <gd32f3x0.h> void TIMER2_IRQHandler(void) { if (timer_flag_get(TIMER2, TIMER_FLAG_CH0) == SET) { timer_flag_clear(TIMER2, TIMER_FLAG_CH0); sw_timer_timeout_irq(); } } /* * Disable mcu global interrupt use for atomic operation * Note : depend on hardware platform, user to overwrite */ void __hw_disable_mcu_irq(void) { __disable_irq(); } /* * Enable mcu global interrupt use for atomic operation * Note : depend on hardware platform, user to overwrite */ static void __hw_enable_mcu_irq(void) { __enable_irq(); } /* * Initialized hardware timer * Requirement : * Counter mode - single, don't repeat * Counter unit - (1 / __HW_TIME_COUNT_PER_MS) ms. For example : * __HW_TIME_COUNT_PER_MS = 10, Counter unit equal 0.1ms * Timeout - trigger interrupt and stop count * Note : depend on hardware platform, user to overwrite */ static void __hw_timer_init(void) { timer_parameter_struct timer_initpara; rcu_periph_clock_enable(RCU_TIMER2); timer_deinit(TIMER2); timer_initpara.prescaler = 96*100 - 1; timer_initpara.alignedmode = TIMER_COUNTER_EDGE; timer_initpara.counterdirection = TIMER_COUNTER_UP; timer_initpara.period = 0xffff; timer_initpara.clockdivision = TIMER_CKDIV_DIV1; timer_initpara.repetitioncounter = 0; timer_init(TIMER2,&timer_initpara); timer_counter_value_config(TIMER2, 0); timer_channel_output_mode_config(TIMER2, TIMER_CH_0, TIMER_OC_MODE_TIMING); } /* * Stop hardware timer * Note : depend on hardware platform, user to overwrite */ static void __hw_timer_stop(void) { timer_interrupt_disable(TIMER2, TIMER_INT_CH0); timer_disable(TIMER2); } /* * Configurate hardware timer timeout, enable hardware timer * interrupt and start hardware timer count * Note : depend on hardware platform, user to overwrite */ static void __hw_timer_start(uint32_t count) { timer_counter_value_config(TIMER2, 0); timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_0, count); timer_flag_clear(TIMER2, TIMER_FLAG_CH0); timer_interrupt_enable(TIMER2, TIMER_INT_CH0); timer_enable(TIMER2); } /* * Get hardware timer counter value * Note : depend on hardware platform, user to overwrite */ static uint32_t __hw_timer_get_count(void) { return timer_counter_read(TIMER2); } /* * Set hardware timer counter value * Note : depend on hardware platform, user to overwrite */ static void __hw_timer_set_count(uint32_t count) { timer_counter_value_config(TIMER2, count); } struct sw_timer { sw_func_cb_t func; void *cb_data; uint32_t timeout_ms; uint32_t remain_ms; uint32_t is_running : 1; uint32_t is_repeat : 1; uint32_t is_used : 1; uint32_t reserve : 29; }; struct sw_timer_context_t { struct sw_timer timer[SW_TIMER_MAX_NUMBER]; uint8_t is_init; }; static struct sw_timer_context_t sw_timer_context; static void update_remain(uint32_t count_ms) { uint16_t i; struct sw_timer *sw_timer; for(i = 0; i < SW_TIMER_MAX_NUMBER; i++) { sw_timer = &sw_timer_context.timer[i]; if(sw_timer->is_used && sw_timer->is_running) { if(sw_timer->remain_ms > count_ms) { sw_timer->remain_ms = sw_timer->remain_ms - count_ms; } else { sw_timer->remain_ms = 0; } } } } static uint32_t get_run_timer_min_remain(void) { uint16_t i; struct sw_timer *sw_timer; uint32_t min_remain = 0xffffffff; for(i = 0; i < SW_TIMER_MAX_NUMBER; i++) { sw_timer = &sw_timer_context.timer[i]; if(sw_timer->is_running) { if(sw_timer->remain_ms < min_remain) { min_remain = sw_timer->remain_ms; } } } return min_remain; } /* * Call by hardware timer interrupt server */ void sw_timer_timeout_irq(void) { uint16_t i; struct sw_timer *sw_timer; __hw_disable_mcu_irq(); uint32_t count_ms = __hw_timer_get_count() / __HW_TIME_COUNT_PER_MS; __hw_timer_set_count(0); __hw_timer_stop(); update_remain(count_ms); __hw_enable_mcu_irq(); for(i = 0; i < SW_TIMER_MAX_NUMBER; i++) { __hw_disable_mcu_irq(); sw_timer = &sw_timer_context.timer[i]; if(sw_timer->is_used && sw_timer->is_running && sw_timer->remain_ms == 0) { if(sw_timer->is_repeat) { sw_timer->remain_ms = sw_timer->timeout_ms; } else { sw_timer->is_running = 0; } __hw_enable_mcu_irq(); if(sw_timer->func) { sw_timer->func(sw_timer->cb_data); } } __hw_enable_mcu_irq(); } __hw_disable_mcu_irq(); uint32_t hw_timeout_ms = get_run_timer_min_remain(); /* * None Software timer need run if hw_timeout_ms equal 0xffffffff. */ if(hw_timeout_ms == 0xffffffff) { __hw_enable_mcu_irq(); return; } if(hw_timeout_ms > __HW_TIME_MAX_COUNT /__HW_TIME_COUNT_PER_MS) { hw_timeout_ms = __HW_TIME_MAX_COUNT / __HW_TIME_COUNT_PER_MS; } __hw_timer_start(hw_timeout_ms * __HW_TIME_COUNT_PER_MS); __hw_enable_mcu_irq(); } int sw_timer_alloc(uint32_t *handle) { uint16_t i; struct sw_timer *sw_timer; for(i = 0; i < SW_TIMER_MAX_NUMBER; i++) { __hw_disable_mcu_irq(); sw_timer = &sw_timer_context.timer[i]; if(!sw_timer->is_used) { memset(sw_timer, 0x00, sizeof(struct sw_timer)); sw_timer->is_used = 1; *handle = SW_TIMER_HANDLE_MAGIC | i; __hw_enable_mcu_irq(); return 0; } __hw_enable_mcu_irq(); } return -1; } int sw_timer_free(uint32_t handle) { uint16_t timer_index = handle ^ SW_TIMER_HANDLE_MAGIC; if((handle & SW_TIMER_HANDLE_MAGIC) != SW_TIMER_HANDLE_MAGIC || timer_index >= SW_TIMER_MAX_NUMBER) { return -1; } __hw_disable_mcu_irq(); struct sw_timer *sw_timer = &sw_timer_context.timer[timer_index]; sw_timer->is_used = 0; sw_timer->is_running = 0; sw_timer->is_repeat = 0; __hw_enable_mcu_irq(); return 0; } int sw_timer_start(uint32_t handle, uint32_t timeout_ms, sw_func_cb_t func, void *cb_data, uint8_t repeat) { uint16_t timer_index = handle ^ SW_TIMER_HANDLE_MAGIC; if((handle & SW_TIMER_HANDLE_MAGIC) != SW_TIMER_HANDLE_MAGIC || timer_index >= SW_TIMER_MAX_NUMBER) { return -1; } if(timeout_ms == 0xffffffff || timeout_ms == 0) { return -1; } __hw_disable_mcu_irq(); struct sw_timer *sw_timer = &sw_timer_context.timer[timer_index]; if(!sw_timer->is_used || sw_timer->is_running) { __hw_enable_mcu_irq(); return -1; } if(!sw_timer_context.is_init) { sw_timer_context.is_init = 1; __hw_timer_init(); } __hw_timer_stop(); uint32_t count_ms = __hw_timer_get_count() / __HW_TIME_COUNT_PER_MS; __hw_timer_set_count(0); update_remain(count_ms); sw_timer->timeout_ms = timeout_ms; sw_timer->func = func; sw_timer->cb_data = cb_data; sw_timer->is_repeat = repeat; sw_timer->is_running = 1; sw_timer->remain_ms = timeout_ms; uint32_t hw_timeout_ms = get_run_timer_min_remain(); if(hw_timeout_ms > __HW_TIME_MAX_COUNT /__HW_TIME_COUNT_PER_MS) { hw_timeout_ms = __HW_TIME_MAX_COUNT / __HW_TIME_COUNT_PER_MS; } if(hw_timeout_ms == 0) { /* * Exist timer expire, write 1ms to trigger interrupt quickly. */ __hw_timer_start(__HW_TIME_COUNT_PER_MS); } else { __hw_timer_start(hw_timeout_ms * __HW_TIME_COUNT_PER_MS); } __hw_enable_mcu_irq(); return 0; } int sw_timer_stop(uint32_t handle) { uint16_t timer_index = handle ^ SW_TIMER_HANDLE_MAGIC; if((handle & SW_TIMER_HANDLE_MAGIC) != SW_TIMER_HANDLE_MAGIC || timer_index >= SW_TIMER_MAX_NUMBER) { return -1; } __hw_disable_mcu_irq(); struct sw_timer *sw_timer = &sw_timer_context.timer[timer_index]; if(!sw_timer->is_used || !sw_timer->is_running) { __hw_disable_mcu_irq(); return -1; } sw_timer->is_running = 0; __hw_disable_mcu_irq(); return 0; } #define SW_TIMER_TEST #ifdef SW_TIMER_TEST static uint32_t time[SW_TIMER_MAX_NUMBER]; static void sw_func_cb_test(void *cb_data) { uint32_t handle = *(unsigned int *)cb_data; usart0_printf("[%x]\r\n", handle); if((handle & 0xffff) < 10) { sw_timer_free(handle); } else if((handle & 0xffff) < SW_TIMER_MAX_NUMBER-1) { sw_timer_stop(handle); } } void sw_timer_test(void) { uint16_t i; for(i = 0; i < SW_TIMER_MAX_NUMBER; i++) { sw_timer_alloc(&time[i]); sw_timer_start(time[i], 1000+(i+1)*10, sw_func_cb_test, &time[i], 1); } } #endif #ifdef __cplusplus } #endif