【C/C++开源库】适合嵌入式的定时器调度器

2023-05-16

一、背景

在嵌入式软件中,我们经常需要使用定时功能,比如每1s执行某个功能,比如触发了某个条件之后持续1s。如果每次遇到定时的功能,我们都自己去计数,这会让我们的代码很混乱,因此我们有必要准备几个好用的定时器调度器模块。

二、MultiTimer

简介

MultiTimer 是一个软件定时器扩展模块,可无限扩展你所需的定时器任务,取代传统的标志位判断方式, 更优雅更便捷地管理程序的时间触发时序。

项目地址:https://github.com/0x1abin/MultiTimer/blob/development/MultiTimer.c

使用方法

  1. 配置系统时间基准接口,安装定时器驱动;
uint64_t PlatformTicksGetFunc(void)
{
    /* Platform implementation */
}

MultiTimerInstall(PlatformTicksGetFunc);
  1. 实例化一个定时器对象;
MultiTimer timer1;
  1. 设置定时时间,超时回调处理函数, 用户上下指针,启动定时器;
int MultiTimerStart(&timer1, uint64_t timing, MultiTimerCallback_t callback, void* userData);
  1. 在主循环调用定时器后台处理函数
int main(int argc, char *argv[])
{
    ...
    while (1) {
        ...
        MultiTimerYield();
    }
}

功能限制

1.定时器的时钟频率直接影响定时器的精确度,尽可能采用1ms/5ms/10ms这几个精度较高的tick;

2.定时器的回调函数内不应执行耗时操作,否则可能因占用过长的时间,导致其他定时器无法正常超时;

3.由于定时器的回调函数是在 MultiTimerYield 内执行的,需要注意栈空间的使用不能过大,否则可能会导致栈溢出。

Examples

见example目录下的测试代码,main.c为普通平台测试demo,test_linux.c为linux平台的测试demo。

#include <stdio.h>
#include <sys/time.h>
#include <time.h>
#include "MultiTimer.h"

MultiTimer timer1;
MultiTimer timer2;
MultiTimer timer3;

uint64_t PlatformTicksGetFunc(void)
{
    struct timespec current_time;
    clock_gettime(CLOCK_MONOTONIC, &current_time);
    return (uint64_t)((current_time.tv_sec * 1000) + (current_time.tv_nsec / 1000000));
}

void exampleTimer1Callback(MultiTimer* timer, void *userData)
{
    printf("exampleTimer1Callback-> %s.\r\n", (char*)userData);
    MultiTimerStart(timer, 1000, exampleTimer1Callback, userData);
}

void exampleTimer2Callback(MultiTimer* timer, void *userData)
{
    printf("exampleTimer2Callback-> %s.\r\n", (char*)userData);
}

void exampleTimer3Callback(MultiTimer* timer, void *userData)
{
    printf("exampleTimer3Callback-> %s.\r\n", (char*)userData);
    MultiTimerStart(timer, 4567, exampleTimer3Callback, userData);
}

int main(int argc, char *argv[])
{
    MultiTimerInstall(PlatformTicksGetFunc);

    MultiTimerStart(&timer1, 1000, exampleTimer1Callback, "1000ms CYCLE timer");
    MultiTimerStart(&timer2, 5000, exampleTimer2Callback, "5000ms ONCE timer");
    MultiTimerStart(&timer3, 3456, exampleTimer3Callback, "3456ms delay start, 4567ms CYCLE timer");

    while (1) {
        MultiTimerYield();
    }
}

源码解析

先看源码:

MultiTimer.h

/*
 * Copyright (c) 2021 0x1abin
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#ifndef _MULTI_TIMER_H_
#define _MULTI_TIMER_H_

#include <stdint.h>

#ifdef __cplusplus  
extern "C" {  
#endif

typedef uint64_t (*PlatformTicksFunction_t)(void);

typedef struct MultiTimerHandle MultiTimer;

typedef void (*MultiTimerCallback_t)(MultiTimer* timer, void* userData);

struct MultiTimerHandle {
    MultiTimer* next;
    uint64_t deadline;
    MultiTimerCallback_t callback;
    void* userData;
};

/**
 * @brief Platform ticks function.
 * 
 * @param ticksFunc ticks function.
 * @return int 0 on success, -1 on error.
 */
int MultiTimerInstall(PlatformTicksFunction_t ticksFunc);

/**
 * @brief Start the timer work, add the handle into work list.
 * 
 * @param timer target handle strcut.
 * @param timing Set the start time.
 * @param callback deadline callback.
 * @param userData user data.
 * @return int 0: success, -1: fail.
 */
int MultiTimerStart(MultiTimer* timer, uint64_t timing, MultiTimerCallback_t callback, void* userData);

/**
 * @brief Stop the timer work, remove the handle off work list.
 * 
 * @param timer target handle strcut.
 * @return int 0: success, -1: fail.
 */
int MultiTimerStop(MultiTimer* timer);

/**
 * @brief Check the timer expried and call callback.
 * 
 * @return int The next timer expires.
 */
int MultiTimerYield(void);

#ifdef __cplusplus
} 
#endif

#endif

MultiTimer.c

#include "MultiTimer.h"
#include <stdio.h>

/* Timer handle list head. */
static MultiTimer* timerList = NULL;

/* Timer tick */
static PlatformTicksFunction_t platformTicksFunction = NULL;

int MultiTimerInstall(PlatformTicksFunction_t ticksFunc)
{
    platformTicksFunction = ticksFunc;
    return 0;
}

