本文约定:操作系统仅指嵌入式实时操作系统,如:uCOS、freeRTOS、VxWorks、RT-Thread等,IDE:KeilMDK。
在程序功能不太复杂的情况下,可能我们并不会考虑在程序中加入一个操作系统,而是直接编写裸机程序,裸机程序一般分为前台程序和后台程序,前台是中断级,后台是任务级,这样构建的编程模型就是中断加任务,需要实时处理的事情就放在中断函数中执行,而不那么重要的事情就放在一个死循环中,虽然这样做在大多数情况都是没有问题的,只是程序的实时性差了点儿,模块化不是那么明显,但为了程序的可读性,可扩展性,实时性等,我们可以对其进行改进,例如把各个功能模块细分,因为不是所有的功能模块都需要一直频繁工作,大多数后台功能都是可以周期性执行的,让周期性执行的任务在各个定时器中周期执行,尽量减少死循环中的任务量,这样程序的模块化和实时性就都得到了提升,但是单片机中定时器资源都是有限的,那怎么才能用一个硬件定时器就能够满足更多的定时器需求呢,今天的内容就派上用场了。
实现大致思路就是用一个硬件定时器作为基础定时器,创建多个软件定时器,使用链表进行管理,给用户回调函数或者是标志的方式编程,注意链表参考了linux内核的双向循环链表,其中使用了GNU扩展语法,所以在Keil MDK配置中应该勾选如图框选的选项(如果你的Keil MDK版本过低,可能找不到此选项),但是知道了实现思路,换个数据结构也可以很简单的实现出来。

