软件定时器の初体验

介绍

依赖SysTick 1ms TickIRQ自定义时基15ms产生一个SoftwareTimer TickIRQ判断各个软定时任务是否需要触发执行。

简要

各个软定时任务通过双循环链表的方式组织;
设定临界保护开关防止删除软定时任务时操作链表过程被TickIRQ中断;
创建任务后,支持同时开启/停止多个软定时任务,但删除软定时任务时不支持;
Tick计数采用递减的方式,到零时重装载,和时基一样可修改(通过宏定义 TIM_TICK_MAX / TIM_TICK_BASE);

槽点

未实现机制与策略的独立,双循环链表只能服务于软定时功能,其他模块无法复用;
执行触发处理函数时,置于TickIRQ中进行,影响系统实时性;
亲测在后台大while(1)循环中执行触发处理函数,并开启硬件定时器中断周期性执行“删除-创建”软定时任务,运行一段时间后会DOWN,猜测原因可能是对链表操作未开启临界保护;
未添加“创建/开启/停止”软定时任务时对链表操作过程设定临界保护;
软定时任务定义ID为短无符整型最多16个id,在32bit系统中最多实现32个软定时任务;
TickIRQ中断产生后,判断任务是否需要触发时采用遍历链表方式影响效率(假如链表长度达到一定值)
触发处理函数判断触发标记而后执行回调函数时同样采用遍历链表方式,加上TickIRQ每进一次就需要遍历链表,假如链表长度达到一定(参考值为20)非常影响效率!
在双循环链表中添加软定时任务新节点时采用遍历法影响效率,而应该直接根据头指针->tm_last操作;
未实现软定时任务待触发时间前后优先排序,却采用每发生一次TickIRQ中断就需要遍历链表;
未实现软定时任务触发处理函数以查表方式执行(参考ucos-II),却采用需要遍历链表判断触发标记进而执行;
 遍历链表操作多,整个过程稍显繁杂,不够精简;

领悟

DeleteNode.png
遍历释放掉的节点指针导致DOWN。谨记!
先跨过头节点,直接从第二个节点开始遍历链表,达到代码层面的精简易阅读。骚操作!
/*################################################ bsp_swtim.h START ###################################################*/
#ifndef _BSP_SWTIM_
#define _BSP_SWTIM_

#ifndef SW_DEBUG_INFO
    #define SW_DEBUG_INFO
#endif

#define     DEBUG_ON                            0

#if defined ( SW_DEBUG_INFO ) && ( DEBUG_ON )
    #define     SW_DEBUG                       1
#endif
#define     ENABLE_CRITICAL_PROTECTION              do{                                          \
                                                       criticalProtectionPoint = 1u;                \
                                                    }while(0u);
#define     DISABLE_CRITICAL_PROTECTION             do{                             \
                                                       criticalProtectionPoint = 0u;            \
                                                    }while(0u);

#define     NULL                            0
#define     TIM_ID_MASK                     0x00FFU
#define     TIM_ID_MAX                      0xFFU

#define     TIM_TICK_MAX                    1000U
#define     TIM_TICK_BASE                   15U

typedef     unsigned char               UINT8;
typedef     unsigned short int          UINT16;
typedef     unsigned long int           UINT32;
typedef     unsigned short int          ID; 
typedef     unsigned short int          TIM_BASE;
typedef     unsigned short int          TIM_TICK;   

#define     ID_1                                    ((UINT16)0x0001)
#define     ID_2                                    ((UINT16)0x0002)
#define     ID_3                                    ((UINT16)0x0004)
#define     ID_4                                    ((UINT16)0x0008)
#define     ID_5                                    ((UINT16)0x0010)
#define     ID_6                                    ((UINT16)0x0020)
#define     ID_7                                    ((UINT16)0x0040)
#define     ID_8                                    ((UINT16)0x0080)

#define     IS_TRUE_ID(id)              (((((UINT16)id) & TIM_ID_MASK ) != 0x00u)   \
                                          && ((((UINT16)id) & ~TIM_ID_MASK) == 0x00u))

typedef void (* p_tmout_handle)( UINT16 , ID );

typedef enum
{
    
    FALSE            = 0,
    
    TRUE             = !FALSE,
    
}bool;      

typedef enum 
{
    
    TM_FAIL                      = 0,
    
    TM_OK                        = 1,
    
}eResult_TypeDef;