int MultiTimerStart(MultiTimer* timer, uint64_t timing, MultiTimerCallback_t callback, void* userData)
{
    if (!timer || !callback ) {
        return -1;
    }
    MultiTimer** nextTimer = &timerList; //因为需要修改timerList,所以我们
    /* Remove the existing target timer. */
    for (; *nextTimer; nextTimer = &(*nextTimer)->next) {
        if (timer == *nextTimer) {
            *nextTimer = timer->next; /* remove from list */
            break;
        }
    }

    /* Init timer. */
    timer->deadline = platformTicksFunction() + timing;
    timer->callback = callback;
    timer->userData = userData;

    /* Insert timer. */
    for (nextTimer = &timerList;; nextTimer = &(*nextTimer)->next) {
        if (!*nextTimer) {
            timer->next = NULL;
            *nextTimer = timer;
            break;
        }
        if (timer->deadline < (*nextTimer)->deadline) {
            timer->next = *nextTimer;
            *nextTimer = timer;
            break;
        }
    }
    return 0;
}

int MultiTimerStop(MultiTimer* timer)
{
    MultiTimer** nextTimer = &timerList;
    /* Find and remove timer. */
    for (; *nextTimer; nextTimer = &(*nextTimer)->next) {
        MultiTimer* entry = *nextTimer;
        if (entry == timer) {
            *nextTimer = timer->next;
            break;
        }
    }
    return 0;
}

int MultiTimerYield(void)
{
    MultiTimer* entry = timerList;
    for (; entry; entry = entry->next) {
        /* Sorted list, just process with the front part. */
        if (platformTicksFunction() < entry->deadline) {
            return (int)(entry->deadline - platformTicksFunction());
        }
        /* remove expired timer from list */
        timerList = entry->next;

        /* call callback */
        if (entry->callback) {
            entry->callback(entry, entry->userData);
        }
    }
    return 0;
}

三、SmartTimer

##1.SmartTimer能干什么?##

简单说来,SmartTimer是一个轻量级的基于STM32的定时器调度器,在单片机”裸跑”的情况下,可以很方便的实现异步编程。

项目地址:https://github.com/lmooml/SmartTimer

它可以应用在对实时性要求没那么高的场合,比如说一个空气检测装置,每200ms收集一次甲醛数据,这个任务显然对实时性要求没那么高,如果时间上相差几毫秒,甚至几十毫秒也没关系,那么使用SmartTimer非常适合;而如果开发一个四轴飞行器,无论是对陀螺仪数据的采集、计算,以及对4个电机的控制,在时间的控制上都需要非常精确。那么这种场合下SmartTimer无法胜任,你需要一个带有抢占优先级机制的实时系统。

不同的场景,选择不同的工具和架构才是最合理的,SmartTimer只能做它力所能及的事情。

虽然SmartTimer是基于STM32开发的,但是它可以很方便的移植到其他的单片机上。

##2. SmartTimer的一般用法##

###2.1 Runlater。###

在单片机编程中,想实现在”xxx毫秒后调用xxx函数”的功能,一般有3种方法:

  1. 用阻塞的,非精确的方式,就是用for(i=0;i<0xffff;i++);这种循环等待的方式,来非精确的延迟一段时间,然后再顺序执行下面的程序;
  2. 利用硬件定时器实现异步的精确延时,把XXX函数在定时器中断里执行;
  3. 同样是利用硬件定时器,但是只在定时器中断里设置标志位,在系统的主While循环中检测这个标志位,当检测到标志置位后,去运行XXX函数。

从理论上来说,以上3种方式中,第3种采用定时器设定标志位的方法最好。因为首先主程序不用阻塞,在等待的时间里,MCU完全可以去做其他的事情,其次在定时器中断里不用占用太多的时间,节约中断资源。但这种方式有个缺点,就是实现起来相对麻烦一些。因为如果你要有N个runlater的需求,那么就得设置N个标志位,还要考虑定时器的分配、设定。在程序主While循环里也会遍布N个查询标志位的if语句。如果N足够多,其实大于5个,就会比较头疼。这样会使主While循环看起来很乱。这样的实现不够简洁、优雅。

SmartTimer首先解决的就是这个问题,它可以优雅地延迟调用某函数。

###2.2 Runloop###

在定时器编程方面还有另一个典型需求,就是“每隔xxx毫秒运行一次XXX函数,一共运行XXX次”。这个实现起来和runlater差不多,就是加一个运行次数的技术标志。我就不再赘述了。还是那句话:

SmartTimer可以优雅的实现Runloop功能。

###2.3 Delay### 并不是说非阻塞就一定比阻塞好,因为在某些场景下,必须得用到阻塞,使单片机停下来等待某个事件。那么SmartTimer也可以提供这个功能。

##3. SmartTimer的高级用法## 所谓的高级用法,并不是说SmartTimer有隐藏模式,能开启黑科技。而是说,如果你能转变思路,举一反三地话,可以利用SmartTimer提供的简单功能实现更加优化、合理的系统结构。

传统的单片机裸跑一般采用状态机模式,就是在主While循环里设定一些标志位或是设定好程序进行的步骤,根据事件的进程来跳转程序。简单的说来,这是一种顺序执行的程序结构。其灵活性和实时性并不高,尤其是当需要处理的业务越来越多,越来越复杂时,状态机会臃肿不堪,一不留神(其实是一定以及肯定)就会深埋bug于其中,调试解决BUG时也会异常痛苦。