程序对应源码如下:
#ifndef __LIST_H#define __LIST_H
#include <stdlib.h>
struct list { struct list *prev, *next;};
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
/** * container_of - cast a member of a structure out to the containing structure * @ptr: the pointer to the member. * @type: the type of the container struct this is embedded in. * @member: the name of the member within the struct. * */#define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );})
#define LIST_HEAD_INIT(name) {&(name),&(name)}
#define LIST_HEAD(name) struct list name = \ LIST_HEAD_INIT(name)
static inline void INIT_LIST_HEAD(struct list* list){ list->next = list->prev = list;}
/** * list_entry - get the struct for this entry * @ptr: the &struct list_head pointer. * @type: the type of the struct this is embedded in. * @member: the name of the list_head within the struct. */#define list_entry(ptr, type, member) \ container_of(ptr, type, member)
/** * list_for_each_entry - iterate over list of given type * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_head within the struct. */#define list_for_each_entry(pos, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member); \ &pos->member != (head); \ pos = list_entry(pos->member.next, typeof(*pos), member))
/** * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry * @pos: the type * to use as a loop cursor. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_head within the struct. */#define list_for_each_entry_safe(pos, n, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member), \ n = list_entry(pos->member.next, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = list_entry(n->member.next, typeof(*n), member))
void list_add_head(struct list *_new, struct list *head);void list_add_tail(struct list *_new, struct list *head);void list_del(struct list *entry);int list_is_last(const struct list *entry, const struct list *head);int list_empty(const struct list *head);
#endif/*__LIST_H*/
#include "./list/list.h"
void __list_add(struct list *_new, struct list *prev, struct list *next){ next->prev = _new; _new->next = next; _new->prev = prev; prev->next = _new;}
void list_add_head(struct list *_new, struct list *head){ __list_add(_new, head, head->next);}
void list_add_tail(struct list *_new, struct list *head){ __list_add(_new, head->prev, head);}
void __list_del(struct list *prev, struct list *next){ next->prev = prev; prev->next = next;}
void list_del(struct list *entry){ __list_del(entry->prev, entry->next);
entry->next = NULL; entry->prev = NULL;}
int list_is_last(const struct list *entry, const struct list *head){ return entry->next == head;}
int list_empty(const struct list *head){ return head->next == head;}
#ifndef __SOFT_TIMER_H#define __SOFT_TIMER_H
#include "./list/list.h"
#ifndef NULL#define NULL ((void *)0)#endif
typedef unsigned int uint32_t;
typedef void (*tmr_callBack_t)(void *);
/*定时器参数*/typedef struct tmrDefinition{ //标志位方法使用 uint32_t flag; //当前计数 uint32_t cnt; //打开或者关闭软件定时器 uint32_t on_off; //软件定时器时基 uint32_t base; //是否自动重装载 uint32_t autoReload; //定时器回调函数 tmr_callBack_t tmr_callBack; //回调函数传参 void *para; //链表 struct list tmr_list;}timerTypeDef;
typedef struct tmrDefinition *tmrHandle_t;
/** 获取硬件定时器的时基(此值越小,定时器分辨率越高,但对CPU消耗越大)* 应该被实现,在使用软件定时器相关函数前*/__weak inline uint32_t tmr_get_base(void);/*被定时调用*/void tmr_polling(void);
void tmr_create(tmrHandle_t tmrHandle,uint32_t period,uint32_t autoReload,tmr_callBack_t tmr_callBack,void *para);void tmr_delete(tmrHandle_t tmrHandle);void tmr_change_period(tmrHandle_t tmrHandle,uint32_t period);uint32_t tmr_get_period(tmrHandle_t tmrHandle);void tmr_start(tmrHandle_t tmrHandle);void tmr_stop(tmrHandle_t tmrHandle);void tmr_reset(tmrHandle_t tmrHandle);void tmr_clc_flag(tmrHandle_t tmrHandle);uint32_t tmr_get_flag(tmrHandle_t tmrHandle);
#endif/*__SOFT_TIMER_H*/
#include "./timer/soft_timer.h"
static LIST_HEAD(tmr_list_head);
__weak inline uint32_t tmr_get_base(void){ return 0;}
void tmr_polling(void){ tmrHandle_t tmr = NULL; tmrHandle_t n = NULL; //遍历软件定时器 list_for_each_entry_safe(tmr,n,&tmr_list_head,tmr_list)// list_for_each_entry(tmr,&tmr_list_head,tmr_list) { //如果软件定时器已经开启 if(tmr->on_off) { //计数增加 ++tmr->cnt; //检测定时时间 if(!(tmr->cnt % (tmr->base / tmr_get_base()))) { tmr->flag = 1; //如果不自动重装载则定时一次后关闭对应软件定时器 if(!tmr->autoReload) { tmr_stop(tmr); } //执行注册的回调函数 if(tmr->tmr_callBack) { tmr->tmr_callBack(tmr->para); } } } }}
void tmr_create(tmrHandle_t tmrHandle,uint32_t period,uint32_t autoReload,tmr_callBack_t tmr_callBack,void *para){ tmrHandle->base = period; tmrHandle->autoReload = autoReload; tmrHandle->tmr_callBack = tmr_callBack; tmrHandle->para = para; list_add_head(&(tmrHandle->tmr_list),&tmr_list_head);}
void tmr_delete(tmrHandle_t tmrHandle){ list_del(&(tmrHandle->tmr_list));}
void tmr_change_period(tmrHandle_t tmrHandle,uint32_t period){ tmrHandle->base = period;}
uint32_t tmr_get_period(tmrHandle_t tmrHandle){ return tmrHandle->base;}
void tmr_start(tmrHandle_t tmrHandle){ tmrHandle->on_off = 1;}
void tmr_stop(tmrHandle_t tmrHandle){ tmrHandle->on_off = 0;}
void tmr_reset(tmrHandle_t tmrHandle){ tmrHandle->cnt = 0;}
void tmr_clc_flag(tmrHandle_t tmrHandle){ tmrHandle->flag = 0;}
uint32_t tmr_get_flag(tmrHandle_t tmrHandle){ return tmrHandle->flag;}
从源码可以看出其实本程序都还有很大的改善空间,例如软件定时器的tmr_polling方法,这里是把所有定时器都进行了轮询,其实这样做的效率并不是太高,只是使用的软件定时器少,所以几乎没有性能问题,你要是有兴趣的话可以使用更优的算法去改进它。
因为应对不同编程情况,所以软件定时器实现了两种使用方法,使用示例如下:
示例1://实现时基获取函数inline uint32_t tmr_get_base(void){ //TIMER_BASE 为你的硬件定时器周期,建议1ms或者10ms return TIMER_BASE;}
//在你的硬件定时器中断函数中调用 tmr_polling();函数
//定义一个定时器对象struct tmrDefinition tmr_buzzer;//回调函数声明void tmr_callBack_buzzer(void *para);
void tmr_callBack_buzzer(void *para){ //do something}//注册软件定时器tmr_create(&tmr_buzzer,300,1,tmr_callBack_buzzer,NULL);//开启软件定时器tmr_start(&tmr_buzzer);
示例2://实现时基获取函数inline uint32_t tmr_get_base(void){ //TIMER_BASE 为你的硬件定时器周期,建议1ms或者10ms return TIMER_BASE;}
//在你的硬件定时器中断函数中调用 tmr_polling();函数
//定义一个定时器对象struct tmrDefinition tmr_buzzer;
//注册软件定时器tmr_create(&tmr_buzzer,300,1,NULL,NULL);//开启软件定时器tmr_start(&tmr_buzzer);
//定义一个标志位uint32_t flag = 0;flag = tmr_get_flag(&tmr_ble);
if(flag){ //手动清除标志位(必须) tmr_clc_flag(&tmr_ble); //do something}
注意:
软件定时器的定时周期值应最好为硬件定时器时基的整数倍;
不要阻塞回调函数,不要让回调函数执行大量的任务;
最后附上STM32硬件定时器示例:
#ifndef __TIMER_H#define __TIMER_H
#include "stm32f0xx_hal.h"#include "./timer/soft_timer.h"
/* Definition for TIMx clock resources */#define TIMx TIM3#define TIMx_CLK_ENABLE() __HAL_RCC_TIM3_CLK_ENABLE()
/* Definition for TIMx's NVIC */#define TIMx_IRQn TIM3_IRQn#define TIMx_IRQHandler TIM3_IRQHandler
//定时器时基#define TIMER_BASE 100U
void Timer_Init(void);void TimerStart(void);void TimerStop(void);
#endif/*__TIMER_H*/
#include "./timer/timer.h"#include "app_com.h"
#define OPT_DBG_INF 1U
TIM_HandleTypeDef timerHandle;
//t = ((Period + 1) * (Prescaler + 1)) / SysClock
//定时 TIMER_BASEmsvoid Timer_Init(void){ uint32_t uwPrescalerValue = (uint32_t)(HAL_RCC_GetSysClockFreq() / 1000) - 1; timerHandle.Instance = TIMx; timerHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; timerHandle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; timerHandle.Init.CounterMode = TIM_COUNTERMODE_UP; timerHandle.Init.Period = TIMER_BASE - 1; timerHandle.Init.Prescaler = uwPrescalerValue; timerHandle.Init.RepetitionCounter = 0; HAL_TIM_Base_Init(&timerHandle); TimerStart();}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim){ TIMx_CLK_ENABLE(); HAL_NVIC_SetPriority(TIMx_IRQn, 0x3, 0); HAL_NVIC_EnableIRQ(TIMx_IRQn);}
void TimerStart(void){ HAL_TIM_Base_Start_IT(&timerHandle);}
void TimerStop(void){ HAL_TIM_Base_Stop_IT(&timerHandle);}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){ tmr_polling();}
void TIMx_IRQHandler(void){ HAL_TIM_IRQHandler(&timerHandle);}
inline uint32_t tmr_get_base(void){ return TIMER_BASE;}
后台回复“软件定时器”四个字可获取本文源码。
评论区
登录后即可参与讨论
立即登录