typedef enum
{
    
    TM_MODE_ONCE                 = 1,
    
    TM_MODE_PERIOD               = 2,
    
}ePeriod_TypeDef;


typedef enum
{
    
    TM_MARK_DISABLE             = 0,
    
    TM_MARK_ENABLE              = 1,
    
}eMark_TypeDef;

typedef enum
{

    TM_READY                    = 1,
    
    TM_RUNNING                  = 2,
    
    TM_STOPPED                  = 3,
    
}eStatus_TypeDef;

typedef enum
{
    
    TM_CREATE                   = 0,
    
    TM_START                    = 1,
    
    TM_RUN                      = 2,
    
    TM_STOP                     = 3,
    
    TM_DELETE                   = 4,
    
    TM_ERROR                    = 5,
    
}eParam_TypeDef;

typedef struct TM_TCB_STRUCT
{
    
    struct TM_TCB_STRUCT *tm_last;
    
    struct TM_TCB_STRUCT *tm_next;
    
    ID      id; 
    
    eMark_TypeDef       mark; 
    
    ePeriod_TypeDef     period;
    
    eStatus_TypeDef     status;
    
    eParam_TypeDef      param;          
    
    UINT16    tim;
    
    UINT32    diff;
    
    p_tmout_handle    pfunc;

}TM_TCB;

eResult_TypeDef SwTimCreate( ID id, UINT16 tmr, ePeriod_TypeDef mod, p_tmout_handle pfunction );
eResult_TypeDef SwTimDelete( ID id );
eResult_TypeDef SwTimStart( ID id );
eResult_TypeDef SwTimStop( ID id );

void SwTim_MarkHandle(void);
void SwTim_IncTick(void);
void Is_Critical_Protection(void);

void Timer1_Callback( UINT16 number, ID id );
void Timer2_Callback( UINT16 number, ID id );
void Timer3_Callback( UINT16 number, ID id );
void Timer4_Callback( UINT16 number, ID id );

#endif
/*################################################ bsp_swtim.h END ###################################################*/

/*################################################ bsp_swtim.c START ###################################################*/

#include "bsp_swtim.h"
#include <stdlib.h>
#include <string.h>

static TM_TCB *ptmListHead = NULL;
static TIM_BASE timBase = 0;
static TIM_TICK timTick = TIM_TICK_MAX;

static TIM_BASE SwTim_GetTimBase(void);
static TIM_TICK SwTim_GetTimTick(void);

static TM_TCB *CreateHeadNode( TM_TCB *ptrAdd );
static TM_TCB * CreateChildNode( TM_TCB *ptrHead, TM_TCB *ptrAdd );
static TM_TCB * DeleteNode( TM_TCB *ptrHead, TM_TCB *ptrDel );
static bool SwTimEnableSta( ID id , eStatus_TypeDef sta );

void Is_Critical_Protection(void);

static void Err_Callback( ID id );

static UINT8 criticalProtectionPoint;

/************************************** Create **************************************/
eResult_TypeDef SwTimCreate( ID id, UINT16 tmr, ePeriod_TypeDef mod, p_tmout_handle pfunction )
{
    TM_TCB *ptrNewNode = NULL;
    TM_TCB *tmTrave = ptmListHead;

    if ( (( id & TIM_ID_MASK ) == 0U ) 
                ||(( id & ~TIM_ID_MASK ) != 0U) )
        return TM_FAIL;
    
    if ( tmr == NULL || tmr > TIM_TICK_MAX )
        return TM_FAIL;
    
    if ( mod != TM_MODE_ONCE && mod != TM_MODE_PERIOD )
        return TM_FAIL;
    
    if ( pfunction == NULL )
        return TM_FAIL;

    ptrNewNode = (TM_TCB *)malloc( sizeof(TM_TCB) );
    
    if ( NULL == ptrNewNode )
        return TM_FAIL;
    
    ptrNewNode->id = id;
    ptrNewNode->mark = TM_MARK_DISABLE;
    ptrNewNode->tim = tmr;
    ptrNewNode->diff = (UINT32)0;
    ptrNewNode->period = mod;
    ptrNewNode->status = TM_READY;
    ptrNewNode->pfunc = pfunction;
    printf("ptrNewNode %d %ld\r\n", id, ptrNewNode);
    if ( NULL != tmTrave )
    {
        /* create child head */
        if ( NULL == (tmTrave = CreateChildNode( tmTrave, ptrNewNode ))){
        //  printf("%ld %ld %ld %ld r\n", *tmTrave->tm_last->tm_next, *tmTrave->tm_next->tm_last , *tmTrave->tm_next, *tmTrave->tm_last);
            return TM_FAIL;
        }
    }
    else 
    {
        /* create head node */
        if ( NULL != (tmTrave = CreateHeadNode( ptrNewNode )))
        {
            ptmListHead = tmTrave;
        }
        else 
        {
            return TM_FAIL;     
        }
    }
    ptrNewNode->pfunc( ptrNewNode->param = TM_CREATE, ptrNewNode->id );

    return TM_OK;
}

