本帖最后由 正版长小强 于 2023-11-11 07:40 编辑
#每日话题# #申请原创# #技术资源#
《芯圣51内核单片机HC89F0xx库分享》- 软定时器库
一、前言
项目需要近期接触芯圣HC89F0xx系列51核单片机 ,超高的 性价比、灵活性 深深的吸引了我 。 为后续项目更快落地,开始做基础功能的封装库工作。
在代码编写过程中 时间要素是关键的 ,初期往往使用阻塞式Delay 延时函数 ,慢慢的 会借用定时器 ,状态机 方式 ,几年前在51核上做一个软定时器模块,一直在用,芯圣上也可以使用 ,为项目功能验证带来极大便利 。现在分享出来 。
往期分享地址 :
《芯圣51内核单片机HC89F0xx库分享》-GPIO库
/*************************************************************************************************************************/
二、分享内容
软件定时器工作原理 , 首先需要为软定时器提供一个 基准心跳 一般是10mS , 这决定了定时器的计时最小单位 。心跳 可以是其他的 。软定时器 提供了一个定时器结构体 ,以及相关的 操作接口 。
1: 声明一个软定时器 xdata new_t sec_t; 定时器是静态的 ,声明了就如变量一般一直存在 ,占用内容 ,推荐大家在xdata 区域定义
2: 配置定时器 new_time_create(&sec_t ,NEW_TIME_TICK_SEC,NEW_TIME_MODE_WHILE,sec_run);
&sec_t :我们刚刚声明的定时器地址
NEW_TIME_TICK_SEC :设定定时器溢出时间 ,这个宏定义与 基准心跳有关 10mS 时值是100 ,注意定时器的计数值范围是 0 - 255 U8 类型
NEW_TIME_MODE_WHILE :定时器工作模式 ,默认代码 两种 持续工作 、到达时间后关闭 ,可通过修改代码实现设定次数
sec_run :回调函数 ,定时器到达时间后会执行这个函数 ,当前没有做参数功能 。
3:启动定时器 new_time_cmd(&sec_t,NEW_TIME_ON);
使用过程很简单 ,需要注意 :不要重复配置定时器(没有做定时器ID 管理 ,制作了链表功能,注意就好 最小配置) 会导致定时器链表异常 ,内存溢出。
/*************************************************************************************************************************/
三、移植过程
在主函数中 10mS 周期 new_time_run_main();
在中断函数中10mS 调用 new_time_run_clock();
注意不要在中断函数中调用new_time_run_main(); 会让定时周期不可控, 如果你对定时器时间要求很低 可以把这两个函数都放在 主函数 10mS 变量盘内。
例:
bit t_10ms_flag = 0;
void Timer1_ISR (void) interrupt TIMER1_VECTOR
{
TL1 = TL1_VALUE;
TH1 = TH1_VALUE;
#ifdef USER_NEW_TIME
new_time_run_clock();
#endif
}
//主函数 while(1) 循环内
if(t_10ms_flag == 1) //10MS 心跳标志
{
#ifdef USER_NEW_TIME
new_time_run_main();
#endif
t_10ms_flag = 0;
}
如果不出意外 :就可以开心的使用软定时器了 ,如果你内存允许 ,可以开很多软定时器 。
/*************************************************************************************************************************/
四、定时器数据机构 、宏定义
struct new_time
{
unsigned char settime; //设定时间
unsigned char contr; //当前时间
unsigned char mode:1; //模式
unsigned char on_off:1; //开关
unsigned char oveflag:1; //溢出标志
void (*run)(void); //回调函数
struct new_time *up_p; //链表
struct new_time *down_p;
};
typedef struct new_time new_t;
#define NEW_TIME_TICK_SEC (100) //定时器每秒最大定时次数
#define NEW_TIME_ON 1 //定时器状态打开
#define NEW_TIME_OFF 0 //定时器状态关闭
#define NEW_TIME_MODE_WHILE 0 //定时器循环触发模式
#define NEW_TIME_MODE_ONE 1 //定时器触发一次后处于关闭状态
/*************************************************************************************************************************/
五、源码分享#ifdef USER_NEW_TIME
struct new_time
{
unsigned char settime;
unsigned char contr;
unsigned char mode:1;
unsigned char on_off:1;
unsigned char oveflag:1;
void (*run)(void);
struct new_time *up_p;
struct new_time *down_p;
};
typedef struct new_time new_t;
#define NEW_TIME_TICK_SEC (100) //定时器每秒最大定时次数
#define NEW_TIME_ON 1 //定时器状态打开
#define NEW_TIME_OFF 0 //定时器状态关闭
#define NEW_TIME_MODE_WHILE 0 //定时器循环触发模式
#define NEW_TIME_MODE_ONE 1 //定时器触发一次后处于关闭状态
/*
函数功能:创建一个软定时器
参数:*nt定时器结构,settime 定时时间10ms最小单位 ,mode 模式,(*run)定时器到达时间要运行的函数
返回:无
*/
extern void new_time_create(new_t *nt,unsigned char settime,unsigned char mode,void (*run)(void));
/*
函数功能:控制某个定时器状态
参数:*nt定时器结构,off_on 定时器状态
返回:无
副作用:无
*/
extern void new_time_cmd(new_t *nt,unsigned char off_on);
/*
函数功能:修改定时器周期
参数:*nt定时器结构,settime 定时时间10ms最小单位
返回:无
*/
extern void new_time_set(new_t *nt,unsigned short settime);
/*
函数功能:清空定时器计时值
参数:*nt定时器结构
返回:无
*/
extern void new_time_reset(new_t *nt);
/*
函数功能:删除这个定时器检查任务
参数:*nt定时器结构
返回:无
*/
/*
extern void new_time_delete(new_t *nt);
*/
/*
函数功能:查询计时结束的定时器并执行指向函数
参数:无
返回:无
循环调用
*/
extern void new_time_run_main(void);
#endif
#ifdef USER_NEW_TIME
xdata unsigned char new_time_num=0;
xdata new_t *new_time_p = 0; //最后定时器的地址
xdata new_t *new_time_C_p = 0; //最后定时器的地址
xdata new_t *new_time_R_p = 0; //最后定时器的地址
xdata new_t *new_time_S_p = 0; //最后定时器的地址
//new_t
//定时器创建
void new_time_create(new_t *nt,unsigned char settime,unsigned char mode,void (*run)(void))
{
memset(nt,0,sizeof(struct new_time));
// if(up_t->down_p == 0)
{
nt->contr = 0;
nt->mode = mode;
nt->settime = settime;
nt->run = run;
if(new_time_num ==0)
{
nt->up_p = nt;
nt->down_p = nt;
new_time_C_p = nt;
new_time_R_p = nt;
new_time_S_p = nt;
}
else
{
nt->down_p = new_time_p;
nt->up_p = new_time_p->up_p;
new_time_p->up_p = nt;
}
new_time_p = nt;
new_time_num++;
new_time_S_p->down_p = nt;
}
}
//设定定时器
void new_time_set(new_t *nt,unsigned short settime)
{
nt->settime = settime;
nt->contr = 0;
}
//删除定时器
/*
void new_time_delete(new_t *nt)
{
xdata new_t *up_t_p = (new_t *)nt->up_p; //上一个定时器
xdata new_t *up_t_d = (new_t *)nt->down_p; //下一个定时器
unsigned char dflag =0;
if((up_t_p ==0 )&&(up_t_d == 0))return;
if( new_time_num > 1) //已经创建定时器
{
if(up_t_p!=0) //如果上一个定时器存在
{
if(up_t_d!=0)up_t_p->down_p = nt->down_p;//如果下边存在定时器把下链表地址改为此地址
else {up_t_p->down_p =0; new_time_p = (new_t *)up_t_p;} //如果下面没有定时器了把最后定时器的指针改为上一个定时器
dflag =1; //定时器数量减
}
else
{
up_t_d->up_p = 0;
dflag =1;
}
if(up_t_d!=0)//如果下一个定时器存在
{
if(up_t_p!=0)up_t_d->up_p = nt->up_p;
else up_t_d->up_p = 0;
dflag =1;
}
if(dflag)new_time_num --;
}
else if( new_time_num == 1)
{
new_time_num = 0; //定时器数量清空
new_time_p = 0; //指针链表清零
}
m_memset((unsigned char *)nt,0,sizeof(new_t));
}
*/
//定时器计时器值初始值
void new_time_reset(new_t *nt)
{
nt->contr = 0;
}
//打开或者关闭定时器
void new_time_cmd(new_t *nt,unsigned char off_on)
{
// if(nt->on_off != off_on)
{ nt->on_off = off_on; }
}
// 定时器溢出运行函数
void new_time_run_main(void)
{
while(new_time_R_p) //定时器是否已经结束
{
if(new_time_R_p->oveflag != 0)
{
new_time_R_p->oveflag = 0 ;
new_time_R_p->run();
}
new_time_R_p = new_time_R_p->down_p;
if(new_time_R_p == new_time_S_p) break;
}
}
// 定时器运行
static void new_time_one_run(new_t *nt)
{
if(nt->on_off == NEW_TIME_ON)
{
nt->contr ++;
if(nt->contr>= nt->settime)
{
if(nt->mode == NEW_TIME_MODE_ONE)
{
nt->on_off = NEW_TIME_OFF;
}
// if(nt->mode > 0) //适配指定定时溢出次数
// {
// nt->mode -- ;
// if(nt->mode == 0)
// {nt->on_off = NEW_TIME_OFF;}
// }
nt->contr = 0; //计时器清零
nt->oveflag = 1; //溢出
}
}
}
//定时器心跳
void new_time_run_clock(void)
{
while(new_time_C_p) //定时器是否已经结束
{
new_time_one_run(new_time_C_p);
new_time_C_p = new_time_C_p->down_p;
if(new_time_C_p == new_time_S_p) break;
}
}
#endif
目前在 芯圣上还属于验证阶段 大家踊跃 尝试 。
|
@flybluefox
@芯圣电子官方QQ
@21小跑堂