如果你能转换一下思路,不再把业务逻辑中各个模块的关系看成基于因果(顺序),而是基于时间,模块间如果需要确定次序可以采用标志位进行同步。那么恭喜你,你已经有了采用实时系统的思想,可以尝试使用RT-thread等操作系统来完成你的项目了。但是,使用操作系统有几个问题,第一是当单片机资源有限的时候,使用操作系统恐怕不太合适;第二是学习操作系统本身有一定的难度,至少你需要花费一定的时间;第三如果你的项目复杂度没有那么高,使用操作系统有点大材小用。

那么,请允许我没羞没臊的说一句,其实利用SmartTimer中的Runloop功能可以简单的实现基于时间的主程序框架。

##4.关于Demo## 与源码一起提供的,还有一个Demo程序。这个Demo比较简单,主要是为了测试SmartTimer的功能。Demo程序基本可以体现Runlater,Runloop,Delay功能。同时也能基本体现基于时间的编程思想(单片机裸跑程序框架)。

##5.SmartTimer的使用## SmartTimer.h中声明的公开函数并不多,总共有8个:

void stim_init ( void );

void stim_tick (void);

void stim_mainloop ( void );

int8_t stim_loop ( uint16_t delayms, void (*callback)(void), uint16_t times);

int8_t stim_runlater ( uint16_t delayms, void (*callback)(void));

void stim_delay ( uint16_t delayms);

void stim_kill_event(int8_t id);

void stim_remove_event(int8_t id);

下面我将逐一介绍 ###5.1 必要的前提### SmartTimer能够工作的必要条件是:

  • A. 设置Systick的定时中断(也可以是其他的硬件定时器TIMx,我选择的是比较简单的Systick),我默认设置为1ms中断一次,使用者可以根据自己的情况来更改。Systick时钟的设置在stim_init函数中,该函数必须在主程序初始化阶段调用一次。
  • B. 在定时器中断函数中调用stim_tick();可以说,这个函数是SmartTimer的引擎,如A步骤所述,默认情况下,每1ms,定时器中断会调用一次stim_tick();
  • C. 在主While循环中执行stim_mainloop(),这个函数主要有两个作用,一是执行定时结束后的回调函数;二是回收使用完毕的timer事件的资源。

###5.2 开始使用SmartTimer### 做好以上的搭建工作后,就可以开始使用SmartTimer了。

int8_t stim_runlater ( uint16_t delayms, void (*callback)(void));

该函数接受两个参数,返回定时事件的id。参数delayms传入延迟多长时间,注意这里的单位是根据之前A步骤里,你设置的时间滴答来确定的(默认单位是1ms);第二个参数是回调函数的函数指针,目前只支持没有参数,且无返回值的回调函数,未来会考虑加入带参数和返回值的回调。 举例:

timer_runlater(100,ledflash); //100豪秒(100*1ms=100ms)后,执行void ledflash(void)函数

如果在stim_init()中,设置的时钟滴答为10ms执行一次,那么传入同样的参数,意义就会改变:

timer_runlater(100,ledflash); //1秒(100*10ms=1000ms=1S)后,执行void ledflash(void)函数

int8_t stim_loop ( uint16_t delayms, void (*callback)(void), uint16_t times);

这个函数的参数意义同runlater差不多,我就不详细说明了。 该函数接收3个参数,delayms为延迟时间,callback为回调函数指针,times是循环次数。 举例(以1ms滴答为例):

timer_runloop(50,ledflash,5); // 每50ms,执行一次ledflash(),总共执行5次` `timer_runloop(80,ledflash, TIMER_LOOP_FOREVER); // 每80ms,执行一次ledflash(),无限循环。

void timer_delay ( uint16_t delayms); //延迟xx ms

这个函数会阻塞主程序,并延迟一段时间。

void stim_kill_event(int8_t id);

void stim_remove_event(int8_t id);

这两个函数,可以将之前设定的定时事件取消。比如之前用stim_loop无限循环了一个事件,当获取某个指令后,需要取消这个任务,则可以用这两个函数取消事件调度。这两个函数的区别是:

void stim_kill_event(int8_t id); //直接取消事件,忽略未处理完成的调度任务。` `void stim_remove_event(int8_t id);//将已经完成计时的调度任务处理完毕之后,再取消事件

###5.3 注意事项### SmartTimer可接受的Timer event数量是有上限的,这个上限由smarttimer.h中的宏定义

#define TIMEREVENT_MAX_SIZE 20

来决定的。默认为20个,你可以根据实际情况增加或减少。但不可多于128个

源码分析

smarttimer.h

/*
 * =====================================================================================
 *
 *       Filename:  smarttimer.h
 *
 *    Description:  
 *
 *        Version:  1.1
 *        Created:  2016/7/14 ÐÇÆÚËÄ ÉÏÎç 10:48:35
 *       Revision:  none
 *       Compiler:  armcc
 *
 *         Author:  lell 
 *   Organization:  
 *
 * =====================================================================================
 */
#ifndef  __SMARTTIMER_H__
#define  __SMARTTIMER_H__
#include "stm32f10x.h"

//#define STIM_DEBUG


#ifndef NULL
#define NULL ((void *)0)
#endif

#define CLOSE_INTERRUPT()       __ASM("CPSID  I")
#define OPEN_INTERRUPT()        __ASM("CPSIE  I")  


#define	STIM_EVENT_MAX_SIZE      20			            /*max size of timer event number  */
#define STIM_LOOP_FOREVER       (uint16_t)0xffff