static TM_TCB *CreateHeadNode( TM_TCB *ptrAdd )
{
    TM_TCB *ptrHead = NULL;
    
    if ( NULL == ptrAdd )
        return NULL;
    
    ptrHead = ptrAdd;
    ptrHead->tm_next = ptrHead;
    ptrHead->tm_last = ptrHead;
    
    return ptrHead;
}

static TM_TCB * CreateChildNode( TM_TCB *ptrHead, TM_TCB *ptrAdd )
{
    TM_TCB *tmTrave = ptrHead;
    
    if ( NULL == ptrHead || NULL == ptrAdd )
        return NULL;
    
#if 0
    do
    {
        if ( tmTrave->tm_next == ptrHead )
        {
            tmTrave->tm_next = ptrAdd;
            ptrHead->tm_last = ptrAdd;
            ptrAdd->tm_last = tmTrave;
            ptrAdd->tm_next = ptrHead;
            return ptrAdd;
        }
    }while( (tmTrave = tmTrave->tm_next) != ptrHead );
#else
    ptrHead->tm_last->tm_next = ptrAdd;
    ptrAdd->tm_last = ptrAdd->tm_last; 
    ptrHead->tm_last = ptrAdd;
    ptrAdd->tm_next = ptrHead;
#endif
    return NULL;
}

/************************************ Delete *********************************/

eResult_TypeDef SwTimDelete( ID id )
{
    TM_TCB *tmTrave = ptmListHead;
    TM_TCB *ptrHead = ptmListHead;
    TM_TCB *ptrTmp = NULL;

    if ( (( id & TIM_ID_MASK ) == 0U ) 
                ||(( id & ~TIM_ID_MASK ) != 0U) )
        return TM_FAIL;
    
    if ( NULL == ptmListHead )
        return TM_FAIL;
    
    Is_Critical_Protection();
    
ENABLE_CRITICAL_PROTECTION
    do
    {
        if ( id == tmTrave->id )
        {
            ptrTmp = tmTrave;
            tmTrave = tmTrave->tm_next;
            if ( ptmListHead != (ptrHead = DeleteNode( ptrHead, ptrTmp )))
            {
                ptmListHead = ptrHead;
            }
        }
        else 
        {
            tmTrave = tmTrave->tm_next;
        }
    }while ( NULL != ptrHead && tmTrave != ptrHead );
    
DISABLE_CRITICAL_PROTECTION
    
    return TM_FAIL;
} 

static TM_TCB * DeleteNode( TM_TCB *ptrHead, TM_TCB *ptrSub )
{
    if ( ptrHead == ptrSub && ptrHead->tm_next != ptrHead )
    {
        /* delete head node */
        ptrHead = ptrSub->tm_next;
    }
    else if ( ptrHead == ptrSub && ptrHead->tm_next == ptrHead )
    {
        /* only one node */
        ptrHead = NULL;
    }

    ptrSub->tm_last->tm_next = ptrSub->tm_next;
    ptrSub->tm_next->tm_last = ptrSub->tm_last;     
    ptrSub->tm_last = NULL;
    ptrSub->tm_next = NULL;
    
    /* empty memory */
    memset( ptrSub, 0x00, sizeof( TM_TCB ) );
    
    if ( NULL != ptrSub )
    {
        free( ptrSub );
        ptrSub = NULL;
    }

    return ptrHead;
}

/********************************** Start *********************************/

