【基于链表的软件定时器实现】

本文详细介绍了如何在单片机开发中使用链表实现软件定时器,支持按优先级插入和轮询,同时支持自动重载和次数限制。通过实例展示了定时器的创建、删除、插入操作及功能测试过程。

基于链表的软件定时器实现


1. 应用需求
1.1 背景
  • 主要是针对单片机的开发,一般单片机的硬件定时器是有限的,不太可能有一个定时任务就用硬件定时器实现。
1.2 前提
  • 有一个已经实现的硬件定时器在进行计数。(本文的实现是基于time.h的库进行获取的时间)
1.3 功能
  • 支持根据参数设置的优先级进行顺序插入,优先级的值越小,则轮询定时器的时候会更快执行。
  • 支持配置软件定时器的定时时间,可以有两种模式配置,一种是持续性的自动装载,另外一种是设置重复运行的次数。
2. 功能实现
2.1 头文件定义
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "time.h"

//所用的参数类型定义
typedef unsigned char       uint8_t;
typedef unsigned short      uint16_t;
typedef unsigned long       uint32_t;

//超时回调函数,无参数无返回值
typedef void (*TimeoutCb)(void);

//软件定时器属性
typedef struct
{
    uint8_t id;                 //软件定时器ID
    uint8_t priority;           //软件定时器优先级
    uint16_t mode;              //BIT15:1表示无限制连续重载,0表示启用重载次数,此时BIT[14:0]表示重载的次数
    uint16_t timeout;           //定时时间
    uint32_t startTimer;        //设置软件定时器的起始时间
    TimeoutCb timeoutFun;       //软件定时的超时回调
} SoftTimer;

//软件定时器节点
typedef struct _SoftTimerNode
{
    SoftTimer property;
    struct _SoftTimerNode *next;
} SoftTimerNode;
2.2 源文件功能
2.2.1 链表头的定义
//初始化一个软件定时器链表默认指向空
static SoftTimerNode *s_softTimerHead = NULL;
2.2.2 基准定时器的参考
  • 在单片机的应用中,单片机中应该有一个基准定时器进行计数,可供外部文件获取计数值。本文中直接从系统中获取一个utc时间,模拟计数值的产生。
  • 检测软件定时器是否超时,这里要特别注意溢出的情况,本文中实现没有考虑这个问题,实际应用中根据情况而定。
//获取系统当前UTC时间
uint32_t GetTimerCount(void)
{
    time_t utcTimer = 0;

    time(&utcTimer);

    return utcTimer;
}

//软件定时器是否超时
uint8_t IsSoftTimerTimeout(SoftTimer *softTimer)
{
    return ((softTimer->startTimer + softTimer->timeout) <= GetTimerCount());
}
2.2.3 获取软件定时器的个数
  • 如果链表是空的话,直接返回0,表示没有定时器。
  • 如果链表存在,则返回实际的软件定时器个数。
//获取软件定时器链表的长度
uint32_t GetSoftTimerCount(void)
{
    SoftTimerNode *cur = NULL;
    uint32_t cnt = 0;

    if (s_softTimerHead == NULL)
    {
        return 0;
    }

    cur = s_softTimerHead;
    //下一个数据不为空
    while (cur->next != NULL)
    {
        cnt++;
        cur = cur->next;
    }

    return 1 + cnt;
}
2.2.4 查找软件定时器的位置
  • 主要用于查找固定的定时器ID是否存在于当前链表的位置,如果不存在返回一个-1。
  • 如果链表中存在这个名称为ID的软件定时器,则返回当前存在的位置。这个位置从0开始表示,即链表头指向的是第一个数据,此时位置为0。
//查找定时器ID的位置
int SearchSoftTimerById(uint8_t id)
{
    SoftTimerNode *cur = NULL;
    int pos = 0;

    if (s_softTimerHead == NULL)
    {
        return -1;
    }

    cur = s_softTimerHead;
    for (;;)
    {
        if (cur->property.id == id)
        {
            break;
        }

        if (cur->next == NULL)
        {
            pos = -1;            //找不到位置
            break;
        }

        cur = cur->next;
        pos++;
    }

    return pos;
}
2.2.5 根据优先级判断应该插入的位置
  • 主要用于判断当前优先级应该位于链表的哪个位置,这个位置从0开始表示,即链表头指向的是第一个数据,此时位置为0。
  • 链表的排放顺序是按照优先级进行顺序排放,优先级的参数值越小,则插入的位置应该越靠前。
  • 如果优先级是相同的,则放在相同优先级结点的最后一个位置。
//获取插入的位置
uint32_t GetInsertPosByPriority(uint16_t priority)
{
    SoftTimerNode *cur = NULL;
    uint32_t pos = 0;

    if (s_softTimerHead == NULL)
    {
        return pos;           //插入在头部
    }

    cur = s_softTimerHead;
    for (;;)
    {
        //优先级比当前的结点要高
        if (priority < cur->property.priority)
        {
            break;
        }

        pos++;
        if (cur->next == NULL)
        {
            break;
        }

        cur = cur->next;
    }

    return pos;
}
2.2.6 删除一个软件定时器
  • 软件ID应该是唯一的,类似于软件定时的名称

                
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值