#define STIM_INVALID            0xff

#define STIM_EVENT_IDLE         0x00
#define STIM_EVENT_ACTIVE       0x01
#define STIM_EVENT_RECYCLE      0x02

void stim_init ( void );
void stim_tick (void);
void stim_mainloop ( void );
int8_t stim_loop ( uint16_t delayms, void (*callback)(void), uint16_t times);
int8_t stim_runlater ( uint16_t delayms, void (*callback)(void));
void stim_delay ( uint16_t delayms);
void stim_kill_event(int8_t id);
void stim_remove_event(int8_t id);

#ifdef STIM_DEBUG
uint8_t stim_get_eventnum(void);
void stim_print_status(void);
#endif

#endif    

smarttimer.c

/*
 * =====================================================================================
 *
 *       Filename:  smarttimer.c
 *
 *    Description:  software timer dispath
 *
 *        Version:  1.1
 *        Created:  2015/7/14 ÐÇÆÚËÄ ÉÏÎç 10:37:39
 *       Revision:  none
 *       Compiler:  armcc
 *
 *         Author:  lell(elecsunxin@gmail.com)  
 *   Organization:  
 *
 * =====================================================================================
 */
//#include <stdlib.h>
#include "smarttimer.h"

#ifdef STIM_DEBUG
#include <stdio.h>
#endif

struct stim_event{
  uint32_t tick_punch;         
  uint32_t interval;    
  uint32_t looptimes;   
  uint8_t id;     
  uint8_t stat;
  struct stim_event *next;
  struct stim_event *prev;
};

struct stim_event_list{
  struct stim_event *head;
  struct stim_event *tail;
  uint8_t count;
};


struct stim_event_list_manager{
  struct stim_event_list list[2]; 
  uint8_t cur_index;
};


static struct stim_event event_pool[STIM_EVENT_MAX_SIZE];
static struct stim_event_list_manager list_manager;
static struct stim_event_list recycle_list;

static void (*callback_list[STIM_EVENT_MAX_SIZE])(void);
static uint8_t mark_list[STIM_EVENT_MAX_SIZE];
static uint32_t current_tick;



/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  init_linked
 *  Description:  init a stim_event_list linked
 * =====================================================================================
 */
static void init_linked ( struct stim_event_list *list )
{
  list->head = list->tail = NULL;
  list->count = 0;
}		/* -----  end of static function init_linked  ----- */


/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  remove_node
 *  Description:  remove a node from a stim_event_list linked
 * =====================================================================================
 */
static void remove_node ( struct stim_event *event, struct stim_event_list *list )
{

  if(list->head == event){
    list->head = event->next;
    if(list->head == NULL){
      list->tail = NULL;
    }else{
      event->next->prev = NULL;
    }
  }else{
    event->prev->next = event->next;
    if(event->next == NULL){
      list->tail = event->prev;
    }else{
      event->next->prev = event->prev;
    }
  }

  list->count--;
}		/* -----  end of static function remove_event  ----- */


/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  insert_node_prev
 *  Description:  insert a nodes in stim_event_list linked
 * =====================================================================================
 */
static void insert_node_prev ( struct stim_event *new_node,struct stim_event *node,struct stim_event_list *list )
{
  new_node->next = node;
  if(node->prev == NULL){
    list->head = new_node;
    new_node->prev = NULL;
  }else{
    new_node->prev = node->prev;
    node->prev->next = new_node;
  }
  node->prev = new_node;

  list->count++;
}		/* -----  end of static function insert_node_prev  ----- */

/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  insert_to_tail
 *  Description:  insert a nodes in stim_event_list linked
 * =====================================================================================
 */
static void insert_to_tail ( struct stim_event *new_node ,struct stim_event_list *list)
{
  struct stim_event *node = list->tail;

  if(list->count == 0){
    list->head = new_node;
    list->tail = new_node;
    new_node->next = NULL;
    new_node->prev = NULL;
  }else{
    node->next = new_node;
    new_node->prev = node;
    new_node->next = NULL;
    list->tail = new_node;
  }

  list->count++;
}		/* -----  end of static function insert_event  ----- */

/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  malloc_event
 *  Description:  get a new event struct from event_pool
 *       return:  a pointer of event struct. 
 *
 * =====================================================================================
 */
static struct stim_event* malloc_event (void)
{
  uint8_t i;
  for(i = 0; i < STIM_EVENT_MAX_SIZE; i++){
    if(event_pool[i].stat == STIM_EVENT_IDLE){
      event_pool[i].stat = STIM_EVENT_ACTIVE;
      return &event_pool[i];
    }
  }
  return NULL;
}		/* -----  end of static function stim_malloc_event  ----- */


/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  free_event
 *  Description:  release event to event_pool
 * =====================================================================================
 */
static void free_event (struct stim_event *event)
{
  callback_list[event->id] = NULL;
  mark_list[event->id] = STIM_INVALID;

  event->stat = STIM_EVENT_IDLE;
  event->interval = 0;
  event->looptimes = 0;
  event->tick_punch = 0;
  event->prev = NULL;
  event->next = NULL;

}		/* -----  end of static function stim_free_event  ----- */


/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  insert_event
 *  Description:  insert a timer event to a special list
 * =====================================================================================
 */
