搜索
bottom↓
回复: 18

分享:STM32下的非阻塞延时模块,可以在状态机下使用

  [复制链接]

出65入0汤圆

发表于 2014-12-5 15:16:43 | 显示全部楼层 |阅读模式
本帖最后由 luweixuan 于 2014-12-5 15:47 编辑

        在参考各个开发板的例程的基础上,经过自身编程实践重新构建和修改,编写了以下方便在状态机模块里使用的延时计数模块。延时计数的基础是SYSTICK,设置为1ms.具体代码如下:
softtimer.h:
  1. /*
  2. *********************************************************************************************************
  3. *
  4. *                                                SOFTTIMER
  5. *
  6. *                                     ST Microelectronics STM32
  7. *                                              on the
  8. *
  9. *                                         STM32Fx-DISCOVERY
  10. *                                         Evaluation Board
  11. *
  12. * 文件名      : softtimer.h
  13. * 版 本       : V0.1
  14. * 作 者       : Lux
  15. * 时 间       : 2014/11/27
  16. * 编译环境    : D:\Program Files (x86)\IAR Systems\Embedded Workbench 7.0
  17. * 摘    要    : SOFTTIMER Drive,软件方式实现的非阻塞方式的超时计时模块
  18. *********************************************************************************************************/
  19. #ifndef  __SOFT_TIMER_H
  20. #define  __SOFT_TIMER_H



  21. #ifdef __cplusplus
  22. extern "C" {
  23. #endif
  24.   
  25.   //-----------------------Include files--------------------------------//
  26.   #include <stdbool.h>
  27.   #include <stdint.h>
  28.   //------------------------Data struct---------------------------------//
  29.   typedef enum
  30.   {
  31.     ONCE_MODE = 0x00,
  32.     CIRC_MODE = 0x01
  33.   }TIMER_MODE_t;
  34.   
  35.   typedef struct SOFT_TIMER
  36.   {
  37.     TIMER_MODE_t          mode;
  38.     bool                  isActivate;
  39.     volatile uint32_t     value;
  40.     uint32_t              totvalue;
  41.     struct SOFT_TIMER*    next;
  42.   }SOFT_TIMER_t;
  43.   
  44.   //------------------------Extern function-----------------------------//
  45.   void soft_timer_register(SOFT_TIMER_t *st);
  46.   void set_and_star_soft_timer(SOFT_TIMER_t *st, TIMER_MODE_t mode, uint32_t totvalue);
  47.   bool is_soft_timer_Out(SOFT_TIMER_t *st);
  48.   void soft_timer_release(SOFT_TIMER_t *st);
  49.   
  50. #ifdef __cplusplus
  51. }
  52. #endif
  53. #endif  /*__SOFTTIMER_H*/
复制代码