eResult_TypeDef SwTimStart( ID id )
{
    if ( (( id & TIM_ID_MASK ) == 0U ) 
                ||(( id & ~TIM_ID_MASK ) != 0U) )
        return TM_FAIL;
    
    if ( FALSE == SwTimEnableSta( id, TM_RUNNING ) )
        return TM_FAIL;
    
    return TM_OK;
}

static bool SwTimEnableSta( ID id , eStatus_TypeDef sta )
{
    TM_TCB *tmTrave = ptmListHead;
    TIM_TICK tick;
    
    if ( TM_READY != sta && TM_RUNNING != sta && TM_STOPPED != sta )
        return FALSE;
    
    do 
    {
        if ( (ID)FALSE == (id & tmTrave->id) )
            continue;
        if ( sta == TM_RUNNING && (tmTrave->status == TM_READY || tmTrave->status == TM_STOPPED))
        {
            tmTrave->status = TM_RUNNING;
            if ( (tick = SwTim_GetTimTick()) >= tmTrave->tim )
            {
                tmTrave->diff = (UINT32)(tick - tmTrave->tim);
            }
            else 
            {
                tmTrave->diff = (UINT32)(TIM_TICK_MAX - (tmTrave->tim - tick));
            }
        }
        else if ( sta == TM_STOPPED && tmTrave->status == TM_RUNNING )
        {
            tmTrave->status = TM_STOPPED;
            tmTrave->diff = (UINT32)0;
            tmTrave->pfunc( tmTrave->param = TM_STOP, tmTrave->id );
        }
        else 
        {
            tmTrave->pfunc( tmTrave->param = TM_ERROR, tmTrave->id );
            return FALSE;
        }
    }while ( (tmTrave = tmTrave->tm_next) != ptmListHead );
    
    return TRUE;
}

/***************************************** Stop *********************************/

eResult_TypeDef SwTimStop( ID id )
{
    if ( (( id & TIM_ID_MASK ) == 0U ) 
                ||(( id & ~TIM_ID_MASK ) != 0U) )
        return TM_FAIL;
    
    if ( FALSE == SwTimEnableSta( id, TM_STOPPED ) )
        return TM_FAIL;
    
    return TM_OK;
}

/*********************************** Mark and Handle ************************************/

static void SwTim_EnableMark( TIM_TICK tick )
{
    TM_TCB *tmTrave = ptmListHead;
    
    do
    {
        if ( TM_RUNNING != tmTrave->status )
            continue;
        
        if ( tmTrave->diff == (UINT32)tick )
        {
//          tmTrave->mark = TM_MARK_ENABLE;
            tmTrave->pfunc( tmTrave->param = TM_RUN, tmTrave->id );
//          /* clear mark */
//          tmTrave->mark = TM_MARK_DISABLE;
            if ( TM_MODE_ONCE != tmTrave->period )
            {
                tick = SwTim_GetTimTick();
                
                if ( tick >= tmTrave->tim )
                {
                    tmTrave->diff = (UINT32)(tick - tmTrave->tim);
                }
                else 
                {
                    tmTrave->diff = (UINT32)(TIM_TICK_MAX - (tmTrave->tim - tick));
                }
            }
            else 
            {
                tmTrave->status = TM_READY;
                tmTrave->diff = (UINT32)0;
            }
        }
    }while( (tmTrave = tmTrave->tm_next) != ptmListHead );
}

void SwTim_MarkHandle(void)
{
    TM_TCB *tmTrave = ptmListHead;
    TIM_TICK tick;
    
IS_CRITICAL_PROTECTION

ENABLE_CRITICAL_PROTECTION  

    do 
    {
        if ( TM_MARK_ENABLE != tmTrave->mark )
            continue ;
        
        /* handle callback func */
        tmTrave->pfunc( tmTrave->param = TM_RUN, tmTrave->id );
        /* clear mark */
        tmTrave->mark = TM_MARK_DISABLE;
        
        if ( TM_MODE_ONCE != tmTrave->period )
        {
            tick = SwTim_GetTimTick();
            
            if ( tick >= tmTrave->tim )
            {
                tmTrave->diff = (UINT32)(tick - tmTrave->tim);
            }
            else 
            {
                tmTrave->diff = (UINT32)(TIM_TICK_MAX - (tmTrave->tim - tick));
            }
        }
        else 
        {
            tmTrave->status = TM_READY;
            tmTrave->diff = (UINT32)0;
        }
    }while( (tmTrave = tmTrave->tm_next) != ptmListHead );
    
DISABLE_CRITICAL_PROTECTION
    
}