static void insert_event ( struct stim_event *event,struct stim_event_list *list )
{
  uint8_t i;
  struct stim_event *node;

  if(list->count == 0){
    //insert event to a empty linked
    insert_to_tail(event,list);
  }else{
    node = list->head;
    for(i = 0; i < list->count; i++){
      if(event->tick_punch > node->tick_punch){
        node = node->next;
      }else{
        break;
      }
    }

    if(node == NULL){
      //insert event to linked tail
      insert_to_tail(event,list);
    }else{
      //insert event to a linked
      insert_node_prev(event,node,list);
    }
  }
}		/* -----  end of static function insert_event  ----- */



/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  find_event
 *  Description:  
 * =====================================================================================
 */
static struct stim_event* find_event ( int8_t id, struct stim_event_list *list )
{ 
	uint8_t i;
  struct stim_event *event;
  event = list->head;
  for(i = 0; i < list->count; i++){
    if(event->id == id){
      break;
    }else{
      event = event->next;
    }
  }

  return event;
}		/* -----  end of static function find_event  ----- */



/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  recyle_event
 *  Description:  insert an event which stat is STIM_EVENT_RECYCLE into recycle linked
 * =====================================================================================
 */
static void recyle_event ( struct stim_event *event )
{
  struct stim_event_list *list = &list_manager.list[list_manager.cur_index];

  remove_node(event,list);
  insert_to_tail(event,&recycle_list);
}		/* -----  end of static function recyle_event  ----- */





/*
 * ===  FUNCTION  ======================================================================
 *         Name:  push_event
 *  Description:  push a stim_event to event linked list.
 * =====================================================================================
 */
static struct stim_event* push_event ( uint32_t delayms, void (*callback)(void),uint16_t times )
{
  struct stim_event *event;

  event = malloc_event();
  event->interval = delayms;
  event->looptimes = times;
  event->next = NULL;
  event->tick_punch = current_tick + delayms;

  mark_list[event->id] = 0;
  if(callback != NULL){
    callback_list[event->id] = callback;
  }

  if(event->tick_punch < current_tick){
    insert_event(event,&list_manager.list[list_manager.cur_index ^ 0x01]);
  }else{
    insert_event(event,&list_manager.list[list_manager.cur_index]);
  }

  return event;
}		/* -----  end of static function stim_push_delay_event  ----- */


/*
 *
 * ===  FUNCTION  ======================================================================
 *         Name:  stim_delay
 *  Description:  wait some milliseconds.It will blocking process until time out.
 * =====================================================================================
 */
void stim_delay ( uint16_t delayms)
{

  struct stim_event *event;
  CLOSE_INTERRUPT();  
  event = push_event(delayms,NULL,1);
  OPEN_INTERRUPT();
  while(mark_list[event->id] == 0);
}		/* -----  end of function stim_delay  ----- */




/*
 * ===  FUNCTION  ======================================================================
 *         Name:  stim_runlater
 *  Description:  run callback function after some milliseconds by asynchronous.
 * =====================================================================================
 */
int8_t stim_runlater ( uint16_t delayms, void (*callback)(void))
{
  struct stim_event *event;

  CLOSE_INTERRUPT(); 
  event = push_event(delayms,callback,1);
  OPEN_INTERRUPT(); 
  return event->id;
}		/* -----  end of function stim_runlater  ----- */
/*
 * ===  FUNCTION  ======================================================================
 *         Name:  stim_loop
 *  Description:  looping invok each interval by asynchronous.
 * =====================================================================================
 */
int8_t stim_loop ( uint16_t delayms, void (*callback)(void), uint16_t times)
{
  struct stim_event *event;

  CLOSE_INTERRUPT();  
  event = push_event(delayms,callback,times);
  OPEN_INTERRUPT(); 
  return event->id;
}		/* -----  end of function stim_loop  ----- */



/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  stim_kill_event
 *  Description:  
 * =====================================================================================
 */
void stim_kill_event(int8_t id)
{
  uint8_t i;
  struct stim_event_list *list;
  struct stim_event *event;

  CLOSE_INTERRUPT();  
  for(i = 0; i < 2; i++){
    list = &list_manager.list[i];
    event = find_event(id,list);
    if(event != NULL){
      remove_node(event,list);
      free_event(event);
      break;
    }
  }
  OPEN_INTERRUPT(); 
}


/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  stim_remove_event
 *  Description:  
 * =====================================================================================
 */
void stim_remove_event(int8_t id)
{
  uint8_t i;
  struct stim_event_list *list;
  struct stim_event *event;

  CLOSE_INTERRUPT();  
  for(i = 0; i < 2; i++){
    list = &list_manager.list[i];
    event = find_event(id,list);
    if(event != NULL){
      event->stat = STIM_EVENT_RECYCLE;
      recyle_event(event);
      break;
    }
  }
  OPEN_INTERRUPT(); 
}



/*
 * ===  FUNCTION  ======================================================================
 *         Name:  stim_tick
 *  Description:  engine of smarttimer.
 *                It must be execute in systick( or hardware timer) interrupt
 * =====================================================================================
 */
void stim_tick (void)
{
  struct stim_event *event;
  struct stim_event_list *list;

  if(((current_tick + 1) & 0xffffffff) < current_tick){
    list_manager.cur_index ^= 0x01;
  }
  current_tick++;

  list = &list_manager.list[list_manager.cur_index];
  event = list->head;

  while(event != NULL && event->tick_punch <= current_tick){
    mark_list[event->id] += 1;



    if((event->looptimes != STIM_LOOP_FOREVER) && 
        (--event->looptimes == 0)){
      event->stat = STIM_EVENT_RECYCLE;
      recyle_event(event);
    }else{
      event->tick_punch = current_tick + event->interval;
      remove_node(event,list);
      if(event->tick_punch < current_tick){
        list = &list_manager.list[list_manager.cur_index ^ 0x01];
      }
      insert_event(event,list);
    }
    event = event->next;
  }

}		/* -----  end of function stim_dispach  ----- */