softtimer.c:
  1. #include "softtimer.h"
  2. #include <stdlib.h>
  3. #include "stm32f10x.h"

  4. /* 使能中断 */
  5. #define ENABLE_SYSTICK_INT()        __set_PRIMASK(0)
  6. #define DISABLE_SYSTICK_INT()        __set_PRIMASK(1)
  7. /* 禁止中断 */

  8. static struct SOFT_TIMER *head = NULL;

  9. /* SoftTimer_ISR中断服务程序,每隔1ms进入1次 ****************************************/
  10. void SoftTimer_ISR(void)
  11. {
  12.   struct SOFT_TIMER *t = head;
  13.   if (t == NULL)
  14.     return;
  15.   
  16.   while(t != NULL)
  17.   {
  18.     if (t->isActivate == true)
  19.       t->value++;
  20.     t = t->next;
  21.   }
  22. }


  23. // 注册软件计时器
  24. void soft_timer_register(SOFT_TIMER_t *st)
  25. {
  26.   struct SOFT_TIMER *t = head;  
  27.   st->isActivate = false;
  28.   st->mode       = ONCE_MODE;
  29.   st->value      = 0;
  30.   st->totvalue   = 0;
  31.   st->next       = NULL;

  32.   if (head == NULL)
  33.   {
  34.     head = st;
  35.     return;
  36.   }
  37.   
  38.   while(t->next != NULL )
  39.   {
  40.     if (t == st) return; //此语句排除重复注册的现象
  41.     t = t->next;
  42.   }
  43.   DISABLE_SYSTICK_INT();  
  44.   t->next = st;
  45.   ENABLE_SYSTICK_INT();
  46. }

  47. // 设置并开始软件计时器,计时单位为ms
  48. void set_and_star_soft_timer(SOFT_TIMER_t *st, TIMER_MODE_t mode, uint32_t totvalue)
  49. {
  50.   st->mode = mode;
  51.   st->totvalue = totvalue;
  52.   DISABLE_SYSTICK_INT();
  53.   st->value = 0;
  54.   st->isActivate = true;
  55.   ENABLE_SYSTICK_INT();
  56. }

  57. // 查询软件计时器是否超时
  58. bool is_soft_timer_Out(SOFT_TIMER_t *st)
  59. {
  60.   bool res = false;
  61.   if (st->value >= st->totvalue)
  62.   {
  63.     res = true;
  64.     DISABLE_SYSTICK_INT();  
  65.     if (st->mode == CIRC_MODE)
  66.     {
  67.       st->value = 0;  
  68.       st->isActivate = true;
  69.     }else
  70.       st->isActivate = false;
  71.     ENABLE_SYSTICK_INT();
  72.   }  
  73.   return res;   
  74. }

  75. // 注销软件计时器
  76. void soft_timer_release(SOFT_TIMER_t *st)
  77. {
  78.   struct SOFT_TIMER *p = head,*t = head;
  79.   
  80.   if (head == st)
  81.   {
  82.     head = st->next;
  83.     return;
  84.   }
  85.   
  86.   while(t->next != NULL )
  87.   {
  88.     if (t == st)      //从节点删除st
  89.     {
  90.       DISABLE_SYSTICK_INT();
  91.       p->next = st->next;
  92.       ENABLE_SYSTICK_INT();
  93.       st->next = NULL;
  94.       return;
  95.     }
  96.     else
  97.     {
  98.       p = t;
  99.       t = t->next;
  100.     }
  101.   }
  102. }
复制代码



使用方法如下:
  1. // LED模块的状态机
  2. void led_module()
  3. {
  4.   static uint8_t state = 0;
  5.   static SOFT_TIMER_t led_st;  
  6.   
  7.   switch(state)
  8.   {
  9.   case 0:// 初始状态
  10.     led1.init();
  11.     led1.on();
  12.     soft_timer_register(&led_st);
  13.     set_and_star_soft_timer(&led_st, CIRC_MODE, 500);
  14.     state = 1;
  15.     //break;
  16.   case 1:// led跳变状态
  17.     led1.tigger();
  18.     state = 2;
  19.     break;
  20.   case 2:// 延时等待状态
  21.     if (is_soft_timer_Out(&led_st) == true)
  22.       state = 1;      
  23.     break;
  24.    
  25.   }
  26. }
复制代码



ps: 编辑原因:添加代码压缩包

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

阿莫论坛20周年了!感谢大家的支持与爱护!!

知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)

出0入0汤圆

发表于 2014-12-5 16:06:57 | 显示全部楼层
不得不说,楼主写的很精彩。谢谢。

出100入85汤圆

发表于 2014-12-5 16:09:52 | 显示全部楼层
我也曾实现了这种,不过写的没这么规范。

出0入0汤圆

发表于 2014-12-5 16:19:19 | 显示全部楼层
写得真好

出0入0汤圆

发表于 2014-12-5 16:48:15 | 显示全部楼层
支持,前几天也自己写了用回调函数的软件定时器。

出0入0汤圆

发表于 2014-12-5 20:48:07 | 显示全部楼层
虽然还未看懂,但还要支持。

出0入0汤圆

发表于 2014-12-7 12:21:16 | 显示全部楼层
先mark,回头细看

出0入0汤圆

发表于 2014-12-7 14:03:06 | 显示全部楼层
感觉还是不够灵活,不过使用os的可以参考

出0入0汤圆

发表于 2014-12-7 14:10:32 | 显示全部楼层
楼主写得真好,就是在小资源的片子上内存开销比较大

出0入0汤圆

发表于 2014-12-7 14:31:41 | 显示全部楼层
谢分享,只是用起来有点费劲了。

出0入0汤圆

发表于 2014-12-7 21:11:10 | 显示全部楼层
这个还不错。

出0入0汤圆

发表于 2014-12-7 21:45:41 | 显示全部楼层
好像还不错,回去和我现在用的做个比较,看效果怎么样。

