软件定时器的原理和创建

由于硬件定时器资源有限,所以引出软件定时器

实现方式分为两种,一种是通过任务实现,一种是直接搭载在系统时钟节拍上

这里需要注意的是在软件定时器中因实现方式不同,分为了软件定时器和硬件定时器,前者是通过任务实现,后者是在时钟节拍上运行即硬件定时器

typedef struct _tTimer
{
    // 链表结点
    tNode linkNode;
    // 初次启动延后的ticks数
    uint32_t startDelayTicks;
    // 周期定时时的周期tick数
    uint32_t durationTicks;
    // 当前定时递减计数值
    uint32_t delayTicks;
    // 定时回调函数
    void (*timerFunc) (void * arg);
    // 传递给回调函数的参数
    void * arg;
    // 定时器配置参数
    uint32_t config;
    // 定时器状态
    tTimerState state;
}tTimer;

void tTimerInit (tTimer * timer, uint32_t delayTicks, uint32_t durationTicks,
                 void (*timerFunc) (void * arg), void * arg, uint32_t config)
{
    tNodeInit(&timer->linkNode);
    timer->startDelayTicks = delayTicks;
    timer->durationTicks = durationTicks;
    timer->timerFunc = timerFunc;
    timer->arg = arg;
    timer->config = config;
    // 如果初始启动延时为0,则使用周期值
    if (delayTicks == 0)
    {
        timer->delayTicks = durationTicks;
    }
    else
    {
        timer->delayTicks = timer->startDelayTicks;
    }
    timer->state = tTimerCreated;
}

这里的定时器支持设置初始值和周期,这两个可以不一样

软件定时器的启动和停止

static tList tTimerHardList;
// "软"定时器列表
static tList tTimerSoftList;
// 用于访问软定时器列表的信号量
static tSem tTimerProtectSem;
// 用于软定时器任务与中断同步的计数信号量
static tSem tTimerTickSem;
void tTimerStart (tTimer * timer)
{
    switch (timer->state)
    {
        case tTimerCreated:
        case tTimerStopped:
            timer->delayTicks = timer->startDelayTicks ? timer->startDelayTicks : timer->durationTicks;
            timer->state = tTimerStarted;
            // 根据定时器类型加入相应的定时器列表
            if (timer->config & TIMER_CONFIG_TYPE_HARD)
            {
                // 硬定时器,在时钟节拍中断中处理,所以使用critical来防护
                uint32_t status = tTaskEnterCritical();
                // 加入硬定时器列表
                tListAddLast(&tTimerHardList, &timer->linkNode);
                tTaskExitCritical(status);
            }
            else
            {
                // 软定时器,先获取信号量。以处理此时定时器任务此时同时在访问软定时器列表导致的冲突问题
                tSemWait(&tTimerProtectSem, 0);
                tListAddLast(&tTimerSoftList, &timer->linkNode);
                tSemNotify(&tTimerProtectSem);
            }
            break;
        default:
            break;
    }
}
void tTimerStop (tTimer * timer)
{
    switch (timer->state)
    {
        case tTimerStarted:
        case tTimerRunning:
            // 如果已经启动,判断定时器类型,然后从相应的延时列表中移除
            if (timer->config & TIMER_CONFIG_TYPE_HARD)
            {
                // 硬定时器,在时钟节拍中断中处理,所以使用critical来防护
                uint32_t status = tTaskEnterCritical();
                // 从硬定时器列表中移除
                tListRemove(&tTimerHardList, &timer->linkNode);
                tTaskExitCritical(status);
            }
            else
            {
                // 软定时器,先获取信号量。以处理此时定时器任务此时同时在访问软定时器列表导致的冲突问题
                tSemWait(&tTimerProtectSem, 0);
                tListRemove(&tTimerSoftList, &timer->linkNode);
                tSemNotify(&tTimerProtectSem);
            }
            timer->state = tTimerStopped;
            break;
        default:
            break;
    }
}

这些全局变量前两个是列表的创建,后面是为了保护定时器所以创建了信号量,这里需要注意的是为什么软件定时器需要信号量,主要是因为工作环境不一样

  • 硬件定时器:在中断上下文中运行,操作的定时器列表不会与其他任务并发访问,因此只需要

界区保护,不需要信号量

  • 软件定时器:在任务上下文中运行,可能与其他任务并发访问定时器列表,因此需要使用信号量

来保证对列表的互斥访问,防止竞争条件。

tTimerStart和tTimerStop逻辑大致一样,这里使用case语句的原因是因为后期如果想在TimerCreated的时候修改比较方便;逻辑就是先赋值信息,然后根据定时器类型操作,如果是硬件定时器就使用关中断的方式插入列表,如果是软件定时器,使用信号量的方式插入列表;停止的话,就是把插入改为移除

