OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 17134|回复: 33

裸机用C语言写多任务程序的时候,用时间片轮询的方式的话,任务里面的延时函数怎么处理呢?

[复制链接]

21

主题

109

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
321
金钱
321
注册时间
2015-3-28
在线时间
64 小时
发表于 2016-11-21 14:59:18 | 显示全部楼层 |阅读模式
5金钱
本帖最后由 小小速 于 2016-11-21 16:48 编辑

最近遇到点麻烦,写了十几天的驱动才发现放在一起主程序没法工作了刚毕业的小白,身边也没个懂的,只能来求助广大网友了
想用时间片轮询的方式,看了一天的资料现在有点迷糊了

[mw_shl_code=c,true]// 任务结构
typedef struct _TASK_COMPONENTS
{
    uint8 Run;                 // 程序运行标记:0-不运行,1运行
    uint8 Timer;              // 计时器
    uint8 ItvTime;              // 任务运行间隔时间
    void (*TaskHook)(void);    // 要运行的任务函数
} TASK_COMPONENTS;       // 任务定义[/mw_shl_code]
定时器中断函数
[mw_shl_code=c,true]void TaskRemarks(void)
{
    uint8 i;
    for (i=0; i<TASKS_MAX; i++)          // 逐个任务时间处理
    {
         if (TaskComps.Timer)          // 时间不为0
        {
            TaskComps.Timer--;         // 减去一个节拍
            if (TaskComps.Timer == 0)       // 时间减完了
            {
                 TaskComps.Timer = TaskComps.ItvTime;       // 恢复计时器值,从新下一次
                 TaskComps.Run = 1;           // 任务可以运行
            }
        }
   }
}[/mw_shl_code]

[mw_shl_code=c,true]/**************************************************************************************
* FunctionName   : TaskProcess()
* Description    : 任务处理
* EntryParameter : None
* ReturnValue    : None
**************************************************************************************/
void TaskProcess(void)
{
    uint8 i;
    for (i=0; i<TASKS_MAX; i++)           // 逐个任务时间处理
    {
         if (TaskComps.Run)           // 时间不为0
        {
             TaskComps.TaskHook();         // 运行任务
             TaskComps.Run = 0;          // 标志清0
        }
    }   
}[/mw_shl_code]
问题在下面这里:
[mw_shl_code=c,true]static TASK_COMPONENTS TaskComps[] =
{
    {0, 60, 60, TaskDisplayClock},            // 显示时钟
    {0, 20, 20, TaskKeySan},               // 按键扫描
    {0, 30, 30, TaskDispStatus},            // 显示工作状态
     // 这里添加你的任务。。。。
};[/mw_shl_code]
时间片轮询去执行任务,每个任务的执行周期是在上面结构体里初始化的,然后每隔一定时间(如1ms)进入定时器中断,在中断函数里刷新任务,同时进行调度,这个能看懂
问题是这种方法,只是说明了每个周期性任务如何互不干扰的运行,但是并没有说当一个任务里面包含delay函数时,delay函数的实现,
比如有一个IIC的任务每隔100ms产生一次通信,这个100ms的周期可以在上面的任务结构体里面初始化参数uint8 ItvTime; // 任务运行间隔时间
那IIC的时序延时又该如何实现呢?小的延时通过死等实现,对于大于最小时间片(1ms)的延时,是不是在delay的地方重新对TaskComps.Timer进行赋值呢?
还有就是这个时间轮询好像没有提到状态机的问题,是不是这种轮询不需要任务里面写状态机就能实现多任务并行呢?











最佳答案

