介绍
依赖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 ###################################################*/
网友评论