搜索
bottom↓
回复: 32
打印 上一主题 下一主题

状态机调用的函数有延时怎么处理?

[复制链接]

出0入0汤圆

跳转到指定楼层
1
发表于 2013-1-8 08:38:10 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
如果状态机调用的函数需要用到延时,需要怎么处理这个函数呢?如果此时把该函数也设计成一个状态机,那么怎么解决函数被状态机中两个状态或多个状态同时调用时函数数据保存的问题呢?除了加锁外,还有什么方法可以把该函数变成一个可重入函数?

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

如果想吃一顿饺子,就得从冰箱里取出肉,剁馅儿,倒面粉、揉面、醒面,擀成皮儿,下锅……
一整个繁琐流程,就是为了出锅时那一嘴滚烫流油的热饺子。

如果这个过程,禁不住饿,零食下肚了,饺子出锅时也就不香了……《非诚勿扰3》

出0入0汤圆

2
发表于 2013-1-8 09:19:16 | 只看该作者
所谓可重入的函数也是函数内部的临时变量另存起来罢了,再需要的时候调出来

不同状态调用分别用不同的变量保存,static声明,同时及时调用更新状态

出0入296汤圆

3
发表于 2013-1-8 12:45:42 | 只看该作者
本帖最后由 Gorgon_Meducer 于 2013-1-8 12:49 编辑

延时函数也要用状态机来写:软件延时还是定时器延时都无所谓的,都有具体写法。

对于延时,其实不推荐写成函数来调用,而推荐做成资源,我记得我有一个很老的帖子里面有提到过,具体接口是这样的:
1、有一个申请延时的函数request_delay(),然后把延时时间传递进去,这个函数会返回一个句柄
2、如果句柄有效,则可以通过is_time_out()函数利用传入的句柄来查询是否所需的延时已经到时间了

可以看出,这个接口是把延时当成后台资源的,这并不难做,用一个定时器维护一个计数器就可以做到。并且非常适合
状态机环境使用。但是需要注意的是,句柄是有生命周期的,一旦一个句柄代表的延时到达时间了,或者说timeout了,
则查询的过程会自动注销这个句柄。

你可以自己根据这个思路来设计下这种结构,如果有问题,我找找看能不能找到过去的那个代码……

厄……居然找到代码了……单击这里下载

下面是接口部分的定义——貌似是我2年前的风格……呵呵……凑合着用吧

  1. #ifndef _USE_MULTI_DELAY_H_
  2. #define _USE_MULTI_DELAY_H_

  3. /*-----------------------------*
  4. *  include head files         *
  5. *----------------------------*/
  6. #include "..\..\..\utils\compiler.h"
  7. #include "Multi-Delay_cfg.h"

  8. /*-----------------------------*
  9. *  Macros for constants       *
  10. *----------------------------*/

  11. /*-----------------------------*
  12. *  Macros for others          *
  13. *----------------------------*/

  14. /*-----------------------------*
  15. *  type definitions           *
  16. *----------------------------*/
  17. typedef enum DelayState
  18. {
  19.     DELAY_DOWN_COUNTING,
  20.     DELAY_TIME_OUT,
  21.     DELAY_ERROR
  22. }ES_DELAY_STATE;

  23. /*-----------------------------*
  24. *  public functions prototypes*
  25. *----------------------------*/
  26. extern void MutiDelay_INIT(void);
  27. extern uint16_t Add_Delay_Item(uint16_t hwTime);
  28. extern ES_DELAY_STATE If_Time_Out(uint16_t hwDHandl);
  29. extern void Cancel_Delay_Item(uint16_t hwDHandl);

  30. #endif
复制代码

本帖子中包含更多资源

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

x

出0入0汤圆

4
 楼主| 发表于 2013-1-8 21:24:43 | 只看该作者
xivisi 发表于 2013-1-8 09:19
所谓可重入的函数也是函数内部的临时变量另存起来罢了,再需要的时候调出来

不同状态调用分别用不同的变量 ...

谢谢咯,这个思路也考虑过,但是当时没想到比较好的方式来存储,怕麻烦,所以就略过了,我觉得应该再好好设计下~

出0入0汤圆

5
 楼主| 发表于 2013-1-8 21:28:04 | 只看该作者
Gorgon_Meducer 发表于 2013-1-8 12:45
延时函数也要用状态机来写:软件延时还是定时器延时都无所谓的,都有具体写法。

对于延时,其实不推荐写成 ...

非常感谢傻孩子的恢复,受了很大启发,看来我的思维模式还没彻底转换到状态机的状态上来 。另外还想问问傻孩子如果我的系统有一个状态是需要定时运行的,怎么处理呢?

出0入0汤圆

6
发表于 2013-1-9 08:51:03 | 只看该作者
droper 发表于 2013-1-8 21:28
非常感谢傻孩子的恢复,受了很大启发,看来我的思维模式还没彻底转换到状态机的状态上来 。另外 ...

你就当两个线程  定时器中断去更新状态,  正常程序做状态迁移,     傻孩子是大师多看他的帖子

