搜索
bottom↓
回复: 163

单片机编程模式提高--面向任务

  [复制链接]

出0入0汤圆

发表于 2012-4-19 15:28:11 | 显示全部楼层 |阅读模式
我也是个初学者 题目起的对不对不太清楚 我就是这么理解的吧

自己之前学过一阵子avr单片机,也做了一些小玩意儿,自认为对代码结构优化的还不错。
最近看了一本书,发现自己的程序还是面向过程的最基本的方式写的,书中提供了一种编程模式--面向任务,非常不错。(也可能是我很小白)
推荐给大家---《时间触发嵌入式系统设计模式》

学完单片机不知道怎么提高 就看这本书 下面有一个单片机实现简易操作系统的例子(就是一个任务调度器,为书中的例子)

简易的任务调度模式,简易的系统
一个调度器的部分:调度器数据结构、初始化函数、中断服务,用来以一定的时间间隔刷新调度器、向调度器添加任务、使任务在应当执行的时候被执行、从调度器删除任务(不是必须)。
思想:面向任务

①调度器数据结构:
typedef data struct
{
        void (*pTask)(void);//指向任务的指针
        INT16U delay;//距离下一次执行的时间
        INT16U period;//连续执行的周期
        INT8U  run;//为0时任务不执行,为1时任务执行
}sTask;
# define SCH_MAX_TASKS //最大任务数
sTask SCH_tasks[SCH_MAX_TASKS];//任务队列

②初始化函数:初始化定时器,并设置“时标”
void SCH_Iint(void)
{
        //设置定时器,产生周期中断,比如1ms
}
应保证调度器只有一个中断源

③刷新函数:调度器的中断服务函数,当其确定某个任务要执行时,将run设置为1,该任务就会由调度程序执行。
ISR(TIMER0_OVF_vect)
{
        INT8U index;
        //充填定时器,1ms

        for(index = 0; index < SCH_MAX_TASKS; index++)//遍历任务队列
        {
                if(SCH_tasks[index].pTask)//有任务
                {
                        if(SCH_tasks[index].delay == 0)//任务需要执行
                        {
                                SCH_tasks[index].run = 1;//设置执行标志
                                if(SCH_tasks[index].period != 0)//任务为周期执行
                                        SCH_tasks[index].delay = SCH_tasks[index].period;
                        }
                        else//任务没到执行时间
                                SCH_taska[index].delay--;
                }        
        }
}

④添加函数:用来向任务队列添加新的任务
INT8U SCH_Add_Task(void (*pFunction)(), INT16U delay, INT16U period)
{
        INT8U index = 0;
        while ((SCH_tasks[index].pTask != 0) && (index < SCH_MAX_TASKS))//找到队列空闲位置
                index++;
        if (index == SCH_MAX_TASKS)
        {
                //返回错误代码
        }
        //有位置的话
        SCH_tasks[index].pTask = pFunction;
        SCH_tasks[index].delay = delay;
        SCH_tasks[index].period = period;
        SCH_tasks[index].run = 0;
        return index;//返回任务ID
}

⑤调度函数
void SCH_Dispatch_Tasks(void)
{
        INT8U index;
        for (index = 0; index < SCH_MAX_TASKS; index++)//遍历队列找到第一个需要执行的任务并执行
        {
                if (SCH_tasks[index].run > 0)
                {
                        (*SCH_tasks[index].pTask)();//执行任务
                        SCH_tasks[index].run = 0;
                        if (SCH_tasks[index].period == 0)
                                SCH_Delete_Task(index);
                }               
        }
        //报告系统状况
        SCH_Report_Status();
        //调度器进入空闲模式
        SCH_Go_To_Sleep();
}
主函数的的while(1),调用此函数即可

⑥开始函数
void SCH_Start(void)
{
        sei();//开始全局中断
}

⑦删除函数
INT8U SCH_Delete_Task(INT8U index)
{
        if (SCH_tasks[index].pTask == 0)
                //返回错误代码
        SCH_tasks[index].pTask = NULL;
        SCH_tasks[index].delay = 0;
        SCH_tasks[index].period = 0;
        SCH_tasks[index].run = 0;
        //返回正确删除代码
}

⑧休眠函数:支持低功耗,在空闲时睡眠,并由定时器中断唤醒
void SCH_Go_To_Sleep(void)
{
        //设置睡眠方式
}

⑨增加一个看门狗,在调度开始函数初始化看门狗,在调度刷新函数喂狗

处理任务重叠:
SCH_Add_Task(FunctionA, 0, 1000);
SCH_Add_Task(FunctionB, 0, 3000);
任务A1s执行一次,任务B3s执行一次,则任务B每次执行都必须任务A执行完毕,若任务A运行时间变化,任务B就发生“抖动”,无法保证3s的周期,没有正确调用任务B。
SCH_Add_Task(FunctionA, 0, 1000);
SCH_Add_Task(FunctionB, 5, 3000);
只要任务A最慢可以在5ms内执行结束,任务B就可以正常执行,周期为3s。

有这么一种情况,时标为1ms,但是有可能某任务某一次运行时间超过1ms,比如一些while()判断ad转换是否完成,串行数据是否收到,都有可能使循环长时间执行,最终超过1ms,而使得其他可能该执行的任务没有执行,怎么办???
另设置一个定时器,假设定时500us,在while()中除了对ad转换完成,串行数据传输等待外,添加对此定时器溢出位的读取,当定时器溢出则跳出循环,执行后面语句,保证整个任务可以再1ms内完成。(导致此次任务执行可能会失败哦)

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

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出0入0汤圆

发表于 2012-4-20 09:00:43 | 显示全部楼层
先看看。

出0入0汤圆

发表于 2012-4-20 09:21:36 | 显示全部楼层
从傻孩子的帖子里知道的这本书

出0入0汤圆

发表于 2012-4-20 12:39:45 | 显示全部楼层
有这个本书吗?论坛上找不到这本书的资源

出0入0汤圆

发表于 2012-4-20 13:10:21 | 显示全部楼层
不错,学习一下。

出0入0汤圆

发表于 2012-4-20 13:12:16 | 显示全部楼层
aladiu 发表于 2012-4-20 12:39
有这个本书吗?论坛上找不到这本书的资源

在iask上找

出0入0汤圆

发表于 2012-4-20 13:46:08 | 显示全部楼层
不错,学习一下。

出0入0汤圆

 楼主| 发表于 2012-4-20 20:08:31 | 显示全部楼层
书有点大 不上传了 网上还是可以找到的

出0入0汤圆

发表于 2012-4-20 20:15:03 | 显示全部楼层
跟系统中的任务调度类似

出425入0汤圆

发表于 2012-4-20 21:31:32 | 显示全部楼层
我最近在做一个产品,正在琢磨这种结构。我弄本书来看看。期待大家多探讨这类的话题。

出0入0汤圆

发表于 2012-4-20 21:58:26 | 显示全部楼层
各位大侠,我想问一下:关于《时间触发嵌入式系统设计模式》书中提到的:    保证调度器只有一个中断源
这句话这么理解?  
是不是说如果MCU中用了这种调度器结构,那么 只能有一个定时器中断使能作为系统时标;其他的比如串口的、其他定时器的、外部中断都不能用了?这样岂不是很浪费?如果能用,该如何融合?
初学者的疑问,困惑中,忘高手赐教!~