查看完整内容[请看2#楼]

如果延时时间小于定时器中断的时间,那就只能死等或者另外再开个定时器来完成这部分延时 但我看你问的好像不完全是这个问题,你还想问在状态机编程时如何解决多个地方延时对吧? 关于这个问题我刚花了点时间仿照contiki的思想写了一个很简单的测试代码,风格跟操作系统很像,但本质还是状态机: 代码我晚点会以帖子的形式发布并做详细解释,你看下你想要的是不是这个效果吧,在某个任务里面有多个延时
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

22

主题

181

帖子

0

精华

高级会员

Rank: 4

积分
878
金钱
878
注册时间
2014-7-7
在线时间
311 小时
发表于 2016-11-22 13:49:43 | 显示全部楼层
比如有一个IIC的任务每隔100ms产生一次通信,这个100ms的周期可以在上面的任务结构体里面初始化参数uint8 ItvTime; // 任务运行间隔时间
那IIC的时序延时又该如何实现呢?
对于这个,时序延时你是避免不了要死等的!
但是,对于延时时间比mainloop的周期时间长的,使用状态机进行状态切换即可,而对于一些极短的例如us级别的,那么你就死等也没关系的!
加入你的时序要延时5ms,你的滴答定时器是1ms,那么你只需要把iic的任务时间改为5ms,然后在任务内进行状态切换来完成你的时序控制。而你需要100ms
产生一次通信,那么你应该建立一个新任务,这个新任务是100ms调用一次,而每次把你要发的数据通过管道存储,然后iic任务完整状态完毕后从管道中提取
数据,然后继续发送。
iic任务流程:
等待发送事件----管道提取数据--------发送数据状态----------发送完毕状态--------reset
100ms发送一次数据流程:
(100ms到达)---数据送入管道---发送发送事件----reset
回复

使用道具 举报

27

主题

711

帖子

0

精华

版主

Rank: 7Rank: 7Rank: 7

积分
11922
金钱
11922
注册时间
2015-11-5
在线时间
2086 小时
发表于 2016-11-21 14:59:19 | 显示全部楼层
如果延时时间小于定时器中断的时间,那就只能死等或者另外再开个定时器来完成这部分延时
但我看你问的好像不完全是这个问题,你还想问在状态机编程时如何解决多个地方延时对吧?
关于这个问题我刚花了点时间仿照contiki的思想写了一个很简单的测试代码,风格跟操作系统很像,但本质还是状态机: 捕获.PNG
代码我晚点会以帖子的形式发布并做详细解释,你看下你想要的是不是这个效果吧,在某个任务里面有多个延时
拿来长岛冰茶换我半晚安睡
回复

使用道具 举报

21

主题

109

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
321
金钱
321
注册时间
2015-3-28
在线时间
64 小时
 楼主| 发表于 2016-11-21 15:46:42 | 显示全部楼层
貌似发错区了……
回复

使用道具 举报

5

主题

99

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
289
金钱
289
注册时间
2016-10-27
在线时间
76 小时
发表于 2016-11-21 16:02:14 | 显示全部楼层
这种硬件时序一般用死等,时钟滴答最小都是1ms,时序不可能这么长,只能用死等,而且如果多个任务中包含此硬件操作,还需考虑互斥问题
回复

使用道具 举报

21

主题

109

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
321
金钱
321
注册时间
2015-3-28
在线时间
64 小时
 楼主| 发表于 2016-11-21 16:13:10 | 显示全部楼层
本帖最后由 小小速 于 2016-11-21 16:15 编辑
ljz1987cj 发表于 2016-11-21 16:02
这种硬件时序一般用死等,时钟滴答最小都是1ms,时序不可能这么长,只能用死等,而且如果多个任务中包含此 ...

小延时有的书说是关中断死等,可是稍微大点的延时怎么弄,这里并没有上操作系统,仅仅是裸跑,和一小段时间片轮询代码
回复

使用道具 举报

5

主题

99

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
289
金钱
289
注册时间
2016-10-27
在线时间
76 小时
发表于 2016-11-21 16:51:04 | 显示全部楼层
这个就要靠你自己编码了 ,既然在滴答中断中做任务调度,那么只需在延时的滴答时间内不去做该任务的调度即可,这样不就是实现了延时挂起任务了吗 做一个实现延时并且挂起任务的函数api
回复

使用道具 举报

21

主题

109

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
321
金钱
321
注册时间
2015-3-28
在线时间
64 小时
 楼主| 发表于 2016-11-21 16:53:23 | 显示全部楼层
本帖最后由 小小速 于 2016-11-21 16:57 编辑
ljz1987cj 发表于 2016-11-21 16:51
这个就要靠你自己编码了 ,既然在滴答中断中做任务调度,那么只需在延时的滴答时间内不去做该任务的调度即 ...

是不是在delay的地方重新对TaskComps.Timer进行赋值呢?不去等待定时器中断刷新任务Timer值这样问题又来了,重新赋值Timer怎么去保存每个任务执行到一半去延时的位置呢?就是任务挂起怎么用C语言保存任务执行的状态……好像我的疑问就在这里
回复

使用道具 举报

5

主题

99

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
289
金钱
289
注册时间
2016-10-27
在线时间
76 小时
发表于 2016-11-21 16:59:37 | 显示全部楼层
好像是的
回复

使用道具 举报

21

主题

109

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
321
金钱
321
注册时间
2015-3-28
在线时间
64 小时
 楼主| 发表于 2016-11-21 17:03:27 | 显示全部楼层

我好像明白了,那个帖子并没有给出完整的解决办法……
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165309
金钱
165309
注册时间
2010-12-1
在线时间
2108 小时
发表于 2016-11-21 18:37:01 | 显示全部楼层
楼主这种方式,只能每个任务的运行时间必须小于你的调度周期,否则就肯定出问题了。。。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165309
金钱
165309
注册时间
2010-12-1
在线时间
2108 小时
发表于 2016-11-21 18:37:11 | 显示全部楼层
先天不足啊。。。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复

使用道具 举报

21

主题

109

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
321
金钱
321
注册时间
2015-3-28
在线时间
64 小时
 楼主| 发表于 2016-11-21 18:51:55 | 显示全部楼层

就是发现了有问题……除了将大任务用状态机拆分,还有没有别的先天比较足的不用汇编的方法呢?
回复

使用道具 举报

21

主题

109

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
321
金钱
321
注册时间
2015-3-28
在线时间
64 小时
 楼主| 发表于 2016-11-21 19:34:57 | 显示全部楼层
正点原子 发表于 2016-11-21 18:37
楼主这种方式,只能每个任务的运行时间必须小于你的调度周期,否则就肯定出问题了。。。

现在找不到太好的裸跑构架,大任务拆分成小的状态,分步执行,算不算个解决办法呢……
记得原子哥老版本mini板有个综合例程就是裸跑的,不过现在资料更新找不到了
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165309
金钱
165309
注册时间
2010-12-1
在线时间
2108 小时
发表于 2016-11-21 22:07:12 | 显示全部楼层
小小速 发表于 2016-11-21 19:34
现在找不到太好的裸跑构架,大任务拆分成小的状态,分步执行,算不算个解决办法呢……
记得原子哥老版本 ...

我们那是大循环。。。
现在都上OS了,建议上OS
你这样做效果不大
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复

使用道具 举报

21

主题

109

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
321
金钱
321
注册时间
2015-3-28
在线时间
64 小时
 楼主| 发表于 2016-11-21 23:09:10 | 显示全部楼层
本帖最后由 小小速 于 2016-11-21 23:33 编辑
正点原子 发表于 2016-11-21 22:07
我们那是大循环。。。
现在都上OS了,建议上OS
你这样做效果不大

刚才找到了那个案例,看了下还真是大循环……并不适合我现在做的这个多任务并行产品上,会考虑用OS去解决这个问题……
不过就是心理有疑问:不用OS能不能很好的解决这些问题呢?又不用额外开个定时器呢?
之前裸跑做复杂应用的时候难道都是像mini的综合历程一样很费劲的写菜单数据结构再来个大循环?要真是这样上OS确实是上上之策
总想把不懂得东西搞清楚……有时候太爱钻牛角尖,自己跟自己过不去
回复

使用道具 举报

21

主题

109

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
321
金钱
321
注册时间
2015-3-28
在线时间
64 小时
 楼主| 发表于 2016-11-21 23:11:48 | 显示全部楼层
FreeRTOS 发表于 2016-11-21 22:29
如果延时时间小于定时器中断的时间,那就只能死等或者另外再开个定时器来完成这部分延时
但我看你问的好像 ...

这个图没看懂是啥意思……我的疑惑确实是状态机下的延时问题
回复

使用道具 举报

21

主题

109

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
321
金钱
321
注册时间
2015-3-28
在线时间
64 小时
 楼主| 发表于 2016-11-21 23:13:22 | 显示全部楼层
正点原子 发表于 2016-11-21 22:07
我们那是大循环。。。
现在都上OS了,建议上OS
你这样做效果不大

都说ucos商业要钱,freertos不要钱……那么ucos怎么知道我在产品上用没用他的系统呢?
回复

使用道具 举报

27

主题

711

帖子

0

精华

版主

Rank: 7Rank: 7Rank: 7

积分
11922
金钱
11922
注册时间
2015-11-5
在线时间
2086 小时
发表于 2016-11-21 23:22:47 | 显示全部楼层
小小速 发表于 2016-11-21 23:11
这个图没看懂是啥意思……我的疑惑确实是状态机下的延时问题

“小的延时通过死等实现,对于大于最小时间片(1ms)的延时,是不是在delay的地方重新对TaskComps.Timer进行赋值呢?”

这个问题是你提出来的吧?别的不说,先考虑IIC时序,有部分延时是超过1ms的,那请问你怎么解决这个时序的延时?想不通的话看看我上面的截图吧,我的测试代码是最小延时节拍是1ms,只要你的IIC延时大于1ms,可以直接像操作系统那样子调用PROCESS_DELAY来实现
拿来长岛冰茶换我半晚安睡
回复

使用道具 举报

21

主题

109

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
321
金钱
321
注册时间
2015-3-28
在线时间
64 小时
 楼主| 发表于 2016-11-21 23:39:06 | 显示全部楼层
本帖最后由 小小速 于 2016-11-21 23:40 编辑
FreeRTOS 发表于 2016-11-21 23:22
“小的延时通过死等实现,对于大于最小时间片(1ms)的延时,是不是在delay的地方重新对TaskComps.Timer ...

我做的话,大延时,会把有delay的地方前后分开成两个状态,delay的时间去重新赋值任务TaskComp.Timer,等下次任务执行时,利用状态机的状态去记录任务执行到延时处的位置,我没用过操作系统,也不知道图里面PROCESS_DELAY是怎么实现的,一张截图我确实看不明白
回复

使用道具 举报

27

主题

711

帖子

0

精华

版主

Rank: 7Rank: 7Rank: 7

积分
11922
金钱
11922
注册时间
2015-11-5
在线时间
2086 小时
发表于 2016-11-21 23:54:51 | 显示全部楼层
小小速 发表于 2016-11-21 23:39
我做的话,大延时,会把有delay的地方前后分开成两个状态,delay的时间去重新赋值任务TaskComp.Timer,等 ...

你有没有想过,假如你某个任务里面有20处地方需要延时,而且延时时间都不是一个定值,那该怎么处理?
拿来长岛冰茶换我半晚安睡
回复

使用道具 举报

27

主题

711

帖子

0

精华

版主

Rank: 7Rank: 7Rank: 7

积分
11922
金钱
11922
注册时间
2015-11-5
在线时间
2086 小时
发表于 2016-11-22 00:06:54 | 显示全部楼层
小小速 发表于 2016-11-21 23:39
我做的话,大延时,会把有delay的地方前后分开成两个状态,delay的时间去重新赋值任务TaskComp.Timer,等 ...

针对你这个帖子,我另外开了个帖子,代码在那帖子里,有兴趣的话就花些时间研究下呗!
http://www.openedv.com/forum.php ... p;page=1&extra=
拿来长岛冰茶换我半晚安睡
回复

使用道具 举报

21

主题

109

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
321
金钱
321
注册时间
2015-3-28
在线时间
64 小时
 楼主| 发表于 2016-11-22 00:11:06 | 显示全部楼层
FreeRTOS 发表于 2016-11-21 23:54
你有没有想过,假如你某个任务里面有20处地方需要延时,而且延时时间都不是一个定值,那该怎么处理?

那……把必须把delay函数单独拿出来重新定义……不过没想好怎么定义,上面说的时间轮询每次轮询一个任务,并无法保存任务运行到一半时的状态。重新定义delay的时候必须考虑怎么保存任务运行状态…可是不知道怎么做才能单纯的用c语言通用的构架去保存
回复

使用道具 举报

21

主题

109

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
321
金钱
321
注册时间
2015-3-28
在线时间
64 小时
 楼主| 发表于 2016-11-22 00:12:47 | 显示全部楼层
本帖最后由 小小速 于 2016-11-22 00:14 编辑
FreeRTOS 发表于 2016-11-22 00:06
针对你这个帖子,我另外开了个帖子,代码在那帖子里,有兴趣的话就花些时间研究下呗!
http://www.opene ...

明天再看困得受不了了
回复

使用道具 举报

22

主题

181

帖子

0

精华

高级会员

Rank: 4

积分
878
金钱
878
注册时间
2014-7-7
在线时间
311 小时
发表于 2016-11-22 11:19:56 | 显示全部楼层
楼主,你贴的代码,是属于时间触发的嵌入式系统构架!
然而,楼主你的理解本质上是错的!
原因:TaskProcess这个函数就不应该在中断里运行,而是应该在主循环运行!
结构是:IRQ:TaskRemarks
              Main Loop:TaskProcess
这是典型的前台负责系统滴答,后台负责任务轮询调度!
然后:在设计任务的时候,设计任务时单次运行时间应该越短越好!
除时序延时外的延时都尽量去除而采用状态转移(状态机)来处理!
假如要发送一个hello
如果while(print(hello))这种形式是最笨的设计方法!
用switch ---h---e---l---l---o把hello分成5次发送,发送不要死等标记,发送成功退出,不发送成功照样退出!
设计的时候考虑消费者生产者原则!认清辅助和主线任务,必要的时候要做取舍(偏心)!
回复

使用道具 举报

21

主题

109

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
321
金钱
321
注册时间
2015-3-28
在线时间
64 小时
 楼主| 发表于 2016-11-22 13:13:56 | 显示全部楼层
本帖最后由 小小速 于 2016-11-22 13:15 编辑
our单片机 发表于 2016-11-22 11:19
楼主,你贴的代码,是属于时间触发的嵌入式系统构架!
然而,楼主你的理解本质上是错的!
原因:TaskProc ...

中断里面是remark……不是remark下面所有都是中断……可能没写主函数让你理解错了
回复

使用道具 举报

22

主题

181

帖子

0

精华

高级会员

Rank: 4

积分
878
金钱
878
注册时间
2014-7-7
在线时间
311 小时
发表于 2016-11-22 13:39:30 | 显示全部楼层
时间片轮询去执行任务,每个任务的执行周期是在上面结构体里初始化的,然后每隔一定时间(如1ms)进入定时器中断,在中断函数里刷新任务,同时进行调度,这个能看懂
回复

使用道具 举报

21

主题

109

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
321
金钱
321
注册时间
2015-3-28
在线时间
64 小时
 楼主| 发表于 2016-11-22 13:43:30 | 显示全部楼层
本帖最后由 小小速 于 2016-11-22 13:49 编辑
our单片机 发表于 2016-11-22 13:39
时间片轮询去执行任务,每个任务的执行周期是在上面结构体里初始化的,然后每隔一定时间(如1ms)进入定时 ...

好吧,我去找我语文老师,问问他是怎么教的总之,昨天折腾一天,有你们的帮助,很有收获
回复

使用道具 举报

21

主题

109

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
321
金钱
321
注册时间
2015-3-28
在线时间
64 小时
 楼主| 发表于 2016-11-22 13:52:43 | 显示全部楼层
our单片机 发表于 2016-11-22 13:49
比如有一个IIC的任务每隔100ms产生一次通信,这个100ms的周期可以在上面的任务结构体里面初始化参数uint8 I ...

看完这段更清晰了,昨天还在头疼呢
回复

使用道具 举报

22

主题

181

帖子

0

精华

高级会员

Rank: 4

积分
878
金钱
878
注册时间
2014-7-7
在线时间
311 小时
发表于 2016-11-22 14:00:31 | 显示全部楼层
正点原子 发表于 2016-11-21 18:37
楼主这种方式,只能每个任务的运行时间必须小于你的调度周期,否则就肯定出问题了。。。

出问题那是设计者笨!
任务的运行期间出现中断是必然的事情,这个时候就需要你来保护了(原子保护)
这个正点原子保护在你们用的OS中有个名词,叫临界区!
回复

使用道具 举报

56

主题

289

帖子

0

精华

高级会员

Rank: 4

积分
865
金钱
865
注册时间
2012-11-16
在线时间
65 小时
发表于 2016-12-16 15:03:09 | 显示全部楼层
看了这么多,28楼是高手
回复

使用道具 举报

63

主题

305

帖子

1

精华

高级会员

Rank: 4

积分
853
金钱
853
注册时间
2012-8-3
在线时间
79 小时
发表于 2017-7-21 10:56:26 | 显示全部楼层
mark
回复

使用道具 举报

7

主题

63

帖子

0

精华

高级会员

Rank: 4

积分
530
金钱
530
注册时间
2013-12-26
在线时间
124 小时
发表于 2017-9-25 09:31:08 | 显示全部楼层
Mark一下,,,
回复

使用道具 举报

0

主题

1

帖子

0

精华

新手入门

积分
3
金钱
3
注册时间
2021-5-30
在线时间
0 小时
发表于 2021-11-10 11:56:44 | 显示全部楼层
写一个并行式的的延时函数,第一次运行查系统时间,下次运行比对延时就可以了。缺点是没法要求双向精确,只能是延时时间至少大于设定时间,等于说是把延时的死等循环和大循环再合并一次。
代码实现上可能要用到静态局部变量,宏函数和GNU扩展支持才好实现
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-22 12:40

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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