1 概述
在单片机系统中,会有几个硬件定时器。在程序需要延时或者定时功能的时候需要使用定时器,但是在整个单片机里面定时器的数量是有限的。不可能每一个功能都去使用一个硬件定时器。对于时间要求不是很严格的场合,可以多个应用层功能共享使用一个定时器。所以这篇文章是基于一个硬件定时器写的一个软件定时器框架,可以移植到任何带有32位,16位,8位定时器的单片机中。本程序在GD32F350上面已经测试完成。有需要的朋友们可以移植到自己的平台上。这个定时器可以用在低功耗场景下(不能频繁中断),定时器不会频繁产生中断,根据具体时间计算产生中断的间隔。不会大量占用CPU资源。
可以在gitee上下载源码: https://gitee.com/huohongpeng/software-timer
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 <[email protected]>
*
* 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