出0入0汤圆

 楼主| 发表于 2012-4-20 23:36:48 | 显示全部楼层
Free_Bird 发表于 2012-4-20 21:58
各位大侠,我想问一下:关于《时间触发嵌入式系统设计模式》书中提到的:    保证调度器只有一个中断源
这 ...

意思是说 作为调度器时标的定时器只能有一个 书后面部分也讲解了使用外部中断的调度器 使用异步收发器的调度器
但是对于再使用一个定时器 我不知道你想做什么 如果是作为时标已经不必再用 周期性的任务都由调度器调用
还有问题 多多交流

出0入0汤圆

发表于 2012-4-21 00:05:14 | 显示全部楼层
本帖最后由 Free_Bird 于 2012-4-21 00:14 编辑
xiaoziwen 发表于 2012-4-20 23:36
意思是说 作为调度器时标的定时器只能有一个 书后面部分也讲解了使用外部中断的调度器 使用异步收发器的 ...


你说的我明白了点;我才看到合作式调度器;可能是我关于系统方面的知识太匮乏了,多实践吧嘿嘿;

感谢楼主的关注 和 这样无私的分享精神;  赞 ~!

出0入0汤圆

 楼主| 发表于 2012-4-21 09:31:05 | 显示全部楼层
Free_Bird 发表于 2012-4-21 00:05
你说的我明白了点;我才看到合作式调度器;可能是我关于系统方面的知识太匮乏了,多实践吧嘿嘿;

感谢楼 ...

我也是新手 多多交流 呵呵

出0入0汤圆

发表于 2012-4-21 12:12:55 | 显示全部楼层
hellobear 发表于 2012-4-20 13:12
在iask上找

OK,谢谢,我找到了。

出425入0汤圆

发表于 2012-4-29 17:09:34 | 显示全部楼层
我来探讨一下:
如果让一个LED灯亮1秒,灭1秒。3次后熄灭。初学者(MCU低效率)一般是先设定3次,亮--等待1秒--灭--等待1秒,次数减一,检测次数不等于0,循环。
for(i=0;i<3;i++)
{
    led=1;  
    delayms(1000);
    led=0;
    delayms(1000);
}

在实际的工程中,不能用delay,要把它写成一个有初始化,过程,结束的一个东东。用面向任务的思考方式,怎样写这个任务?

出0入0汤圆

 楼主| 发表于 2012-5-2 19:28:47 | 显示全部楼层
guolun 发表于 2012-4-29 17:09
我来探讨一下:
如果让一个LED灯亮1秒,灭1秒。3次后熄灭。初学者(MCU低效率)一般是先设定3次,亮--等待1 ...

任务周期是1s,时标假设为1ms,这样任务的周期就是1000时标,每过1000个时标执行以下程序。
程序段中需要一个static变量,进入任务后检测static变量的奇偶性。假设static unsigned char a = 0,为为偶数时点亮led,为奇数熄灭,这样第一秒亮,第二秒熄灭,第三秒亮。。。。。第六秒熄灭后删除任务即可。
不知道理解的对不。

出10入10汤圆

发表于 2012-5-2 20:12:17 | 显示全部楼层
《时间触发嵌入式系统设计模式》是一本好书,现在好像买不到了

出425入0汤圆

发表于 2012-5-2 20:26:58 | 显示全部楼层
“程序段中需要一个static变量”。我没有想通的问题在这里。灯闪烁到第四次的时候,static a==3,灯灭了。这时有个其他的操作,让它停下来了(任务删除了)。下一次要任务调度器运行这个闪烁的程序段时,static会被初始化吗?如果不能,它==5的时候,程序段判断到终点,将会调用任务删除函数。如果初始化 a,怎样让程序段不会反复初始化a?

出425入0汤圆

发表于 2012-5-2 20:31:25 | 显示全部楼层
本帖最后由 guolun 于 2012-5-2 20:35 编辑
xiaoziwen 发表于 2012-5-2 19:28
任务周期是1s,时标假设为1ms,这样任务的周期就是1000时标,每过1000个时标执行以下程序。
程序段中需要 ...


“程序段中需要一个static变量”。我没有想通的问题在这里。灯闪烁到第四次的时候,static a==3,灯灭了。这时有个其他的操作,让它停下来了(任务删除了)。下一次要任务调度器运行这个闪烁的程序段时,static会被初始化吗?如果不能,它==5的时候,程序段判断到终点,将会调用任务删除函数。如果程序段初始化 a,那么每次运行这个程序段a都会被初始化,怎样让程序段不会反复初始化a?或者说,每次执行闪烁3次的任务,调用闪烁的程序段时,只进行一次初始化a=0。其他5次不执行初始化?

出0入0汤圆

 楼主| 发表于 2012-5-2 20:36:09 | 显示全部楼层
guolun 发表于 2012-5-2 20:31
“程序段中需要一个static变量”。我没有想通的问题在这里。灯闪烁到第四次的时候,static a==3,灯灭了 ...

a%2 == 0 点亮
a%2 == 1 熄灭

0点亮 1熄灭 2点亮 3熄灭 4点亮 5熄灭 6点亮

对于6 此时希望其初始化为0 对不对

那么我在程序刚进来时a%6就好了

这样a为6时,结果为0,完成初始化

不知道对不对

出425入0汤圆

发表于 2012-5-2 20:41:42 | 显示全部楼层
本帖最后由 guolun 于 2012-5-2 20:56 编辑
xiaoziwen 发表于 2012-5-2 20:36
a%2 == 0 点亮
a%2 == 1 熄灭


判断哪个量来结束闪烁?
flash(){
        if(a%5!=0){
                a++;
                swtich(a%2)
                case 0:led=1;break;
                case 1:led=0;break;
        }
        else{
                delete_sch_task();
        }
}
这样对吗?

出425入0汤圆

发表于 2012-5-2 20:58:43 | 显示全部楼层
本帖最后由 guolun 于 2012-5-2 21:20 编辑

上面的代码有误,修改如下:
flash(){
        if(a/6<1){
                swtich(a%2)
                case 0:led=1;break;
                case 1:led=0;break;
                a++;
        }
        else{
                a=0;
                delete_sch_task();
        }
}
请指教。

出0入0汤圆

 楼主| 发表于 2012-5-2 20:59:26 | 显示全部楼层
guolun 发表于 2012-5-2 20:41
判断哪个量来结束闪烁?

其实你说的这类人物具有特殊性
原先的调度器任务分为两种,第一种周期执行,第二种单次执行
你这种任务属于第三种,周期执行一段时间结束(但是,如果熄灭的时间是固定的,比如第六秒后熄灭10秒,则属于第一种情况)

这时需要做的不是添加过多的判断变量,而是应该修改TCB,添加新的成员变量
比如unsigned char last,表示任务执行的总时长,单位为任务周期,为255时表示一直周期执行(第一种任务),为1时单次执行(第二种任务)

你说的任务此变量就应该为6(6s),没执行一次任务变量-1,为0时删除任务。

明白没

出0入0汤圆

 楼主| 发表于 2012-5-2 21:02:56 | 显示全部楼层