出0入296汤圆

7
发表于 2013-1-9 10:41:50 | 只看该作者
xivisi 发表于 2013-1-9 08:51
你就当两个线程  定时器中断去更新状态,  正常程序做状态迁移,     傻孩子是大师多看他的帖子 ...

谢谢说明。
如果你有调度器或者操作系统,那么就在中断里面设置信号量,在状态机里面等待信号量。
如果你是裸机,就在中断里面设置一个标志位,在状态机里面等标志位就可以了。

出0入0汤圆

8
 楼主| 发表于 2013-1-9 10:57:31 | 只看该作者
xivisi 发表于 2013-1-9 08:51
你就当两个线程  定时器中断去更新状态,  正常程序做状态迁移,     傻孩子是大师多看他的帖子 ...

嗯,非常感谢,应该再细致的研究下傻孩子的帖子~

出0入0汤圆

9
 楼主| 发表于 2013-1-9 10:58:45 | 只看该作者
Gorgon_Meducer 发表于 2013-1-9 10:41
谢谢说明。
如果你有调度器或者操作系统,那么就在中断里面设置信号量,在状态机里面等待信号量。
如果你 ...

嗯,基本有思路了,非常感谢傻孩子的指导~

出0入0汤圆

10
发表于 2013-1-9 16:59:17 | 只看该作者
傻孩子,人代码意图,没有看懂,可否详细分析一下。。。

出0入296汤圆

11
发表于 2013-1-10 15:07:01 | 只看该作者
wuqiushan741826 发表于 2013-1-9 16:59
傻孩子,人代码意图,没有看懂,可否详细分析一下。。。

不好意思哈,暂时可能没有时间来进行详细分析。但是我可以说以下基本思路,然后你就着这个思路
尝试去看代码,有问题的话我们再进一步具体讨论,好不?

首先,我说说这个模块的基本功能。
    这个代码利用一个定时器维护一个32位的计数器,并通过这个计数器同时提供若干路延时的标定
服务。所谓标定服务是指,在你需要延时的时候,你通过接口函数Add_Delay_Item()申请一个你想要延时
的时间,这个时候,系统就会在后台分配一个标定名额给你,这个标定实际上就是一个时间预测,比如,
当前的32位计数器数值是x,你要延时300ms(假设定时器产生1ms中断),则这个预测的标定值就是
(x+300)。那么我们怎么知道延时到了呢?实际上你就要不停的通过接口函数If_Time_Out()来查询刚刚
你登记的那个标定是否到了,也就是检查现在的32位计数器是不是超过了原本的x+300,如果超过了,说明
延时到了,返回true,并注销当前的标定,如果没有超过,直接返回false。

这个服务实际上也可以叫做并行延时服务。最大的标定个数是可以通过宏来设定的,你可以从代码里面找到,
另外,每个标定在使用的时候都会获得一个不同的ID,或者叫句柄,用于查询。这个句柄一定要每次都不同,
以防止别人用过期的句柄破坏了当前有效的标定——我想你能理解,就相当于火车票上的日期能有效地防止
别人用过期的票做同班次的列车。

有一个函数是用来插入到定时器的毫秒中断处理程序里面的。其实延时的单位并不是固定的1ms,如果定时器
中断是10ms,那么这个延时最小单位就是10ms,以此类推。

出0入0汤圆

12
发表于 2013-1-11 21:25:10 来自手机 | 只看该作者
多谢分享!

出0入0汤圆

13
发表于 2013-1-11 22:39:14 | 只看该作者
Gorgon_Meducer 发表于 2013-1-10 15:07
不好意思哈,暂时可能没有时间来进行详细分析。但是我可以说以下基本思路,然后你就着这个思路
尝试去看 ...

这个思路不错

以前在TI 的代码里面也是看到的类似的处理方法,不过更具有操作性
思路和此状态机制差不多,不过多偏向于消息机制


首先 需要延时的话, 先在timeout链表中加入(当前计数+延时事件) 以及任务ID 和 消息类型

当计时到这个计数值的时候, 定时器就向此任务ID发送一个timeout消息


任务只要在大循环中不断的去获取属于自己的消息,分辨不同的消息类型和消息值,从而决定做什么事情


这就是消息 +状态机 做到任务之间的同步 以及延时等

出0入296汤圆

14
发表于 2013-1-12 00:57:19 | 只看该作者
ele_eye 发表于 2013-1-11 22:39
这个思路不错

以前在TI 的代码里面也是看到的类似的处理方法,不过更具有操作性

消息机制会增加系统的复杂度。如果要做消息,其实可以做的更彻底。现在的延时是基于查询的,
如果要做成消息驱动的,就可以做成真阻塞的地,状态机在等待延时的时候,实际上不仅注册延时,
注册的时候将状态机自己的任务本身也作为TAG注册到里面去了,同时这个任务也从系统的就绪队列
里面删除了——这个任务就真正的阻塞了,当timeout消息被推送出来的时候,实际上会先把TAG里面
保存的任务重新加入到就绪队列里面。

