美文网首页
软件定时器の初体验

软件定时器の初体验

作者: imMazda | 来源:发表于2019-11-08 11:26 被阅读0次

介绍

依赖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 ###################################################*/

相关文章

网友评论

      本文标题:软件定时器の初体验

      本文链接:https://www.haomeiwen.com/subject/xjtbbctx.html