/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  stim_mainloop
 *  Description:  checkout mark_list,and execute callback funtion if mark be setted.
 *                this function must be execute by while loop in main.c
 * =====================================================================================
 */
void stim_mainloop ( void )
{
  uint8_t i;
  struct stim_event *event;

  for(i = 0; i < STIM_EVENT_MAX_SIZE; i++){
    if((mark_list[i] != STIM_INVALID) && (mark_list[i] > 0)){
      if(callback_list[i] != NULL){
        callback_list[i]();
      }
      mark_list[i] -= 1;
    }
  }

  if(recycle_list.count > 0){
    event = recycle_list.head;
    while(event != NULL){
      if(mark_list[event->id] == 0){
        remove_node(event,&recycle_list);
        free_event(event);
        break;
      }else{
        event = event->next;
      }
    }
  }

}		/* -----  end of function stim_loop  ----- */




/*
 * ===  FUNCTION  ======================================================================
 *         Name:  stim_init
 *  Description:  smarttimer init program.  Init event linked list,and setup system clock.
 * =====================================================================================
 */
void stim_init ( void )
{
  uint8_t i;
  struct stim_event *event;

  init_linked(&recycle_list);
  init_linked(&list_manager.list[0]);
  init_linked(&list_manager.list[1]);
  list_manager.cur_index = 0;

  current_tick = 0;

  for(i = 0; i < STIM_EVENT_MAX_SIZE; i++){
    event = &event_pool[i];
    event->stat = STIM_EVENT_IDLE;
    event->interval = 0;
    event->tick_punch = 0;
    event->looptimes = 0;
    event->next = NULL;
    event->prev = NULL;
    event->id = i;

    callback_list[i] = NULL;
    mark_list[i] = STIM_INVALID;
  }

  SysTick_Config(SystemCoreClock / 1000);     //tick is 1ms

}		/* -----  end of function stim_init  ----- */

#ifdef STIM_DEBUG
uint8_t stim_get_eventnum(void){
  return list_manager.list[0].count + list_manager.list[1].count;  
}

void stim_print_status(void)
{
  uint8_t i;
  struct stim_event *node;
  struct stim_event_list *list;

  printf("=============================\r\n");
  printf("current_tick = %X\r\n",current_tick);
  printf("cur_index = %d\r\n",list_manager.cur_index);
  list = &list_manager.list[list_manager.cur_index];
  if(list->count == 0){
    printf("list is empty!\r\n");
  }else{
    node = list->head;
    for(i = 0; i <list->count; i++){
      printf("event tick_punch = %X\r\n",node->tick_punch);
      printf("event id = %d\r\n",node->id);
      node = node->next;
    }
  }
  printf("========another list===========\r\n");
  list = &list_manager.list[list_manager.cur_index ^ 0x01];
  if(list->count == 0){
    printf("list is empty!\r\n");
  }else{
    node = list->head;
    for(i = 0; i <list->count; i++){
      printf("event tick_punch = %X\r\n",node->tick_punch);
      printf("event id = %d\r\n",node->id);
      node = node->next;
    }
  }
  printf("========recycle list===========\r\n");
  list = &recycle_list;
  if(list->count == 0){
    printf("list is empty!\r\n");
  }else{
    node = list->head;
    for(i = 0; i <list->count; i++){
      printf("mark_list[%d] = %d\r\n",node->id,mark_list[node->id]);
      node = node->next;
    }
  }
  printf("=============================\r\n");
}

#endif

四、定时器矩阵

这个来自于:https://blog.csdn.net/qq_41854911/article/details/123027606?spm=1001.2014.3001.5501

五、总结