static void tTimerCallFuncList (tList * timerList)
{
    tNode * node;   
    // 检查所有任务的delayTicks数,如果不0的话,减1。
    for (node = timerList->headNode.nextNode; node != &(timerList->headNode); node = node->nextNode)
    {
        tTimer * timer = tNodeParent(node, tTimer, linkNode);
        // 如果延时已到,则调用定时器处理函数
        if ((timer->delayTicks == 0) || (--timer->delayTicks == 0))
        {
            // 切换为正在运行状态
            timer->state = tTimerRunning;
            // 调用定时器处理函数
            timer->timerFunc(timer->arg);
            // 切换为已经启动状态
            timer->state = tTimerStarted;
            if (timer->durationTicks > 0)
            {
                // 如果是周期性的,则重复延时计数值
                timer->delayTicks = timer->durationTicks;
            }
            else
            {
                // 否则,是一次性计数器,中止定时器
                tListRemove(timerList, &timer->linkNode);
                timer->state = tTimerStopped;
            }
        }
    }
}
static tTask tTimeTask;
static tTaskStack tTimerTaskStack[TINYOS_TIMERTASK_STACK_SIZE];
static void tTimerSoftTask (void * param)
{
    for (;;)
    {
        // 等待系统节拍发送的中断事件信号
        tSemWait(&tTimerTickSem, 0);
        // 获取软定时器列表的访问权限
        tSemWait(&tTimerProtectSem, 0);
        // 处理软定时器列表
        tTimerCallFuncList(&tTimerSoftList);
        // 释放定时器列表访问权限
        tSemNotify(&tTimerProtectSem);
    }
}
void tTimerModuleTickNotify (void)
{
    uint32_t status = tTaskEnterCritical();
    // 处理硬定时器列表
    tTimerCallFuncList(&tTimerHardList);
    tTaskExitCritical(status);
    // 通知软定时器节拍变化
    tSemNotify(&tTimerTickSem);
}
void tTimerModuleInit (void)
{
    tListInit(&tTimerHardList);
    tListInit(&tTimerSoftList);
    tSemInit(&tTimerProtectSem, 1, 1);
    tSemInit(&tTimerTickSem, 0, 0);
#if TINYOS_TIMERTASK_PRIO >= (TINYOS_PRO_COUNT - 1)
    #error "The proprity of timer task must be greater then (TINYOS_PRO_COUNT - 1)"
#endif
    tTaskInit(&tTimeTask, tTimerSoftTask, (void *)0,
        TINYOS_TIMERTASK_PRIO, &tTimerTaskStack[TINYOS_TIMERTASK_STACK_SIZE]);
}

tTimerCallFuncList是一个辅助函数,可以看到函数那里有一个static—只允许本文件调用,主要功能是遍历指定的定时器列表,调用各个定时器处理函数,主要逻辑是遍历,然后调用处理函数,赋值相关信息等

tTimerSoftTask是软件定时器的任务,里面也使用了信号量进行安全操作,需要注意的是,这里的信号量是wait了tTimerTickSem和tTimerProtectSem,前者是保护tTimerModuleTickNotify,后者是保护这个任务

tTimerModuleTickNotify中对软件定时器和硬件定时器分别进行通知调用

tTimerModuleInit就是对模块的初始化,包括初始化信号量和任务

软件定时器的删除和状态查询

typedef struct _tTimerInfo
{
    // 初次启动延后的ticks数
    uint32_t startDelayTicks;
    // 周期定时时的周期tick数
    uint32_t durationTicks;
    // 定时回调函数
    void (*timerFunc) (void * arg);
    // 传递给回调函数的参数
    void * arg;
    // 定时器配置参数
    uint32_t config;
    // 定时器状态
    tTimerState state;
}tTimerInfo;

void tTimerDestroy (tTimer * timer)
{
    tTimerStop(timer);
    timer->state = tTimerDestroyed;
}

void tTimerGetInfo (tTimer * timer, tTimerInfo * info)
{
    uint32_t status = tTaskEnterCritical();
    info->startDelayTicks = timer->startDelayTicks;
    info->durationTicks = timer->durationTicks;
    info->timerFunc = timer->timerFunc;
    info->arg = timer->arg;
    info->config = timer->config;
    info->state = timer->state;
    tTaskExitCritical(status);
}

新增tTimerInfo信息查询结构体,使用tTimerGetInfo把相关信息赋值

删除比较简单,就使用tTimerStop即可