出0入0汤圆

15
发表于 2013-1-29 20:37:48 来自手机 | 只看该作者
学习一下......

出0入0汤圆

16
发表于 2013-5-10 17:32:29 | 只看该作者
学习一下......

出0入0汤圆

17
发表于 2013-7-28 20:28:32 | 只看该作者
mark            

出0入0汤圆

18
发表于 2013-9-3 09:14:04 | 只看该作者
标记学习,状态机模式 多延时

出0入0汤圆

19
发表于 2013-9-10 11:19:51 | 只看该作者
知识太少大家说的不太明白,消息机制不会用。
我在状态机里用延时,比如1.IO输出;2.对定时器中断标志位计时,大于某个时间进行状态迁移;3.IO输出,结束状态机;呵呵

出0入0汤圆

20
发表于 2013-12-16 22:07:02 | 只看该作者
Gorgon_Meducer 发表于 2013-1-12 00:57
消息机制会增加系统的复杂度。如果要做消息,其实可以做的更彻底。现在的延时是基于查询的,
如果要做成 ...

傻孩子 可否介绍本消息驱动方面的书籍

出0入296汤圆

21
发表于 2013-12-17 10:23:35 | 只看该作者
卖菜老汉 发表于 2013-12-16 22:07
傻孩子 可否介绍本消息驱动方面的书籍

看深入浅出MFC的头6章

出0入0汤圆

22
发表于 2014-7-22 09:34:34 | 只看该作者
      mark

出0入0汤圆

23
发表于 2014-7-25 14:46:04 | 只看该作者
都是好东西!

出0入0汤圆

24
发表于 2014-9-15 17:30:20 | 只看该作者
Gorgon_Meducer 发表于 2013-1-8 12:45
延时函数也要用状态机来写:软件延时还是定时器延时都无所谓的,都有具体写法。

对于延时,其实不推荐写成 ...

傻孩子大侠,知道你最近很忙,不好意思打扰一下:
最近在阅读你的Multi-Delay程序 其中有个难理解的地方:

uint16_t Add_Delay_Item(uint16_t hwTime)
{
...
                s_dlDelayBuff[n].wTimer = s_wDelayTimerCounter + hwTime;        //根据定时器当前值及要延时的值给定时器设置超时值
  ...                                          
}

ES_DELAY_STATE If_Time_Out(uint16_t hwDHandl)
{
    ....
            if (s_dlDelayBuff[n].wTimer <= wTempDelayTimerCounter)                //如果超时值小于计时器当前值
....
           
}

如果当添加定时器时  wTimer = 0xffffff02 +0xff = 0x02;
那是不是说不要延时0xff时是就认为延时到了呢??




出0入0汤圆

25
发表于 2014-9-15 20:59:46 | 只看该作者
在这个程序中会不会产生计时回绕问题,如果产生怎样避免,例如:linux中jiffies回绕

出0入296汤圆

26
发表于 2014-9-15 21:22:20 | 只看该作者
黄晨0410 发表于 2014-9-15 20:59
在这个程序中会不会产生计时回绕问题,如果产生怎样避免,例如:linux中jiffies回绕 ...

我计算过这个时间,基本上是天文数字,另外,你没有注意到,我代码另外一个地方,如果判断当前没有任何延时,就自动让
计数器清0。这段代码意思就是说,在天文数字的时间范围内,只要有一小段时间没有任何延时,那么就会从头开始,这是一种
用大概率避免绕回的方式。

出0入0汤圆

27
发表于 2014-9-15 21:36:24 | 只看该作者
谢谢提醒 我再仔细分析。

出0入0汤圆

28
发表于 2014-9-15 21:44:32 | 只看该作者
1:因为wTimer为uint32_t 范围为:0xffffffff = 4294967295 就算时钟节拍为1ms计数一次,也要计算到 49天才溢出,
2:void MultiDelay_Insert_MS_Timer_ISR_Code(void)
{
    if (0 == s_chDelayItemCounter)
    {
        s_wDelayTimerCounter = 0;
    }
    else
    {
        s_wDelayTim erCounter++;
    }
}

因为这段代码,所以49天内出现过一次没有申请一个软定时器,就会复位到0。

国该是这样吧!!

出0入296汤圆

29
发表于 2014-9-16 10:22:27 | 只看该作者
黄晨0410 发表于 2014-9-15 21:44
1:因为wTimer为uint32_t 范围为:0xffffffff = 4294967295 就算时钟节拍为1ms计数一次,也要计算到 49天才 ...

是的,49天……

出0入0汤圆

30
发表于 2015-8-4 17:16:17 | 只看该作者
感觉不仅要学习傻孩子的代码,更要学习傻孩子的思想啊!

出0入0汤圆

31
发表于 2015-10-26 09:00:27 | 只看该作者
这个思路值得学习~多谢分享

出0入0汤圆

32
发表于 2016-1-4 22:16:30 | 只看该作者
好,标记下。

出0入0汤圆

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

本版积分规则

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

GMT+8, 2024-3-28 22:53

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

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