/*********************************** Critical Protection *****************************/

void Is_Critical_Protection(void)
{
    IS_CRITICAL_PROTECTION;
}

/************************************ SysTick IRQ ***************************************/
/**
  * @brief This function handles System tick timer.
  */
void SysTick_Handler(void)
{
  /* USER CODE BEGIN SysTick_IRQn 0 */

  /* USER CODE END SysTick_IRQn 0 */
  HAL_IncTick();
  /* USER CODE BEGIN SysTick_IRQn 1 */
  SwTim_IncTick();
  /* USER CODE END SysTick_IRQn 1 */
}

/**************************************** Tick Base **********************************/

void SwTim_IncTick(void)
{
    /* TIM_BASE -- 10 ms  */
    if ( timBase++ >= TIM_TICK_BASE - 1U )
    {
        
IS_CRITICAL_PROTECTION
        
        if ( timTick-- == 0U )
        {
            timTick = TIM_TICK_MAX;
        }
        timBase = 0;
        SwTim_EnableMark( timTick );
        
    }
}

static TIM_BASE SwTim_GetTimBase(void)
{
    return timBase;
}

static TIM_TICK SwTim_GetTimTick(void)
{
    return timTick;
}

/************************************** call back ************************************/

void Debug_Printf( eParam_TypeDef par, ID id )
{
    #if ( SW_DEBUG )
    switch ( par )
    {
        case TM_CREATE:
            printf("Timer%d create succes!\r\n", id );
        break;
        case TM_START:
            printf("Timer%d start!\r\n", id );
        break;
        case TM_RUN:
            printf("Timer%d run!\r\n", id );
        break;
        case TM_STOP:
            printf("Timer%d stop!\r\n", id );
        break;
        case TM_DELETE:
            printf("Timer%d delete succes!\r\n", id );
        break;
        case TM_ERROR:
            printf("Timer%d start/stop error!\r\n", id );
        break;
        default:
            break;
    }
    #endif
}

void Timer1_Callback(UINT16 number, ID id )
{
    Debug_Printf( number, id );
    LED1_TOGGLE;
}

void Timer2_Callback(UINT16 number, ID id )
{
    Debug_Printf( number, id );
    LED2_TOGGLE;
}

void Timer3_Callback(UINT16 number, ID id )
{
    Debug_Printf( number, id );
    LED3_TOGGLE;
}

void Timer4_Callback(UINT16 number, ID id )
{
    Debug_Printf( number, id );
    LED4_TOGGLE;
}

static void Err_Callback( ID id )
{
    printf( "Delete timer err : %d \r\n", id );
}

/************************************ Software Timer Start ***************************************/
void SoftwareTimerStart(void)
{
    eResult_TypeDef res;

    res =SwTimCreate( ID_1, 5, TM_MODE_PERIOD, Timer1_Callback );
    if(res == TM_FAIL)
    {
        printf("create %d error!\r\n",ID_1);
    }
    res =SwTimCreate( ID_2, 7, TM_MODE_PERIOD, Timer2_Callback );
    if(res == TM_FAIL)
    {
        printf("create %d error!\r\n",ID_2);
    }
    res =SwTimCreate( ID_3, 50, TM_MODE_PERIOD, Timer3_Callback );
    if(res == TM_FAIL)
    {
        printf("create %d error!\r\n",ID_3);
    }
    res = SwTimCreate( ID_4, 100, TM_MODE_PERIOD, Timer4_Callback );
    if(res == TM_FAIL)
    {
        printf("create %d error!\r\n",ID_4);
    }
    res = SwTimStart( ID_1|ID_2|ID_3|ID_4 );
    if(res == TM_FAIL)
    {
        printf("start error!\r\n");
    }
}
/*################################################ bsp_swtim.c END ###################################################*/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 222,183评论 6 516
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,850评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,766评论 0 361
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,854评论 1 299
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,871评论 6 398
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,457评论 1 311
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,999评论 3 422
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,914评论 0 277
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,465评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,543评论 3 342
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,675评论 1 353
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,354评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,029评论 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,514评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,616评论 1 274
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,091评论 3 378
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,685评论 2 360