出65入0汤圆

 楼主| 发表于 2014-12-7 22:21:24 来自手机 | 显示全部楼层
ijlc1314 发表于 2014-12-7 14:10
楼主写得真好,就是在小资源的片子上内存开销比较大

确实,内存会占用的,可以修改两个数据类型从32位到十六位或八位,具体看你的需求了。本来想增加一个内存管理模块,然后就可以即时申请使用,用完释放的。但是这样就要依靠内存管理模块,复杂度上去了。小项目已经完成了,现在要去弄FPGA了,所以就懒得再折腾了。

出0入0汤圆

发表于 2014-12-8 09:33:50 | 显示全部楼层
luweixuan 发表于 2014-12-7 22:21
确实,内存会占用的,可以修改两个数据类型从32位到十六位或八位,具体看你的需求了。本来想增加一个内存 ...

8位机我用的是数组实现的,比较简单,每需要的时候从缓冲区中申请,用完后注销

出0入0汤圆

发表于 2015-1-25 01:00:00 | 显示全部楼层
看到这个程序,用到链表的,但我觉得开销有点大,竟然在中断中1ms带用自加,为嘛不用自减,可以少用一个4个字节的空间
typedef struct SOFT_TIMER
  {
    TIMER_MODE_t          mode;
    bool                  isActivate;         //可以和modej采用宏定义方式
    volatile uint32_t     value;
    uint32_t              totvalue;        //(可以去掉,中断调用采用自减)
    struct SOFT_TIMER*    next;
  }SOFT_TIMER_t;

这样就变成
typedef struct SOFT_TIMER
  {
    uint8_t                  status;
    volatile uint32_t     value;
    struct SOFT_TIMER*    next;
  }SOFT_TIMER_t;
宏取status 其中三位就两个模式,和isactive. 用位俺码,宏定义
中断采用
void Soft_Timer_Insert_ISR(void)
{
  SOFT_TIMER_t *t = Sthead;

   while(t != NULL)
   {
    if ( 宏isactive  && (t->value > 0))
      t->value--;
    t = t->next;
   }
}

我感觉这个延时以1ms为准,2个字节就能最大产生65.536s,都能符合要求了。

另外我对这段代码的
  1. // 注册软件计时器
  2. void soft_timer_register(SOFT_TIMER_t *st)
  3. {
  4.   struct SOFT_TIMER *t = head;  
  5.   st->isActivate = false;
  6.   st->mode       = ONCE_MODE;
  7.   st->value      = 0;
  8.   st->totvalue   = 0;
  9.   st->next       = NULL;

  10.   if (head == NULL)
  11.   {
  12.     head = st;
  13.     return;
  14.   }
  15.   
  16.   while(t->next != NULL )
  17.   {
  18.     if (t == st) return; //此语句排除重复注册的现象
  19.     t = t->next;
  20.   }
  21.   DISABLE_SYSTICK_INT();  
  22.   t->next = st;
  23.   ENABLE_SYSTICK_INT();
  24. }
复制代码

里的那个遍历 while(t->next != NULL )找重复注册现象有疑问。  假设现在判断第二个是不是重复,为嘛判断next的指针?
是不是写错了。

以上个人建议。另外这样改完后,代码量就减少很多。我现在暂时用的就是这种,

出0入85汤圆

发表于 2015-1-25 09:00:59 | 显示全部楼层
注册、注销有问题
while(t->next != NULL)
当要删除的是最后一个节点时,删不掉,并且更坏的情况是再马上重新注册未被删除的那个节点可以被注册进去,结果就是最终倒数第二个节点和倒数第一个节点重复,倒数第二个节点的下一个节点就是自己,后果就是遍历所有节点时死循环!

出255入0汤圆

发表于 2015-1-25 11:09:22 | 显示全部楼层
很不错,以前实现基本都是用数组。很少用链表

出0入0汤圆

发表于 2015-1-25 21:34:25 | 显示全部楼层
为你点赞

出0入0汤圆

发表于 2015-4-25 22:56:21 | 显示全部楼层
Huaan 发表于 2015-1-25 09:00
注册、注销有问题
while(t->next != NULL)
当要删除的是最后一个节点时,删不掉,并且更坏的情况是再马上重 ...

高人,确实如你所说,谢谢
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-6-28 03:05

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表