基于OSAL的嵌入式裸机事件驱动框架——软件定时器osal_timer
参考B站up主【架构分析】嵌入式祼机事件驱动框架
感谢大佬分享
软件定时器
定时器使用链表将所有定时器连接起来
需要将定时器更新函数osal_timer_update放在Systick中断中
osal_timer.h
#ifndef OSAL_TIMER_H
#define OSAL_TIMER_H
#include "osal.h"
#include "osal_event.h"
typedef struct
{
void *next; /* next timer in the list */
osal_uint16_t timeout; /* timeout value in ticks */
osal_uint16_t reload; /* reload value in ticks */
event_id_t event_id; /* event id */
task_id_t task_id; /* task id */
}osal_timer_t;
/*接口*/
osal_uint32_t osal_get_systick(void);
osal_uint8_t osal_timer_startex(task_id_t task_id, event_id_t event_id, osal_uint16_t timeout);
osal_uint8_t osal_timer_startre(task_id_t task_id, event_id_t event_id, osal_uint16_t timeout);
osal_uint8_t osal_timer_stopex(task_id_t task_id, event_id_t event_id);
osal_uint8_t osal_timer_num(void);
osal_uint16_t osal_timer_gettimeoutex(task_id_t task_id, event_id_t event_id);
void osal_timer_update(void);
#endif
osal_timer.c
#include "osal.h"
#include "osal_event.h"
#include "osal_memory.h"
#include <string.h>
/*********************************************************************
* @fn osal_init_system * * @brief * * This function initializes the "task" system by creating the * tasks defined in the task table (OSAL_Tasks.h). * * @param void * * @return ZSUCCESS */osal_uint8_t osal_init_system( void )
{
// Initialize the Memory Allocation System
#if OSALMEM_METRICS
osal_mem_init();
#endif
return ( OSAL_SUCCESS );
}
/*********************************************************************
* @fn osal_start_system * * @brief * * This function is the main loop function of the task system. It * will look through all task events and call the task_event_processor() * function for the task with the event. If there are no events (for * all tasks), this function puts the processor into Sleep. * This Function doesn't return. * * @param void * * @return none *//*可以考虑不加临界区,直接用原子操作,减少开销*/
void osal_system_start(void)
{
event_asb_t events,ret_events;
osal_task_t *task_active;
while(1)
{
task_active = osal_task_active();
if ( task_active != NULL )
{
OSAL_ENTER_CRITICAL();
events = task_active->events;
task_active->events = SYS_EVE_NONE;
if(events != SYS_EVE_NONE)
{
if(task_active->ops->handler != NULL)
{
ret_events = task_active->ops->handler(task_active->task_id,events);
OSAL_ENTER_CRITICAL();
task_active->events |= ret_events;
OSAL_EXIT_CRITICAL();
}
}
}
}
}
/*********************************************************************
* @fn osal_strlen * * @brief * * Calculates the length of a string. The string must be null * terminated. * * @param char *pString - pointer to text string * * @return int - number of characters */int osal_strlen( char *pString )
{
return (int)( strlen( pString ) );
}
/*********************************************************************
* @fn osal_memcpy * * @brief * * Generic memory copy. * * Note: This function differs from the standard memcpy(), since * it returns the pointer to the next destination uint8. The * standard memcpy() returns the original destination address. * * @param dst - destination address * @param src - source address * @param len - number of bytes to copy * * @return pointer to end of destination buffer */void *osal_memcpy( void *dst, const void *src, unsigned int len )
{
osal_uint8_t *pDst;
const osal_uint8_t *pSrc;
pSrc = src;
pDst = dst;
while ( len-- )
*pDst++ = *pSrc++;
return ( pDst );
}
/*********************************************************************
* @fn osal_revmemcpy * * @brief Generic reverse memory copy. Starts at the end of the * source buffer, by taking the source address pointer and moving * pointer ahead "len" bytes, then decrementing the pointer. * * Note: This function differs from the standard memcpy(), since * it returns the pointer to the next destination uint8. The * standard memcpy() returns the original destination address. * * @param dst - destination address * @param src - source address * @param len - number of bytes to copy * * @return pointer to end of destination buffer */void *osal_revmemcpy( void *dst, const void *src, unsigned int len )
{
osal_uint8_t *pDst;
const osal_uint8_t *pSrc;
pSrc = src;
pSrc += (len-1);
pDst = dst;
while ( len-- )
*pDst++ = *pSrc--;
return ( pDst );
}
/*********************************************************************
* @fn osal_memdup * * @brief Allocates a buffer [with osal_mem_alloc()] and copies * the src buffer into the newly allocated space. * * @param src - source address * @param len - number of bytes to copy * * @return pointer to the new allocated buffer, or NULL if * allocation problem. */void *osal_memdup( const void *src, unsigned int len )
{
osal_uint8_t *pDst;
pDst = osal_mem_alloc( len );
if ( pDst )
{
osal_memcpy( pDst, src, len );
}
return ( (void *)pDst );
}
/*********************************************************************
* @fn osal_memcmp * * @brief * * Generic memory compare. * * @param src1 - source 1 addrexx * @param src2 - source 2 address * @param len - number of bytes to compare * * @return TRUE - same, FALSE - different */osal_uint8_t osal_memcmp( const void *src1, const void *src2, unsigned int len )
{
const osal_uint8_t *pSrc1;
const osal_uint8_t *pSrc2;
pSrc1 = src1;
pSrc2 = src2;
while ( len-- )
{
if( *pSrc1++ != *pSrc2++ )
return OSAL_ERROR;
}
return OSAL_SUCCESS;
}
/*********************************************************************
* @fn osal_memset * * @brief * * Set memory buffer to value. * * @param dest - pointer to buffer * @param value - what to set each uint8 of the message * @param size - how big * * @return pointer to destination buffer */void *osal_memset( void *dest, osal_uint8_t value, int len )
{
return memset( dest, value, len );
}
osal_timer_create
定时器创建
1如果定时器已存在,则更新定时器的timeout
2如果不存在,则创建一个心得定时器并将其插入定时器链表尾部
/*********************************************************************
* @fn osal_timer_create * * @brief Add a timer to the timer list. * Ints must be disabled. * * @param task_id * @param event_flag * @param timeout * * @return osal_timer_t * - pointer to newly created timer */static osal_timer_t *osal_timer_create(task_id_t task_id, event_id_t event_id, osal_uint16_t timeout)
{
osal_timer_t *timer_new;
osal_timer_t *timer_sech;
//check if timer already exists, if yes, update timeout
timer_new = osal_timer_find(task_id, event_id);
if (timer_new!= NULL)
{
timer_new->timeout = timeout;
return timer_new;
}else{
timer_new = (osal_timer_t *)osal_mem_alloc(sizeof(osal_timer_t));
if (timer_new != NULL)
{
timer_new->task_id = task_id;
timer_new->event_id = event_id;
timer_new->timeout = timeout;
timer_new->next = (void *)NULL;
timer_new->reload = 0;
/* add timer to list
* if list is empty, set head to new timer * else, traverse list to find end of list and add new timer */ if(timer_head == NULL){
timer_head = timer_new;
}else{
timer_sech = timer_head;
while(timer_sech->next != NULL)
{
timer_sech = timer_sech->next;
}
timer_sech->next = timer_new;
}
return timer_new;
}
else{
return NULL;
}
}
}
osal_timer_startex
一次性定时器
其reload值为0
/*********************************************************************
* @fn osal_start_timerEx * * @brief * * This function is called to start a timer to expire in n mSecs. * When the timer expires, the calling task will get the specified event. * * @param uint8 taskID - task id to set timer for * @param uint16 event_id - event to be notified with * @param UNINT16 timeout_value - in milliseconds. * * @return OSAL_SUCCESS, or INVALID_TIMER. */osal_uint8_t osal_timer_startex(task_id_t task_id, event_id_t event_id, osal_uint16_t timeout)
{
osal_timer_t *timer_new;
OSAL_ENTER_CRITICAL();
timer_new = osal_timer_create(task_id, event_id, timeout);
OSAL_EXIT_CRITICAL();
return ((timer_new != NULL) ? OSAL_SUCCESS : INVALID_TIMER);
}
osal_timer_startre
可自动重装载定时器
现创建一个一次性定时器,然后将其自动重装载值设为 timeout
/*********************************************************************
* @fn osal_start_reload_timer * * @brief * * This function is called to start a timer to expire in n mSecs. * When the timer expires, the calling task will get the specified event * and the timer will be reloaded with the timeout value. * * @param uint8 taskID - task id to set timer for * @param uint16 event_id - event to be notified with * @param UNINT16 timeout_value - in milliseconds. * * @return OSAL_SUCCESS, or NO_TIMER_AVAIL. */osal_uint8_t osal_timer_startre(task_id_t task_id, event_id_t event_id, osal_uint16_t timeout)
{
osal_timer_t *timer_new;
OSAL_ENTER_CRITICAL();
timer_new = osal_timer_create(task_id, event_id, timeout);
if(timer_new != NULL){
timer_new->reload = timeout;
}
OSAL_EXIT_CRITICAL();
return ((timer_new != NULL) ? OSAL_SUCCESS : INVALID_TIMER);
}
osal_timer_del
删除定时器,将定时器对应的event_id设为无事件SYS_EVE_NONE
此定时器会在定时器更新函数中被清理
/*********************************************************************
* @fn osal_timer_del * * @brief Delete a timer from a timer list. * * @param table * @param rmTimer * * @return none */static void osal_timer_del(osal_timer_t *timer)
{
if(timer!= NULL){
timer->event_id = SYS_EVE_NONE;
}
}
osal_timer_update
将次函数放在systick中断里,每个tick调用一次
1更新OSAL时钟osal_systick
2遍历定时器链表,将每个定时器的timeout–,有时间到的,将其对应的任务事件置位(这里仅置位,函数执行在osal_system_start中执行)
3清理已删除的定时器
/*********************************************************************
* @fn osal_timer_update * * @brief Update the timer structures for timer ticks. * * @param none * * @return none */void osal_timer_update(void)
{
osal_timer_t *timer_sech;//遍历定时器
osal_timer_t *timer_prev;//保存上一个定时器
OSAL_ENTER_CRITICAL();
osal_systick++;
OSAL_EXIT_CRITICAL();
//链表非空,遍历定时器链表
if(timer_head!= NULL)
{
timer_sech = timer_head;
timer_prev = (void *)NULL;
while(timer_sech != NULL)
{
osal_timer_t *timer_free = NULL;//保存要删除的定时器,单词定时器运行一次后就要删除,事件为SYS_EVE_NONE的定时器也要删除
OSAL_ENTER_CRITICAL();
timer_sech->timeout--;
//可重载定时器超时,事件有效,重新加载定时器
if((timer_sech->timeout == 0) && (timer_sech->event_id != SYS_EVE_NONE) && (timer_sech->reload != 0))
{
osal_task_seteve(timer_sech->task_id, timer_sech->event_id);
timer_sech->timeout = timer_sech->reload;
}
//单次定时器超时,或存在事件为SYS_EVE_NONE的定时器,都将其幅值为timer_free,等待删除
if((timer_sech->timeout == 0) || (timer_sech->event_id == SYS_EVE_NONE))
{
//如果是第一次进来就删除,timer_sech要删除,将timer_head指向别处NULL
if(timer_prev == NULL){
timer_head = timer_sech->next;
}else{
//timer_sech删除,将timer_prev->next指向timer_sech->next,中间扣掉了timer_sech
timer_prev->next = timer_sech->next;
}
//将要删除的定时器保存到timer_free,等待删除,timer_sech继续遍历
timer_free = timer_sech;
timer_sech = timer_sech->next;
}else{
//无删除定时器,遍历下一个定时器
timer_prev = timer_sech;
timer_sech = timer_sech->next;
}
OSAL_EXIT_CRITICAL();
//存在要删除的定时器,释放内存
if(timer_free != NULL)
{
//如果是单次定时器超时,则发送事件后再删除
if(timer_free->timeout == 0){
osal_task_seteve(timer_free->task_id, timer_free->event_id);
}
osal_mem_free(timer_free);
}
}
}
}