打印
[资源共享]

《芯圣51内核单片机HC89F0xx库分享》-软定时器库

[复制链接]
11290|18
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 正版长小强 于 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


目前在 芯圣上还属于验证阶段 大家踊跃 尝试 。


使用特权

评论回复
沙发
正版长小强|  楼主 | 2023-11-10 09:49 | 只看该作者
创建 两个定时器 通过串口发送内容

微信图片_20231110094606.png (83.04 KB )

微信图片_20231110094606.png

微信图片_20231110094557.png (60.32 KB )

微信图片_20231110094557.png

使用特权

评论回复
板凳
weifeng90| | 2023-11-13 07:40 | 只看该作者
软定时器的驱动源来源于硬件定时器。

使用特权

评论回复
地板
正版长小强|  楼主 | 2023-11-13 11:02 | 只看该作者
weifeng90 发表于 2023-11-13 07:40
软定时器的驱动源来源于硬件定时器。

我理解是的 ,需要提供 时钟源,才能实现

使用特权

评论回复
5
tpgf| | 2023-12-4 12:51 | 只看该作者
和软定时器库对应的有硬定时器库吗

使用特权

评论回复
6
drer| | 2023-12-4 16:47 | 只看该作者
这个软件定时器的最小计时精度是多少啊

使用特权

评论回复
7
coshi| | 2023-12-4 20:59 | 只看该作者
软定时器的时钟源可以选择什么呢

使用特权

评论回复
8
qcliu| | 2023-12-4 21:30 | 只看该作者
这个软定时器库可以由用户进行修改吗

使用特权

评论回复
9
kxsi| | 2023-12-5 08:49 | 只看该作者
可以在库里边为定时器选择时钟源吗

使用特权

评论回复
10
wiba| | 2023-12-5 10:01 | 只看该作者
软定时器和我们常说的定时器不是一个部件吗

使用特权

评论回复
评论
正版长小强 2023-12-8 08:06 回复TA
软定时器特点 :依赖硬件定时器,消耗内存 ,内存满足没有数量限制 ,定时实时性低于硬件定时器。 
11
正版长小强|  楼主 | 2023-12-8 08:04 | 只看该作者
coshi 发表于 2023-12-4 20:59
软定时器的时钟源可以选择什么呢

目前会依赖一个硬件定时器,不像ARM有 嘀嗒定时器,芯圣目前产生定时器中断功能有多中方法,都可以实现

使用特权

评论回复
12
正版长小强|  楼主 | 2023-12-8 08:07 | 只看该作者
qcliu 发表于 2023-12-4 21:30
这个软定时器库可以由用户进行修改吗

可以的 ,很简单就是分享这些,没有做过多的 处理,提供最基本的 计时、回调、链表

使用特权

评论回复
13
正版长小强|  楼主 | 2023-12-8 08:09 | 只看该作者
drer 发表于 2023-12-4 16:47
这个软件定时器的最小计时精度是多少啊

默认10ms,如果想低于1ms,建议直接硬件定时器 ,它功能侧重于解决 10ms 以上的 大时段 ,流程性功能程序时间管理

使用特权

评论回复
14
正版长小强|  楼主 | 2023-12-8 08:10 | 只看该作者
tpgf 发表于 2023-12-4 12:51
和软定时器库对应的有硬定时器库吗

项目上对这方面的需求不是很高,用到的话会出一个

使用特权

评论回复
15
chenjun89| | 2023-12-8 20:53 | 只看该作者
软件定时器很实用,用一个硬件定时器源可以实现很多定时器。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

60

主题

194

帖子

4

粉丝