打印
[技术讨论]

雕虫小技(一) --- 一个简单但实用的软件定时器

[复制链接]
1961|5
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
红蛋大叔|  楼主 | 2018-6-13 00:45 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 红蛋大叔 于 2018-6-13 00:57 编辑



当下有许多嵌入式实时系统提供了各种能满足MCU要求的功能,他们的优点对于搞实用过嵌入式实时操作系统的人来说是不言自明的,但是缺点就是针对不同内核的MCU都需要做一次移植,
对于资源占用来说也是不可小觑的,不仅如此,使用者不仅要对RTOS有足够的了解,而且还能熟练的运用RTOS才能发挥RTOS的真正作用。这也意味着不是所有MCU以及MCU使用者都需能
很好的使用那些高大上的嵌入式实时系统,有些简单项目其实本来就没必要用到这种RTOS。

基于此,写一个基于软件定时器以及基于软件定时器的编程方法还是有必要的。虽然结构简单,无法完成像RTOS那样的功能,但是,对于一些简单的应用来说还是有些优势的。

首先,占用资源少、不涉及复杂的移植问题。
其次,使用简单,对于简单的应用来说,简单不论是对于使用者还是软件也意味着更加安全。
再次,定时器模块的复用性强,有些项目中不使用RTOS,但是肯定会使用软件定时器,通过这个软件定时器模块,几乎可以毫不修改的应用到不同的项目中。
最后,如果熟练,基于软件定时器,也能玩出很多不同花样,应对简单的应用绰绰有余。

下面是软件定时器的头文件,具体的源文件以及头文件都在附件中,对于代码不做过多解释,因为相比RTOS来说还是很简单的。

#ifndef _APP_CLK_
#define _APP_CLK_

#define CLK_TASK_MAX_NUM  10  //时钟任务最大数量,不可定义过大

typedef unsigned char a_size_t;     //如果延迟时间不够,将数据类型改为uint16_t
typedef void (* a_cb_t)(void );

typedef struct
{
   a_cb_t   clk_cb;
   a_size_t delay;  
   a_size_t peroid;
}app_clk_t;

typedef enum
{
  CONSTRUCT_FAULT,
  CONSTRUCT_SUC,
  
  CALLBACK_REPEAT = (unsigned char) 0xff
}status_t;

/*******************************************************************************
**函数功能: 创建一个软件系统时钟
**输入参数: clk       - 用户定义的时钟实例
**
**                clk_cb    - 要定期执行的回调函数,该函数由用户定义
**
**                dly     - 创建时钟后,延迟多久执行相应的clk_cb。具体dly延时多久取决
**                            app_clk_scheduler()的定时间隔。假如app_clk_scheduler()的
**                           定时间隔1ms,dly = 100,则clk_cb延迟100ms后执行   
**                  
**               peroid    - 间隔period后再次运行clk_cb.当peroid=0, 表示clk_cb只执行
**                               一次,当peroid!=0,clk_cb永远执行,具体peroid的取值同dly
**
**              run_flag  - 如果创建完成后立刻运行则   - TRUE
**                               如果创建完成后不要立刻运行 - FALSE
**输出参数: status_t  - 如果创建成功  - CONSTRUCT_SUC
**                             - 如果创建失败  - CONSTRUCT_FAULT
*******************************************************************************/
status_t app_construct_clk(app_clk_t *clk, a_cb_t clk_cb, a_size_t dly, a_size_t period, unsigned char run_flag);

/*******************************************************************************
**函数功能: 软件系统时钟调度,该函数调用间隔决定了用户的定时函数最少间隔多久
**          执行一次
**
**输入参数: 空
**输出参数: 空
*******************************************************************************/
void app_clk_scheduler(void );

/*******************************************************************************
**函数功能: 启动定时函数
**
**输入参数: clk - 用户定义的时钟实例
**
**输出参数: 空
*******************************************************************************/
void app_clk_start(app_clk_t *clk);

/*******************************************************************************
**函数功能: 停止定时函数
**
**输入参数: clk - 用户定义的时钟实例
**
**输出参数:
*******************************************************************************/
void app_clk_stop(app_clk_t *clk);

#endif

#include “app_clk.h”

app_clk_t test_handle;


unsigned char clk100us_flag,clk100us;

//硬件定时器中断,中断间隔100us
void interrupt_timer(void )
{
   clk100us_flag = 1;
}

void test_led(void )
{
   //取反LED
}

main()
{
/*
初始化一个硬件定时器,定时器定时为100us(当然,这个值你随你自己定)
*/
timer_init();
/*
初始化一个软件定时器test_led,第一个100表示100ms后test_led开始运行, 第二个100指的是test_led每隔100ms运行一次,如果这个值设置为0,
则test_led只在100ms后运行一次,TRUE表示初始化完成时test_led开始运行,如果是FALSE,初始化完成后,test_led不立即运行,而是通过
app_clk_start(&test_handle)手动启动test_led
*/
   app_construct_clk(&test_handle, test_led,100,100,TRUE);
    while(1)
    {

      if(clk100us_flag)
     {
         clk100us_flag= 0;
         if(++clk100us >= 10 )
         {
             clk100us  = 0;
            app_clk_scheduler();  //软时钟调度
         }
     }

    }
}

爱因斯坦曾说过“科学理论应该尽可能简单,但是不能过于太简单”,有时候简单也不失为一种美,当然,没有哪一种方法是能适应任何场合的,
还是那句话,只有合适的,没有最好的。

这次先把这个软件定时器代码发出来,后续再写一篇基于这个软件定时器的编程方法,希望也能对你有点作用。

源码:
app_clk.zip (1.82 KB)





沙发
红蛋大叔|  楼主 | 2018-6-13 00:47 | 只看该作者

使用特权

评论回复
板凳
xyz549040622| | 2018-6-13 07:57 | 只看该作者
支持下。

使用特权

评论回复
地板
kelly1989| | 2018-6-13 08:22 | 只看该作者

使用特权

评论回复
5
coody| | 2018-6-13 17:16 | 只看该作者
我的软件定时器很简单,一般我用倒计时的。
比如某个定时定时于1ms,这个是系统节拍。1ms中断里处理软件定时器,非0的就减到0。别的程序使用时,给一个非0值即可。

u8  timer[10];  //10个软件定时器

应用层:
    timer[0] = 100;  //启动timer[10],100ms定时
然后在系统节拍下查询if( timer[0] == 0)即可。

1ms系统节拍中断里执行:
for(i=0; i<10; i++)   if(timer[i]!=0)   timer[i]--;  

使用特权

评论回复
6
zzgezi| | 2018-6-14 16:54 | 只看该作者
mark 谢谢。

使用特权

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

本版积分规则

25

主题

69

帖子

3

粉丝