guolun 发表于 2012-5-2 20:41
判断哪个量来结束闪烁?
flash(){
        if(a%5!=0){

还是有问题
初始化如果a是0,
任务永远开始不了了
因为a%5 == 0 一直执行else语句

出0入0汤圆

 楼主| 发表于 2012-5-2 21:05:08 | 显示全部楼层
guolun 发表于 2012-5-2 20:58
应该在删除任务前加入a=0;

而且 我们在写程序时最好做到封装严密
delete函数最好是由调度器调用,而不是任务调用,所以我个人认为我上述的方法更好一些

出425入0汤圆

发表于 2012-5-2 21:25:16 | 显示全部楼层
xiaoziwen 发表于 2012-5-2 21:05
而且 我们在写程序时最好做到封装严密
delete函数最好是由调度器调用,而不是任务调用,所以我个人认为我 ...

我同意你的观点,的确是修改任务结构会更好些。

出0入0汤圆

 楼主| 发表于 2012-5-2 21:28:16 | 显示全部楼层
guolun 发表于 2012-5-2 21:25
我同意你的观点,的确是修改任务结构会更好些。

呵呵 我说的都是理论上的玩意儿 不知道老哥实现的怎么样了 是要做什么产品吗???

出425入0汤圆

发表于 2012-5-4 20:54:55 | 显示全部楼层
xiaoziwen 发表于 2012-5-2 21:28
呵呵 我说的都是理论上的玩意儿 不知道老哥实现的怎么样了 是要做什么产品吗??? ...

最近一个产品上很可能会用到这样的编程思想。

出0入296汤圆

发表于 2012-5-5 14:57:39 | 显示全部楼层
又看到一个对调度器技术感兴趣的兄弟,赞。
作为过来人,我想给楼主一个建议:在概念上别被“基于时间触发”束缚住。这只是任务调度的一种基本形式,而且
并不是万能的,效率也不是最高的——如果你总是执着于用时间来触发的话。其实,任务调度的关键只有以下三点:
1、用于调度的函数指针,及相关的TASK结构体
2、基于触发的调度,触发有很多种,时间恰好只是最笨的一类
3、从触发来考虑,可以渐渐考虑基于消息触发的结构
--------------------------------
等你熟悉了基于消息触发的系统,你的系统应该拥有了相当的复杂度,这个时候要考虑最简化的形式:
即怎样做到最简单最灵活的“基于触发的任务调度”呢?
最终结论:
基于事件触发的调度器系统
进一步推论:
基于状态的多任务系统
……
期待你一路走过来,别忘记多看一些操作系统的知识

出0入0汤圆

发表于 2012-5-5 15:15:15 | 显示全部楼层
  1. 在概念上别被“基于时间触发”束缚住。这只是任务调度的一种基本形式,而且
  2. 并不是万能的,效率也不是最高的
  3. 基于触发的调度,触发有很多种,时间恰好只是最笨的一类
复制代码
完全同意。

状态机编程是一种非常有效的办法。
尤其是基于 面向对象的编程。

出0入0汤圆

 楼主| 发表于 2012-5-5 18:02:49 | 显示全部楼层
Gorgon_Meducer 发表于 2012-5-5 14:57
又看到一个对调度器技术感兴趣的兄弟,赞。
作为过来人,我想给楼主一个建议:在概念上别被“基于时间触发 ...

谢谢老哥的指导

本来就是看了老哥的一个帖子才看得《时间触发嵌入式设计》,感觉之前的程序真是想到哪里编导哪里,没有大体的框架。跟朋友讨论后,发现框架这东西还真多,也就准备多看看,并且把之前的某些程序改改,赶快上道。

关于状态机,其实在老哥的《深入浅出》和马老师的《avr》书中都有涉及,当时仅仅感觉是个按键去抖的好方法。但慢慢接触一些状态机的文章后也发现,状态机也可以做为程序的框架。

慢慢的也是向往操作系统上靠一靠,编程之余多掌握些理论知识。呵呵~~~

出0入0汤圆

发表于 2012-5-5 18:32:06 | 显示全部楼层
简单的任务调度,适用8位机系统。

出425入0汤圆

发表于 2012-5-7 14:59:36 | 显示全部楼层
Gorgon_Meducer 发表于 2012-5-5 14:57
又看到一个对调度器技术感兴趣的兄弟,赞。
作为过来人,我想给楼主一个建议:在概念上别被“基于时间触发 ...

受益良多,我写程序不多,没有什么框架,结构的编程思想。程序如何组织,自己去想,很不规范。也很吃力。对于调度器,信息,事件,状态等认识几乎空白。可否推荐几本书,系统的学习一下。(注:我不是科班出身的)

出0入296汤圆

发表于 2012-5-8 12:40:20 | 显示全部楼层
guolun 发表于 2012-5-7 14:59
受益良多,我写程序不多,没有什么框架,结构的编程思想。程序如何组织,自己去想,很不规范。也很吃力。 ...

入门的话先用《时间触发嵌入式设计》,接下来看《深入浅出MFC》(貌似只要头几个章节就可以了)
做到这两步,基本上就可以自己去探索和实践了,实践一段时间以后看高焕堂的《UML+OOPC》,
然后就牛X了……

出0入0汤圆

发表于 2012-5-8 12:43:59 | 显示全部楼层
不错,学习下

出425入0汤圆

发表于 2012-5-8 20:16:36 | 显示全部楼层
Gorgon_Meducer 发表于 2012-5-8 12:40
入门的话先用《时间触发嵌入式设计》,接下来看《深入浅出MFC》(貌似只要头几个章节就可以了)
做到这两 ...

你的深入浅出AVR第二版什么时候出?

出0入296汤圆

发表于 2012-5-9 00:38:57 | 显示全部楼层
guolun 发表于 2012-5-8 20:16
你的深入浅出AVR第二版什么时候出?

别等了,我压力大。出来自然会做宣传,死了不奇怪...对不起...

出0入0汤圆

发表于 2012-5-9 02:09:10 | 显示全部楼层
  1. ⑤调度函数
  2. void SCH_Dispatch_Tasks(void)
  3. {
  4.         INT8U index;
  5.         for (index = 0; index < SCH_MAX_TASKS; index++)//遍历队列找到第一个需要执行的任务并执行
  6.         {
  7.                 if (SCH_tasks[index].run > 0)
  8.                 {
  9.                         (*SCH_tasks[index].pTask)();//执行任务
复制代码
我不喜欢这种在调度器中直接调用任务函数的调度方式——它使得整个系统处于了不可控的状态。

出0入0汤圆

发表于 2012-5-9 07:47:30 | 显示全部楼层
学习!!!

出0入0汤圆

发表于 2012-5-9 08:42:04 | 显示全部楼层
上传这本书.

出0入0汤圆

 楼主| 发表于 2012-5-9 09:13:09 | 显示全部楼层
eduhf_123 发表于 2012-5-9 02:09
我不喜欢这种在调度器中直接调用任务函数的调度方式——它使得整个系统处于了不可控的状态。 ...

说说原因 或者提供个好方法

出0入0汤圆

发表于 2012-5-9 09:51:10 | 显示全部楼层
任务函数异常或不返回,就无法调度了

出0入0汤圆

发表于 2012-5-9 10:09:01 | 显示全部楼层
任务函数异常或不返回,就无法调度了

出0入0汤圆

发表于 2012-5-9 11:00:35 | 显示全部楼层
zhanghongdong97 发表于 2012-5-9 09:51
任务函数异常或不返回,就无法调度了

对,就是这个原因。

出0入0汤圆

发表于 2012-5-9 11:00:52 | 显示全部楼层
xiaoziwen 发表于 2012-5-9 09:13
说说原因 或者提供个好方法

就是LSS说的这个原因。

出0入0汤圆

 楼主| 发表于 2012-5-9 11:23:01 | 显示全部楼层
zhanghongdong97 发表于 2012-5-9 09:51
任务函数异常或不返回,就无法调度了

也是,程序永远卡在这里了,那实际上还是任务的容错机制要合适,在适当的时候是要退出任务的,即使这次任务执行失败。
这样是不是可以,或者有别的方法》????

出0入0汤圆

 楼主| 发表于 2012-5-9 11:23:28 | 显示全部楼层
eduhf_123 发表于 2012-5-9 11:00
就是LSS说的这个原因。

也是,程序永远卡在这里了,那实际上还是任务的容错机制要合适,在适当的时候是要退出任务的,即使这次任务执行失败。
这样是不是可以,或者有别的方法》????

出0入0汤圆

发表于 2012-5-9 15:07:03 | 显示全部楼层
xiaoziwen 发表于 2012-5-9 11:23
也是,程序永远卡在这里了,那实际上还是任务的容错机制要合适,在适当的时候是要退出任务的,即使这次任 ...

从系统的鲁棒性出发,我们能自然地得到这样的要求:进程崩溃不应造成系统崩溃。

只要至少支持在中断服务程序中进行调度,就能在很大程度上避免一个进程的死循环造成整个系统失去响应。当然了,这样代价会高一些,系统也更加地复杂。

出0入0汤圆

发表于 2012-5-9 15:53:35 | 显示全部楼层
这种简单调度机制,需要把任务设计成 状态机 的方式,尽量把任务切割成一个很小的时间片,每个状态是控制在一个时间片内。

出0入0汤圆

 楼主| 发表于 2012-5-9 15:56:28 | 显示全部楼层
eduhf_123 发表于 2012-5-9 15:07
从系统的鲁棒性出发,我们能自然地得到这样的要求:进程崩溃不应造成系统崩溃。

只要至少支持在中断服务 ...

我猜想啊 毕竟只是水平还不到位

你这样的思路可以具体到这样的例子:两个定时器,一个负责时间片流逝,一个负责为下一个任务执行定时。
--A--B--C-- -- ,5个时间片,A、B、C为三个任务,假设其任务周期都是5个时间片,如图也就是一个周期。
定时器1计算各任务的下次开始所剩时间。
定时器2用来为下个任务所剩的时间定时,并在溢出中断执行任务。

这样的话,系统一开始,定时器2定时一个时间片+间隔(用来错开定时器1),等待任务A执行;
到第二个时间片,定时器1将任务A标志可执行,定时器2溢出,执行任务A,并关闭定时器2;
到第三个时间片,定时器1将任务B标志可执行,设置定时器2定时间隔,定时器2溢出,执行任务B,并关闭定时器2;
到第四个时间片,定时器1将任务C标志可执行,设置定时器2定时间隔,定时器2溢出,执行任务C,并关闭定时器2;
到第五个时间片,定时器1发现任务A还有2个时间片执行,设置定时器2定时两个时间片+间隔;
到第六个时间片,定时器1发现任务A还有1个时间片执行;
到第七个时间片,定时器1将任务S标志可执行,定时器2溢出,执行任务A,并关闭定时器
。。。。。。。。。。。。。。。。。。。。。。。。。

貌似就是这样个思路 的确老麻烦了~~~~~~~~~~~~

出0入0汤圆

 楼主| 发表于 2012-5-9 15:58:29 | 显示全部楼层
zhanghongdong97 发表于 2012-5-9 15:53
这种简单调度机制,需要把任务设计成 状态机 的方式,尽量把任务切割成一个很小的时间片,每个状态是控制在 ...

对啊 保证一个时间片内能执行完任务是必须的
那有什么方法可以保证 任务崩溃不会导致系统调用出错 呢

出425入0汤圆

发表于 2012-5-9 20:54:39 | 显示全部楼层
我现在做的一个食品机器,有红外漫射探头,检测人手进入危险区域,同时也检测按键是否按下。我用C++编写,电机运转,探头检测,按键检测封装在一起。运行电机的函数,如果作为任务加入任务队列,但它1.不是只运行一次,2.不是周期的。3.不是一段确定的时间。它的结束是事件触发的(就是位置检测探头检测到运行到终点位置)。简单照搬这个时间调度器的程序结构看来行不通。

出0入0汤圆

 楼主| 发表于 2012-5-9 21:09:51 | 显示全部楼层
guolun 发表于 2012-5-9 20:54
我现在做的一个食品机器,有红外漫射探头,检测人手进入危险区域,同时也检测按键是否按下。我用C++编写, ...

我不太明白 你这个东西的结构与功能
如果单纯说 探头检测到终点就改变电机的运行状态
那么跟检测到按键就做处理不是一样的嘛

老哥还是说清楚一点好

出425入0汤圆

发表于 2012-5-9 23:12:21 | 显示全部楼层
xiaoziwen 发表于 2012-5-9 21:09
我不太明白 你这个东西的结构与功能
如果单纯说 探头检测到终点就改变电机的运行状态
那么跟检测到按键 ...

我把代码贴出来:方便讨论:
#include "make_motor.h"

void make_motor::run(){   //
                if(safety_flag==1 && press_flag==1) //没有物体进入危险区域,两手按下按键,
                        HC595_data |= 1<<MAKE_MOTOR;  // 电机的控制口输出高电平
                else
                        HC595_data &= ~(1<<MAKE_MOTOR);  
}

inline void make_motor::stop(){        
        HC595_data &= ~(1<<MAKE_MOTOR);
}

void make_motor::check_safety(){  //这个函数要求每1ms运行一次
        static uchar count;
        if(SAFE_SENSOR==0){ //物体进入危险区域
                count++;
                if(count>2)  // 去干扰
                        safety_flag = 1;  
        }
        else{
                safety_flag = 0;
                count = 0;
        }
}

void make_motor::check_press(){  //要求同上
        static uchar count;
        if(MAKE_KEY==0 && LOAD_KEY==0){ //两手按下按键
                count++;
                if(count>2)  //去干扰
                        press_flag = 1;               
        }
        else{
                press_flag = 0;
                count = 0;
        }
}

void make_motor::check_up(){
        static uchar count;
        if(MAKE_UP_SENSOR == 1){ //到达最高点
                count++;
                if(count>2)
                        up_flag = 1;
        }
        else{
                up_flag = 0;
                count = 0;
        }
}

void make_motor::check_down(){
        static uchar count;
        if(MAKE_DOWN_SENSOR == 1){ //到达最低点
                count++;
                if(count>2)
                        down_flag = 1;
        }
        else{
                down_flag = 0;
                count = 0;
        }
}

make_motor MA_MOTOR;

void main(){
        while(1){
                if(ms_flag){  //1ms中断标志
                        ms_flag = 0;
                        ms++;
                        send_HC595_data(HC595_data);
                        MA_MOTOR.check_safety();
                        MA_MOTOR.check_press();       
                        MA_MOTOR.check_up();
                        MA_MOTOR.check_down();
                        MA_MOTOR.run();
                }
        }
}

上面的main是时间轮询的平面的结构。但我的机器类似的电机控制还有4个,步进电机2个,还有按键,显示等很多东西,还有复杂的参数设置,半自动全自动的工作模式。我在寻找一种组织程序的框架,结构,力求逻辑更清晰。状态机,消息触发,任务队列等如何应用?

出0入0汤圆

 楼主| 发表于 2012-5-10 10:57:37 | 显示全部楼层
guolun 发表于 2012-5-9 23:12
我把代码贴出来:方便讨论:
#include "make_motor.h"

我们简化一下系统
按键检测与相应---void make_motor::check_press();
安全监测及相应---void make_motor::check_safety();
运转---void make_motor::run();
检测高低点---void make_motor::check_up(),void make_motor::check_down(),可以合成一个任务。

感觉上hc595发送数据是用来控制电机对吧,这样的话可以和运转合成一个任务。

运转决定于按键、安全两个任务,所以任务的执行顺序,最好是先按键、安全,然后再运行;至于检测高低点,我猜想也是用来改变运行状态,你没说明我就不说这部分了。

接下来我觉得你设置时间片为1ms太小了,按键去抖动检测在10~20ms为宜,而且安全监测除非要求特别严格,否则10~20ms应该不至于出什么事故吧。
所以这三个任务以15ms为周期,开始时间为别是1ms,2ms,3ms就好(高低点儿没加入)。

再说说状态机
你的程序中去抖动是通过cnt计数,这是刚开始我们能想到的第一种方式,状态机其实就是对应用这个cnt,例如我写个按键的,以你的代码改编:
void make_motor::check_press(){  //要求同上
        static uchar count = 0;
           uchar key_press = 0;
           key_press = (MAKE_KEY==0 && LOAD_KEY==0);
         switch(count){
         case:0   
                    if (key_press)
                             count = 1;
              break;
         case:1
                    if (key_press)
                    {
                              count = 2;
                              press_flag = 1;
                   }
                    else
                              count = 0;
                   break;
              case : 2
                      if (!key_press)
                              count = 0;
        }
}


count 表示按键状态, key_press表示按键是什么
15ms扫描间隔,若有按键按下
第一次进入conut = 0,进入case1,发现有按键按下状态count变成1,
第二次进入,count = 1,进入case2,发现有按键,这时可以确定确实按键被按下,press_flag置一
第三次进入,发现无按键,状态回0


其他的安全监测也是这个思路。

至于你说的好几个电机什么的,在进入相关安全监测时先确定是那个电机需要修改状态在处理。

不知道帮到你没

出425入0汤圆

发表于 2012-5-10 14:59:05 | 显示全部楼层
本帖最后由 guolun 于 2012-5-10 15:01 编辑
xiaoziwen 发表于 2012-5-10 10:57
我们简化一下系统
按键检测与相应---void make_motor::check_press();
安全监测及相应---void make_mot ...


总结一下:你说了各个任务的时间间隔上的安排。执行顺序的安排。用按键作为例子,用状态机实现。
按键的状态机写法也很不错。你的思路很清晰。
时间上的安排,我安排的时间更长一些,上例中的1ms是个例子而已。如,为节省CPU的时间,按键可以30ms检测一次,刷新HC595,显示,高低点可以5ms检测一次。其他按键扫描10ms一次。
可能我没有表达清楚我的困惑:我的main中,一堆的动作,为了方便阅读,我后面省略了更多。这些动作里面,有一次性完成的动作:如:stop(),有周期性的动作:如:HC595数据刷新。有一段时间内运行的动作,如:蜂鸣器响60毫秒。有运行有限次数的动作,如:LED闪烁3次。有连续运行但外部事件触发而停止的动作,如电机运转。而且,在不同的状态中,出现的动作不同。如何区分消息,事件,动作。如何用状态机,组织这些动作?还有,如果用任务调度器,如何设计一个任务数据结构,适合不同的动作。如何添加进去,如何判断需要删除?
说到底,就是如何建立一个程序的框架。

出0入0汤圆

 楼主| 发表于 2012-5-10 15:25:23 | 显示全部楼层
guolun 发表于 2012-5-10 14:59
总结一下:你说了各个任务的时间间隔上的安排。执行顺序的安排。用按键作为例子,用状态机实现。
按键的 ...

我觉得啊 所谓的动作不过都是器件不同的状态而已
所以最后实际的任务就是判断不同器件的状态并执行相应的程序段,stop作为一个单次动作,但也是某器件的一个状态而已,没有必要专门设定为一个任务

状态机的思想就是要把任务分成不同阶段,在不同状态执行不同的程序,但都是一个任务

设置任务结构就要抓住的固有特性,即那些属性是大家在执行任务时都要判断或者处理的,就有必要提取出来

至于如何添加与删除,我个人觉得还是你把本该是一个任务的很多小任务分开了
就比如前几天我们讨论的led问题 我猜想他的闪烁应该是外部按键或者别的操作的相应的一部分
当有外部操作,led进入闪烁状态,闪烁够6s后,进入熄灭状态
这样对于led来说就只有一个任务,只是因为状态不同执行的程序就不同
完全没必要像上次咱们讨论的再加入一种任务类型

不知道你明白没?

出425入0汤圆

发表于 2012-5-10 22:19:30 | 显示全部楼层
xiaoziwen 发表于 2012-5-10 15:25
我觉得啊 所谓的动作不过都是器件不同的状态而已
所以最后实际的任务就是判断不同器件的状态并执行相应 ...

你说:没必要像上次咱们讨论的再加入一种任务类型。我感觉,这个任务结构,只适合只运行一次的(priod==0会被删除),或者只适合周期性运行的(priod!=0)。假如我在程序中某处添加了LED的闪烁任务,6S后,LED闪烁完了,LED在熄灭的状态。这个LED闪烁任务会被删除吗?怎么删除?

出0入296汤圆

发表于 2012-5-10 23:43:08 | 显示全部楼层
本帖最后由 Gorgon_Meducer 于 2012-5-10 23:57 编辑
guolun 发表于 2012-5-10 22:19
你说:没必要像上次咱们讨论的再加入一种任务类型。我感觉,这个任务结构,只适合只运行一次的(priod==0 ...


终于有人把问题提到点子上了,所以忍不住站出来嘚瑟一下:
对于这种情况,有很多解决方法,我觉得比较好的一种就是被称为“任务自回收式合作式调度器”。

说起来很简单:
1、把一个大任务拆分成很多原形相同的子程序,每一个子程序都表示一个步骤(通常用来表示状态机的一个状态)。
   而要求使用相同的原形,是为了使用函数指针。
2、每一个子程序都直接指定自己的后续(状态机中就是指定下一个状态的地址)
-------------------------华丽的分割线-----------------------------
以上的内容就是经典的 “状态机函数指针描述法(调度法)”,下面我要介绍基于这个方法的一个改进,也就是所谓
的任务自回收模式:
-------------------------华丽的分割线-----------------------------
3、我们为调度的基本单位定一个TASK块,这是常见的方法,在这个TASK块里面有一个函数指针,指向TASK要运行的
   具体任务(这也是常见的处理方法,没什么稀奇的)。
4、TASK块所运行的那个函数会有一个返回值,返回true或者false。定义true表示TASK块指定的函数还要运行,false,
   表示TASK快指定的那个函数不需要运行了。
5、建立一个TASK队列,也就是传说中的ready队列,一定要用链表来做。
6、建立一个函数,叫做register_task()。这个函数接收一个函数指针(也就是要在注册的时候指定运行的目标函数),
   以及其他参数,比如运行目标函数的时候所要传递的参数列表等等(这个不重要)。这个register_task只干一件事情
   就是从TASK池(堆)中申请一个TASK,按要求设置好它,最后扔到ready队列里面。
6、亮点来了:调度器工作的时候,每次从队列中取一个TASK,并运行其函数指针指向的函数。如果函数返回false,则
   直接把TASK注销——也就是仍回TASK池(堆描述的)中;如果函数返回true,先把当前的TASK注销(同上),然后
   根据刚才注销TASK里面的内容(比如运行哪个函数,传递的参数列表之类),调用register_task()来注册一个新任务。
-------------------------华丽的分割线----------------------------
这就是工作原理,用户要做的事情永远是调用register_task,系统每次都会立即自动注销当前TASK。是不是很类似自动
垃圾收集的原理?你当然也可以称其为任务的垃圾收集器。
那么怎么用呢?考虑以下几种情况
1、建立新任务:当然是register_task(),这个不罗嗦
2、怎么在从一个子状态(函数)跳到下一个子状态呢?
    a. 调用register_task,并把下一个子状态函数传递给他.
      b. 当前子状态return false.
3、怎么结束一个任务?在没有调用regiseter_task的情况下直接return false。
4、怎么知道当前系统有几个活跃的线程?直接看ready队列里面元素的数量。
5、怎么知道系统idle?直接看ready队列是不是空……
6、还有其他特性么?有,简单的在TASK块里面加入一个函数指针的栈和简单的修改,就可以支持子线程的调用,而不必
   让当前线程傻乎乎的在查询子线程的状态。
7、怎么让事情变得简单?
    定义宏,比如把register_task分别定义成TRANSFER_TO_STATE(...)  、NEW_FSM()、CALL_SUB_FSM()等等……
...  
8、这个系统支持多任务么?只要你很好的把每一个任务都拆分成细小的步骤,并用一个函数来描述,每一个这样的函数
   都是NONE-Block的,那么这个系统就是多任务的。这种模式有其适合状态机。
9、如何定义系统允许的最大任务数量?和状态机的状态数量有关么?和状态机状态数量无关。任务的数量也是没有限制的,
   但是有一个最大活跃任务的限制。简单说就是活着的任务有限制。这个当然直接由TASK池中TASK的数量限制的。
10、有最大的活跃人物限制对开发有什么限制么?其实没有,因为register_task是一个有true/false返回的函数,因为系统
   都是由状态机编写的。所以如果要考虑对最大活跃任务的兼容,你可以在调用register_task的时候检测返回值,肯定是
   返回true,才将当前的状态返回false。这样做以后,就变成用时间换资源了。当然,在极端的情况下,还是要考虑死锁
   的可能性。


-------------------------华丽的分割线----------------------------
关于这个调度器,请看帖子
http://www.ourdev.cn/thread-4319119-1-2.html

关于应用例子,请看帖子
http://www.ourdev.cn/thread-5468708-1-1.html

出425入0汤圆

发表于 2012-5-11 08:24:10 | 显示全部楼层
Gorgon_Meducer 发表于 2012-5-10 23:43
终于有人把问题提到点子上了,所以忍不住站出来嘚瑟一下:
对于这种情况,有很多解决方法,我觉得比较好 ...

十分高兴这个话题把傻哥引出来了。你的调度器大餐我一时消化不了。等有想法了,再请教。

出0入0汤圆

发表于 2012-5-11 09:00:58 | 显示全部楼层
这个帖子含金量高了,记得以前有个51上跑最简单的操作系统的帖子,说的也很精彩!!

出425入0汤圆

发表于 2012-5-11 09:02:57 | 显示全部楼层
本帖最后由 guolun 于 2012-5-11 09:04 编辑

我现在的想法是:利用第4点的想法,把函数修改一下。当任务不再需要执行的时候,函数返回false。下面的任务调度修改一下。
void SCH_Dispatch_Tasks(void)
{
         INT8U index;
         bool b;   //增加
         for (index = 0; index < SCH_MAX_TASKS; index++)//遍历队列找到第一个需要执行的任务并执行
         {
                 if (SCH_tasks[index].run > 0)
                 {
                        // (*SCH_tasks[index].pTask)();//执行任务
                   //改为:               
                    b =  (*SCH_tasks[index].pTask)();//   执行任务,并接收返回值                        
                    SCH_tasks[index].run = 0;
                             if(b == false || SCH_tasks[index].period == 0)  
                                     SCH_Delete_Task(index);
                 }               
        }

出0入0汤圆

 楼主| 发表于 2012-5-11 09:36:27 | 显示全部楼层
本帖最后由 xiaoziwen 于 2012-5-11 10:03 编辑
guolun 发表于 2012-5-10 22:19
你说:没必要像上次咱们讨论的再加入一种任务类型。我感觉,这个任务结构,只适合只运行一次的(priod==0 ...


我这样说是有前提的
就是可能这个led闪烁时由外界激发的
如果他只是单纯的 比如一开机闪动6s 那我的前提就不成立了 而且如果是这样的话 也完全没有必要写入调度器 初始化运行一次足以

出0入0汤圆

 楼主| 发表于 2012-5-11 09:53:54 | 显示全部楼层
Gorgon_Meducer 发表于 2012-5-10 23:43
终于有人把问题提到点子上了,所以忍不住站出来嘚瑟一下:
对于这种情况,有很多解决方法,我觉得比较好 ...

傻哥回复子的启发性就是高,醍醐灌顶!!!!!
原先我只想着是把大任务细化为不同state,然后利用switch、case来执行不同部分,你的思路却是任务的执行过程很大一,中没有switch,提高了重用性,而是通过不停变换任务的函数指针达到运行不同state的目的。
看来我对之前这个任务调度器结构中的函数指针理解还是不够深入啊。

但是我还是有问题,返回true时为何也要注销任务,仅仅改变函数指针不是来的更方便嘛。

出0入0汤圆

发表于 2012-5-11 10:04:24 | 显示全部楼层
本帖最后由 单片机玩C++ 于 2012-5-11 10:06 编辑

任务调度 最方便就是 状态机。

而状态机中最好用的就是 函数指针状态机(运行的状态就是函数指针指向的函数)。

函数指针状态机再升级,就成了C++状态机。

出0入296汤圆

发表于 2012-5-11 10:06:53 来自手机 | 显示全部楼层
本帖最后由 Gorgon_Meducer 于 2012-5-11 10:08 编辑
xiaoziwen 发表于 2012-5-11 09:53
傻哥回复子的启发性就是高,醍醐灌顶!!!!!
原先我只想着是把大任务细化为不同state,然后利用switch ...


基于三个考虑:
1. 代码复用,使得编译出来的程序体积最小,也就是轻量级的考虑
2、支持多个ready队列同时存在的情况,这样即能极大的打乱执行流,增加反追踪的安全性,又能解决当主循环访问就序队列并对其进行原子保护时,中断处理程序无法向队列中添加任务的问题。
3、卖个关子…突然想不起来了…呵呵

出0入0汤圆

发表于 2012-5-11 10:29:45 | 显示全部楼层
C++状态机Demo
  1. #include "proj_incs.h"

  2. class VSTATE_SCHED_DEMO_CLASS:public VSTATE_SCHED_CLASS
  3. {
  4. private:
  5.   
  6.   VSTATE_DeclarationIn(VSTATE_SCHED_DEMO_CLASS,VSTATE_0);     //声明一个状态VSTATE_0
  7.   VSTATE_DeclarationIn(VSTATE_SCHED_DEMO_CLASS,VSTATE_1);     //声明一个状态VSTATE_1
  8.   VSTATE_DeclarationIn(VSTATE_SCHED_DEMO_CLASS,VSTATE_2);     //声明一个状态VSTATE_2
  9.   VSTATE_DeclarationIn(VSTATE_SCHED_DEMO_CLASS,VSTATE_3);     //声明一个状态VSTATE_3
  10.   
  11. public:
  12.   void  Init(void);
  13. };


  14. VSTATE_SCHED_DEMO_CLASS  VSTATE_SCHED_DEMO;


  15. void VSTATE_SCHED_DEMO_CLASS::Init(void)
  16. {
  17.   *this<<VSTATE_OBJECT(VSTATE_0);    //设置状态机初始状态为VSTATE_0
  18. }

  19. //------------------------------------------------------------------
  20. //VSTATE_0 实现
  21. VSTATE_ImplementationIn(VSTATE_SCHED_DEMO_CLASS,VSTATE_0)  
  22. {
  23.   //ENTER: LED_0亮,LED_3灭----------------------------
  24.   if(this->GetEnterFlagAndClear())        
  25.   {
  26.     GPIO_LED_0<<IO_ON;   //LED_0亮
  27.     GPIO_LED_3<<IO_OFF;  //LED_3灭
  28.    
  29.     *this<<VSTATE_OBJECT(VSTATE_1)<<1000;  
  30.     //设置状态机下一个状态为VSTATE_1,并延时1000ms
  31.   }
  32.   
  33.   //Do:空-----------------------------------------------------------
  34.   
  35.   
  36.   //EXIT:空---------------------------------------------------------
  37.   return ;
  38. }

  39. //------------------------------------------------------------------
  40. //VSTATE_1 实现
  41. VSTATE_ImplementationIn(VSTATE_SCHED_DEMO_CLASS,VSTATE_1)  
  42. {
  43.   //ENTER: LED_1亮,LED_0灭----------------------------
  44.   if(this->GetEnterFlagAndClear())        
  45.   {
  46.     GPIO_LED_1<<IO_ON;   //LED_1亮
  47.     GPIO_LED_0<<IO_OFF;  //LED_0灭
  48.    
  49.     *this<<VSTATE_OBJECT(VSTATE_2)<<1000;  
  50.     //设置状态机下一个状态为VSTATE_2,并延时1000ms
  51.   }
  52.   
  53.   //Do: 空-----------------------------------------------------------
  54.   
  55.   
  56.   //EXIT: 空---------------------------------------------------------
  57.   return ;
  58. }


  59. //------------------------------------------------------------------
  60. //VSTATE_2 实现
  61. VSTATE_ImplementationIn(VSTATE_SCHED_DEMO_CLASS,VSTATE_2)  
  62. {
  63.   //ENTER: LED_2亮,LED_1灭----------------------------
  64.   if(this->GetEnterFlagAndClear())        
  65.   {
  66.     GPIO_LED_2<<IO_ON;   //LED_2亮
  67.     GPIO_LED_1<<IO_OFF;  //LED_1灭
  68.    
  69.     *this<<VSTATE_OBJECT(VSTATE_3)<<1000;  
  70.     //设置状态机下一个状态为VSTATE_3,并延时1000ms
  71.   }
  72.   //Do: 空-----------------------------------------------------------
  73.   
  74.   
  75.   //EXIT: 空---------------------------------------------------------
  76.   return ;
  77. }

  78. //------------------------------------------------------------------
  79. //VSTATE_3 实现
  80. VSTATE_ImplementationIn(VSTATE_SCHED_DEMO_CLASS,VSTATE_3)  
  81. {
  82.   //ENTER: LED_3亮,LED_2灭----------------------------
  83.   if(this->GetEnterFlagAndClear())        
  84.   {
  85.     GPIO_LED_3<<IO_ON;   //LED_3亮
  86.     GPIO_LED_2<<IO_OFF;  //LED_0灭
  87.    
  88.     *this<<VSTATE_OBJECT(VSTATE_0)<<1000;  
  89.     //设置状态机下一个状态为VSTATE_0,并延时1000ms
  90.   }
  91.   
  92.   //Do: 空-----------------------------------------------------------
  93.   
  94.   
  95.   //EXIT: 空---------------------------------------------------------
  96.   return ;
  97. }

  98. int main()
  99. {
  100.   VSTATE_SCHED_DEMO.Init();
  101.   while(1)
  102.   {
  103.     VSTATE_SCHED_DEMO.Do();
  104.   }
  105. }
复制代码

出0入8汤圆

发表于 2012-5-11 10:30:35 | 显示全部楼层
很好,学习中。。。

出0入296汤圆

发表于 2012-5-11 10:40:17 | 显示全部楼层
基于三个考虑:
1. 代码复用,使得编译出来的程序体积最小,也就是轻量级的考虑
2、支持多个ready队列同时存在的情况,这样即能极大的打乱执行流,增加反追踪的安全性,又能解决当主循环访问就序队列并对其进行原子保护时,中断处理程序无法向队列中添加任务的问题。
3、卖个关子…突然想不起来了…呵呵

出0入0汤圆

 楼主| 发表于 2012-5-11 11:12:54 | 显示全部楼层
Gorgon_Meducer 发表于 2012-5-11 10:40
基于三个考虑:
1. 代码复用,使得编译出来的程序体积最小,也就是轻量级的考虑
2、支持多个ready队列同时 ...

第一条是了解了
第二条我在琢磨琢磨
第三条你在琢磨琢磨
呵呵~~~

出75入4汤圆

发表于 2012-5-11 16:08:09 | 显示全部楼层
本帖最后由 taishandadi 于 2012-5-11 16:34 编辑

时间触发,调度任务,对于任务里面可以根据一些不同状态来做不同的工作。时间触发也可以是触发状态转换的条件,大状态里面可以套小状态。个人看法,不成熟。傻孩子的状态机调度器用了太多的宏,没有注释,整体思路的讲解,很多人不太容易搞懂。希望搞懂的网友加以总结,加以注释,造福广大调度器爱好者。

出75入4汤圆

发表于 2012-5-11 16:39:15 | 显示全部楼层
对于一些等待标志的到来才处理的考虑超时吧。

出0入0汤圆

发表于 2012-5-11 17:45:37 | 显示全部楼层
比较常见了,很多公司的单片机上都是跑这类小OS。再加上足状态机,就可以简单模拟进程、线程的概念。

出0入296汤圆

发表于 2012-5-11 19:42:58 | 显示全部楼层
xiaoziwen 发表于 2012-5-11 11:12
第一条是了解了
第二条我在琢磨琢磨
第三条你在琢磨琢磨

琢磨清楚了……我貌似太激动,然后把后两条写在一条了……

出0入0汤圆

发表于 2012-5-11 21:34:00 | 显示全部楼层
系统-单片机 学习一下

出0入0汤圆

发表于 2012-5-28 15:50:48 | 显示全部楼层
你好楼主,这几天研究你说的那本书,书中说的keil的软件仿真器是从哪弄的?keil自带的吗?

出0入0汤圆

发表于 2012-5-28 16:01:26 | 显示全部楼层
不错,学习学习!

出0入0汤圆

 楼主| 发表于 2012-5-28 22:30:56 | 显示全部楼层
meirenai 发表于 2012-5-28 15:50
你好楼主,这几天研究你说的那本书,书中说的keil的软件仿真器是从哪弄的?keil自带的吗?
...

没用过 不知道 呵呵

出0入0汤圆

发表于 2012-5-28 23:49:44 | 显示全部楼层
szxszx 发表于 2012-5-2 20:12
《时间触发嵌入式系统设计模式》是一本好书,现在好像买不到了

时间触发嵌入式系统设计模式.pdf免费下载:http://ishare.iask.sina.com.cn/f/13844163.html

出0入0汤圆

发表于 2012-5-29 09:40:15 | 显示全部楼层
还是没有达到这境界。

出0入0汤圆

发表于 2012-5-29 11:07:49 | 显示全部楼层
请问:那本书中说的  在合作式调度器中允许使用一个定时器作为时间触发标志,那如果我的任务里有串口中断可以吗?
利用中断来接受数据!

出0入0汤圆

 楼主| 发表于 2012-5-29 11:25:32 | 显示全部楼层
meirenai 发表于 2012-5-29 11:07
请问:那本书中说的  在合作式调度器中允许使用一个定时器作为时间触发标志,那如果我的任务里有串口中断可 ...

书后面 有专门介绍 你说的这种情况

出0入0汤圆

发表于 2012-5-29 11:33:35 | 显示全部楼层
学习了!

出0入0汤圆

发表于 2012-5-30 10:26:36 | 显示全部楼层
楼主  发现一个错误  (*^__^*) 嘻嘻……
楼主位中的程序,周期性任务的执行间隔并不是你设定的值  而是比你设定的值多一个时标周期

具体问题是:
楼主看看对不对

本帖子中包含更多资源

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

x

出0入0汤圆

 楼主| 发表于 2012-5-30 10:49:51 | 显示全部楼层
meirenai 发表于 2012-5-30 10:26
楼主  发现一个错误  (*^__^*) 嘻嘻……
楼主位中的程序,周期性任务的执行间隔并不是你设定的值  而是比你 ...

挺细心的 却是是这个问题

我再项目中第一次使用调度器时 就发现了

所以时标要-1

或者在进入中断时,先将剩余时间减去1,再做后面的判断

出0入0汤圆

发表于 2012-5-30 16:06:08 | 显示全部楼层
本帖最后由 meirenai 于 2012-5-30 16:09 编辑
xiaoziwen 发表于 2012-5-29 11:25
书后面 有专门介绍 你说的这种情况


看了看串口通讯的例子,提两个问题
1、例子中的波特率是9600  查询的时间间隔是10ms   会不会出现丢失数据的情况,如果波特率增加的话那查询频率也得相应的提高,那有效任务的CUP利用率就降低了,这种情况下使用串口中断来接收数据(调度器时标不改变甚至可以变大)岂不是更好。
2、如果有一种情况:除了调度器中断外,必须再用一个中断,会出现什么后果?想了一下,如果处理的好的话应该也不会有什么问题吧!

出0入0汤圆

发表于 2012-5-30 22:46:25 | 显示全部楼层
讲得很不错

出0入0汤圆

发表于 2012-5-31 11:01:38 | 显示全部楼层
应用的方向

出0入0汤圆

发表于 2012-5-31 13:31:52 | 显示全部楼层
不错的资料,学习了……

出0入0汤圆

发表于 2012-5-31 13:40:46 | 显示全部楼层
学习了!

出0入0汤圆

发表于 2012-5-31 13:55:21 | 显示全部楼层
高手云集,顶起!

出0入4汤圆

发表于 2012-6-19 15:06:12 | 显示全部楼层
学习           

出0入0汤圆

发表于 2012-6-19 15:48:29 | 显示全部楼层
原理都差不多,靠时间轮训

出0入0汤圆

发表于 2012-6-19 16:18:29 | 显示全部楼层
现在mark不够字数了

出0入0汤圆

发表于 2012-6-19 16:45:29 | 显示全部楼层
好贴~我终于知道怎么整这些看起来没有错误但是系统实时性差的程序了谢谢

出0入0汤圆

发表于 2012-6-19 17:07:23 | 显示全部楼层
mark mark mark makr

出0入0汤圆

发表于 2012-6-19 17:57:37 | 显示全部楼层
我认为某一次或几次的任务超时不用管他,影响不大,如果有要定时的就在中断中解决,若果是一直通信或一直AD的话,可以1ms测一个通道AD,下1ms测下一通道AD,就是10路AD也才10ms,也不影响其它,Uart的话,也可以1ms只接收2-3个字节,下1ms再接收2-3个字节,看情况而定,这样接收到一定数据后再处理

出0入0汤圆

发表于 2012-6-19 18:00:51 | 显示全部楼层
xiaoziwen 发表于 2012-5-9 15:58
对啊 保证一个时间片内能执行完任务是必须的
那有什么方法可以保证 任务崩溃不会导致系统调用出错 呢 ...

任务出错计数,出错允许循环重试假设3次,3次到还没完成就退出进行下一任务,等到下一次扫描任务到它再重试

出0入0汤圆

发表于 2012-6-19 18:05:19 | 显示全部楼层
guolun 发表于 2012-5-10 22:19
你说:没必要像上次咱们讨论的再加入一种任务类型。我感觉,这个任务结构,只适合只运行一次的(priod==0 ...

可以是一个简单的例子,假设有10个Led同时工作:
第1个一直闪烁,频率1HZ;
第2个一直闪烁,频率2HZ;
第3个一直闪烁,频率3HZ;
第4个一直闪烁,频率4HZ;
第5个一直闪烁,频率5HZ;
第6个一直闪烁,频率6HZ;
第7个一直闪烁,频率7HZ;
第8个一直闪烁,频率8HZ;
第9个一直闪烁,频率9HZ;
第10个一直闪烁,频率10HZ;
搞定这个,就会明白很多.....................
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2024-4-27 07:31

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

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