定时器都需要有一个时间基准,有了基准之后,我们就可以方便设置不同的定时器了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【C/C++开源库】适合嵌入式的定时器调度器 的相关文章

  • 单片机怎么做定时器矩阵,彻底解决各种定时问题?

    转载于 xff1a https blog csdn net weixin 43982452 article details 120555258 spm 61 1001 2014 3001 5502 大家好 xff0c 我是无际 定时功能非常
  • 为什么我学51单片机很顺利,学STM32却一头雾水?

    转载于 xff1a https blog csdn net weixin 43982452 article details 120515134 spm 61 1001 2014 3001 5502 五年懂行 xff0c 十年称王 当初自学转
  • 物联网专业真的把人坑惨了?浅谈物联网的未来发展趋势和未来方向

    转载于 xff1a https blog csdn net weixin 43982452 article details 120200879 spm 61 1001 2014 3001 5502 大家好 xff0c 我是无际 从事10年单
  • STM32单片机跑RTOS会比裸机有优势吗?

    转载于无际 xff1a https blog csdn net weixin 43982452 article details 115139030 spm 61 1001 2014 3001 5502 在工作中总是能碰到通过秀技术来满足虚荣
  • 如何快速学会别人的代码和思维

    转载于 xff1a https blog csdn net weixin 43982452 article details 120700863 spm 61 1001 2014 3001 5502 大家好 xff0c 我是无际 也有很多天没
  • 单片机和嵌入式哪个好?单片机会被嵌入式取代吗?

    转载于 xff1a https blog csdn net weixin 43982452 article details 120062206 spm 61 1001 2014 3001 5502 很多初学者都搞不清楚单片机和嵌入式的区别
  • 怎么看懂别人写的单片机项目代码?

    转载于 xff1a https blog csdn net weixin 43982452 article details 120049443 spm 61 1001 2014 3001 5502 记得刚开始接触代码的时候 xff0c 总觉
  • 嵌入式单片机产品开发设计框架

    转载于 xff1a https blog csdn net weixin 43982452 article details 119616145 spm 61 1001 2014 3001 5502 老板突然要给你一个新的需求 xff0c 要
  • 8大话题,解惑企业数字化

    从IT到DT xff0c 从信息化到数字化 xff0c 这个观念已经毋庸置疑 xff0c 但是 xff0c 这条路却缺少参照 xff0c 暗夜前行 xff0c 全靠摸索 关于数字化 xff0c 肯定不是上马一些OA工具 购买一些营销工具就行
  • ARINC 429总线学习资料?

    Hello xff0c 我是小熊coder xff0c 方向是嵌入式AI xff0c 后端开发 我的主页 xff1a Home xff0c 欢迎互相关注 xff0c 互相学习 最近在网上寻找关于ARINC 429总线的资料时 xff0c 发
  • 周期任务框架在裸机、RTOS上的实现

    周期任务框架在裸机 RTOS上的实现 一 任务的类型 运行的程序 xff0c 有响应指令的触发式程序 xff0c 也有一直运行的守护程序 xff0c 周期程序 贴别是在单片机 嵌入式领域 xff0c 大部分程序都是周期性的执行 xff0c
  • CAN通讯实验

    前面我们讲解了CAN总线的一些基础知识 xff0c 文章链接 xff1a 一口气从零读懂CAN总线以及应用 了解完之后 xff0c 我们也需要来用一用CAN总线 这篇文章就是主要讲解在STM32中怎么使用CAN总线
  • 航空机载总线网络概述

    1 机载总线网络概述 现代战斗机的航空电子系统是航空电子技术经历了半个多世纪的漫长演变和不断进步的结果 航空电子系统结构的每次变化 xff0c 其核心的机载总线网络技术也不断跨上新台阶 xff0c 而且每次变革都能使飞机性能得到大幅提升 现
  • 【C/C++开源库】单片机/嵌入式中的C语言日志库

    日志系统在系统开发和调整过程中的重要性 xff0c 大家应该都清楚 xff0c 特别是项目出问题之后 xff0c 却没有日志可以帮忙定位问题 xff0c 就非常令人痛苦 因为我们不可能一直通过调试器去单步调试程序 xff0c 所以设备的运行
  • 稚晖君软件硬件开发环境总结

    0 引言 这两天在bilibili上发现一个宝藏up主 xff0c 稚晖君 啧啧啧 xff0c 很厉害 虽然年龄不大 xff0c 但是真全栈 xff0c 从产品到机械到电路到软件 xff0c 这就是那种真的聪明 xff0c 一学就会的高智商
  • 一文弄清51、STM32、Linux点灯的区别

    嵌入式初学者入门的第一个 项目 就是LED点灯 xff0c 那么 xff0c 本文带你看看51 STM32 Linux点灯有什么区别 xff1f 51点灯 51点灯 xff0c 是很多单片机初学者的首选 xff0c 难度也是相对比较低的 准
  • Keil 模拟器 STM32F103 上手指南

    一般嵌入式操作系统因为它的特殊性 xff0c 往往和硬件平台密切相关连 xff0c 具体的嵌入式操作系统往往只能在特定的硬件上运行 对于刚接触 RT Thread 操作系统的读者并不容易马上就获得一个和 RT Thread 操作系统相配套的
  • 用PyOD工具库进行「异常检测」

    转载于知乎 xff1a 微调 异常检测 xff08 又称outlier detection anomaly detection xff0c 离群值检测 xff09 是一种重要的数据挖掘方法 xff0c 可以找到与 主要数据分布 不同的异常值
  • 在Keil 模拟器上测试RT-Thread代码

    前面一篇文章已经讲解了如何使用Keil模拟器来模拟硬件 xff0c 然后在此基础上运行RT Thread代码 文章链接 xff1a Keil 模拟器 STM32F103 上手指南 我们可以看到RT Thread官方有许多代码示例供我们学习参
  • 在这个艰难的环境下,我裸辞了

    2022年 xff0c 疫情期间 xff0c 工作了22年的我 xff0c 从软件研发管理的相关工作中 xff0c 辞职创业 xff0c 开启我的独立咨询顾问生涯 很多人不解和迷惑 xff0c 也有朋友关切的询问我的近况 xff0c 就差用

随机推荐

  • 2022年顶级实时操作系统榜单发布

    全球知名的开源软件仓库平台SourceForge最近公布了一份 2022年顶级实时操作系统 xff08 RTOS xff09 榜单 其中包括大家熟悉的FreeRTOS RT Thread ThreadX embOS等实时操作系统 地址 xf
  • IDE也卷了,微软杀入嵌入式IDE

    为什么说 IDE 也卷了 xff1f 因为前不久 xff08 3月初 xff09 xff0c Keil 官方推出了免费 没有代码大小限制的社区版IDE xff08 Community xff09 随后 xff08 在上周 xff09 xff
  • 飞机上一般是什么操作系统?

    航空软件其实并不神秘 xff0c 从计算机架构上来说 xff0c 同普通的计算机系统并无太大区别 xff0c 都是由处理器 总线 I O设备 存储设备 网络设备 通讯设备 操作系统和应用软件所构成的 仅仅是为了满足很高指标的可靠性 健壮性和
  • 重构代码总结

    下面推荐一下进行代码重构的文章 xff1a 重构指北 重构 xff0c 改善既有代码设计 精读 经验总结 重构让你的代码更优美和简洁 常见代码重构技巧 如何实施代码重构 xff1f 代码重构新手教程 xff1a 如何将烂代码变成好代码 xf
  • C语言#和##的使用方法和宏嵌套

    推荐下面几篇好文 xff1a C语言 宏嵌套的展开规则 C语言中define的全部使用方法总结 C语言宏的定义和宏的使用方法 xff08 define xff09 C语言宏定义中 与 的用法
  • 深入PCI与PCIe

    转载于老狼 xff1a https zhuanlan zhihu com p 26172972 https zhuanlan zhihu com p 26244141 PCI总线和设备树是X86硬件体系内很重要的组成部分 xff0c 几乎所
  • 【程序员学理财】了解不同理财产品的利息及合理的选择自己的理财配置

    1 利息计算公式 利息计算公式 2 不同理财产品的利率 首先是银行相关的理财 xff1a 余额宝利率 xff1a 年利率在1 94 支付宝短期定期理财 xff1a 一个月利率 xff1a 3 3 xff1b 三个月利率 xff1a 3 9
  • 嵌入式设备系统有无操作系统的区别

    随着通信 电子物联网的飞速发展 xff0c 每天都有各种各样的芯片被研发出来 xff0c 而要想知道这些芯片怎样工作以及工作后的作用 xff0c 则离不开软硬件工程师的努力 xff0c 任何一个计算机系统都是系统中软硬件协作的结果 xff0
  • 从C语言来理解文件系统

    文章目录 一 文件 文件系统二 C语言文件操作详解C语言中的文件是什么 xff1f 文件流 C语言fopen函数的用法 xff0c C语言打开文件详解fopen 函数的返回值判断文件是否打开成功 fopen 函数的打开方式关闭文件实例演示
  • 如何把CPP源程序改写成C语言?

    转载于 xff1a https mp weixin qq com s Xk5k UAItEndEGrUsQr Yw 曾经参与过公司的bpp项目 xff0c 就是bluetooth print profile 由于使用了hpijs的开源包 x
  • Android源码学习之六——ActivityManager框架解析

    ActivityManager 在操作系统中有重要的作用 xff0c 本文利用操作系统源码 xff0c 逐步理清 ActivityManager 的框架 xff0c 并从静态类结构图和动态序列图两个角度分别进行剖析 xff0c 从而帮助开发
  • 怎样修改Ubuntu&windows10双系统的默认启动顺序

    怎样修改Ubuntu amp windows10双系统的默认启动顺序 参考网址 xff1a https jingyan baidu com article f71d60379e16021ab641d1ab html 很多人在安装Linux系
  • 代码文档生成工具Doxygen教程及实例

    程序员的很多文档 xff0c 特别是有代码的文档 xff0c 绝大部分都是由一款文档生成工具 Doxygen 生成 什么是Doxygen Doxygen 是一个程序的文件产生工具 xff0c 可将程序中的特定批注转换成为说明文件 通常我们在
  • 什么是模块化,怎么写一个好的C语言模块?通过标准库,开源库,驱动库学习

    文章目录 什么是模块化1什么是模块化2为啥要用模块化模块化具体原理 xff1a 3模块化基本代码实现 xff1a c语言中条件编译相关的预编译指令4模块化编程注意事项 外设库 xff0c 硬件库 xff0c 驱动库stm32f10x gpi
  • 完成一个设计大赛,需要按照什么步骤?

    转载于 xff1a https blog csdn net rtthreadiotos article details 124094524 spm 61 1001 2014 3001 5502 亲爱的开发者们 xff1a RT Thread
  • 我又发现一个开源串口神器,太强了

    转载于 xff1a https mp weixin qq com s bLsFkIxQ 3adzskT9xUrEQ 最近发现了一个开源的串口项目 Serial Studio xff0c 这是一个强大的数据可视化软件 xff0c 支持串口通信
  • 互联网架构的软件架构是否会对车用软件等传统嵌入式领域的系统架构产生影响

    转载于 xff1a 互联网架构的软件架构是否会对车用软件等传统嵌入式领域的系统架构产生影响 xff1f xff1f 辣笔小星的回答 知乎 https www zhihu com question 34875160 answer 241043
  • 搭建一个轻量级实验室,还不错

    转载于 xff1a https mp weixin qq com s m4 IwyEheiCne5oSr6 LwQ 几乎绝大部分电子工程师都有一个 梦想 xff0c 那就是拥有自己的实验室 有不少朋友问 xff0c 搭建一个电子实验室是不是
  • C语言类型转换-自动类型转换、强制类型转换、指针类型转换

    数据类型转换就是将数据 xff08 变量 数值 表达式的结果等 xff09 从一种类型转换为另一种类型 自动类型转换 自动类型转换就是编译器默默地 隐式地 偷偷地进行的数据类型转换 xff0c 这种转换不需要程序员干预 xff0c 会自动发
  • 【C/C++开源库】适合嵌入式的定时器调度器

    一 背景 在嵌入式软件中 xff0c 我们经常需要使用定时功能 xff0c 比如每1s执行某个功能 xff0c 比如触发了某个条件之后持续1s 如果每次遇到定时的功能 xff0c 我们都自己去计数 xff0c 这会让我们的代码很混乱 xff