OpenEdv-开源电子网

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

百思不得其姐。求大神解疑

[复制链接]

11

主题

38

帖子

0

精华

初级会员

Rank: 2

积分
105
金钱
105
注册时间
2013-4-3
在线时间
2 小时
发表于 2014-1-23 10:16:51 | 显示全部楼层 |阅读模式
#include "includes.h"

#define  TASK_STK_SIZE   512 //任务堆栈长度

OS_STK   MyTaskStk[TASK_STK_SIZE]; //定义任务堆栈区
OS_STK   YouTaskStk[TASK_STK_SIZE]; //定义任务堆栈区
INT16S   key; //用于退出uCOS_II的键
INT8U y1=0,y2=0; //字符显示位置
BOOLEAN  ac_key; //定义信号量
char* s="原始数据"; //定义要显示的字符
void  MyTask(void *data); //声明任务
void  YouTask(void *data); //声明任务
/************************主函数*********************************/
void  main (void)
{
    OSInit( ); //初始化uCOS_II
    ac_key=1; //设置信号量初值
    PC_DOSSaveReturn( ); //保存Dos环境
    PC_VectSet(uCOS, OSCtxSw); //安装uCOS_II中断
    OSTaskCreate(MyTask, //创建任务MyTask
(void*)0, //给任务传递参数
&MyTaskStk[TASK_STK_SIZE - 1], //设置任务堆栈栈顶指针
0); //使任务的优先级别为0

    OSStart( ); //启动uCOS_II的多任务管理
}

/***********************任务MyTask********************************/

void  MyTask (void *pdata)
{
#if OS_CRITICAL_METHOD == 3
    OS_CPU_SR  cpu_sr;
#endif
INT8U err;
    pdata = pdata; 
    OS_ENTER_CRITICAL( );
    PC_VectSet(0x08, OSTickISR); //安装uCOS_II时钟中断向量
    PC_SetTickRate(OS_TICKS_PER_SEC); //设置uCOS_II时钟频率
    OS_EXIT_CRITICAL( );
    OSStatInit( ); //初始化uCOS_II的统计任务
    OSTaskCreate(YouTask, //创建任务MyTask
(void*)0, //给任务传递参数
&YouTaskStk[TASK_STK_SIZE - 1], //设置任务堆栈栈顶指针
2); //使任务的优先级别为2
    for (;;) 
    {  
        if(ac_key)
ac_key=FALSE; //使信号量无效
        s="MyTask访问共享数据s";         
PC_DispStr(5, ++y1, //显示字符的位置
s, 
DISP_BGND_BLACK+DISP_FGND_WHITE);
ac_key=TRUE; //发信号
}       
//如果按下Esc键则退出uCOS_II
if (PC_GetKey(&key) == TRUE) 
{
            if (key == 0x1B) 
    {
                PC_DOSReturn( );
            }
        }
        OSTimeDly(20); //等待20个时钟节拍
    }
}
/************************任务YouTask****************************/
void  YouTask (void *pdata)
{
#if OS_CRITICAL_METHOD == 3
    OS_CPU_SR  cpu_sr;
#endif
    INT8U err;       
    pdata = pdata; 
    for (;;) 
    { 
        if(ac_key)
{
ac_key=FALSE; //使信号量为无信号                
        s="YouTask访问共享数据s";         
PC_DispStr(28, ++y2, //显示字符串
s, 
DISP_BGND_BLACK+DISP_FGND_WHITE ); 
OSTimeSet(0); //置OSTime为0
while(OSTime<500)
{
PC_DispStr(55, y2, //显示字符串
s, 
DISP_BGND_BLACK+DISP_FGND_WHITE ); 
}
ac_key=TRUE; //发信号
}
        OSTimeDly(10); //等待10个时钟节拍
    }

这是任哲UCOS书中的一段代码,其中ac_key为一个公用信号量。想请教下大神,在这段程序中灰色的代码部分, YouTask的while循环中会发生什么?

我也在电脑上实验了一下,在YouTask的while循环中,循环次数为500个时钟节拍,而MyTask 的等待时间为20个时钟节拍,所以在 YouTask的while循环中,
MyTask每隔20个时钟节拍 也会运行。

现在就是不明白,while循环中MyTask运行了后,程序会怎样运行。

根据实验现象,while循环中发生了中断级调度->调用MyTask->MyTask运行了后继续回到中断处执行while循环。

按我的理解,while循环中发生了中断级调度->调用MyTask->运行到MyTask的延时函数->进行任务调度,运行YouTask。(这是我表面上的理解,肯定错误,但是就是不知道调用MyTask后,程序是怎么运行的,所以请高手解答一下







正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

11

主题

38

帖子

0

精华

初级会员

Rank: 2

积分
105
金钱
105
注册时间
2013-4-3
在线时间
2 小时
 楼主| 发表于 2014-1-23 11:28:31 | 显示全部楼层
只能自己盖一层楼了先。大神们,求指点啊
回复 支持 反对

使用道具 举报

13

主题

91

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
235
金钱
235
注册时间
2012-11-17
在线时间
18 小时
发表于 2014-1-23 13:12:18 | 显示全部楼层
YourTask的while循环时发生中断->Mytask,因没得到信号量(YourTask还没跳出循环执行下一句的释放信号量)又会被挂起,又发生调度->yourTask,然后继续……,直到在第499个时钟街拍的时候,YourTask跳出循环,执行 ac_key=TRUE 释放信号量,然后挂起。在第500个时钟街拍到来,Mytask刚好就绪,执行Mytask,得到信号量,就执行mytask的内容显示,之后释放信号量挂起,两个任务一起挂着等待,之后YourTask就绪,得到信号量,显示内容……

这个应该就是发生的过程,后面省略号的内容就不说下去了,一个流水账的方式希望不会让你那么难明白
回复 支持 反对

使用道具 举报

11

主题

38

帖子

0

精华

初级会员

Rank: 2

积分
105
金钱
105
注册时间
2013-4-3
在线时间
2 小时
 楼主| 发表于 2014-1-23 13:46:16 | 显示全部楼层
回复【3楼】bj2008wyou:
---------------------------------
YourTask的while循环时发生中断->Mytask,因没得到信号量(YourTask还没跳出循环执行下一句的释放信号量)又会被挂起又发生调度->yourTask,然后继续……,直到在第499个时钟街拍的时候,YourTask跳出循环,执行 ac_key=TRUE 释放信号量,然后挂起。在第500个时钟街拍到来,Mytask刚好就绪,执行Mytask,得到信号量,就执行mytask的内容显示,之后释放信号量挂起,两个任务一起挂着等待,之后YourTask就绪,得到信号量,显示内容……


由于刚刚接触UCOS所以很多地方都不太明白,感谢你的解答。你的回答中,有两个内容,还是有些疑问。

又会被挂起:
这是因为执行了Mytask的OSTimeDly(20),所以才被挂起的吗?


又发生调度->yourTask:
你的意思是在Mytask的OSTimeDly(20)中发生调度。如果是这样,那么yourTask是从第一行的程序运行?还是从yourTask->while中发生中断调度的地方开始运行?

从这个实验的现象来看,推测是从yourTask->while中发生中断调度的地方开始运行。YourTask的while循环时发生中断->Mytask又发生调度->yourTask while继续循环。
可能我不太明白的就是:Mytask又发生调度->yourTask while继续循环,为什么yourTask可以回到while中继续运行?
回复 支持 反对

使用道具 举报

13

主题

91

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
235
金钱
235
注册时间
2012-11-17
在线时间
18 小时
发表于 2014-1-23 15:48:58 | 显示全部楼层
确实,在这里myTask的挂起是因为执行Mytask的OSTimeDly(20)的缘故,在这里,调度后是返回被中断的YourTask,返回的就是被中断的 地方,也就是while里被中断的那个地方。你还是要多琢磨理解透OS的任务切换机理。

切换的时候,被中断的任务,该任务的被中断点处上下文被保存

YourTask在执行while里的时候被中断,然后这个断点的上下文就被推入该任务的堆栈并保存SP,然后SP切换到MyTask的堆栈,弹出里面的内容,就完成了任务切换。同理,当返回到YourTask,也就返回到了刚才说的中断点,然后继续往下执行。
回复 支持 反对

使用道具 举报

11

主题

38

帖子

0

精华

初级会员

Rank: 2

积分
105
金钱
105
注册时间
2013-4-3
在线时间
2 小时
 楼主| 发表于 2014-1-23 16:08:08 | 显示全部楼层
回复【5楼】bj2008wyou:
---------------------------------
恩,十分感谢你的解答。看来我看书还是不太仔细,买了本任哲的书,几天就翻完了。。。。
任哲在讲中断时说:当中断服务子程序的运行结束后,系统将会根据情况返回到被终止的任务继续运行,或者转向运行另一个具有更高优先级别的就绪任务。
但是好像没说 转向运行另一个具有更高优先级别的就绪任务后程序该怎么运行。
回复 支持 反对

使用道具 举报

13

主题

91

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
235
金钱
235
注册时间
2012-11-17
在线时间
18 小时
发表于 2014-1-23 16:48:17 | 显示全部楼层
但是好像没说 转向运行另一个具有更高优先级别的就绪任务后程序该怎么运行。

这句话不知道你想说什么。难道你是想说,中断返回后,如果去转向更高优先级的任务后,原来被中断的任务怎么运行?

这么说吧,当中断没有嵌套,也就是最后一层中断后,内核在将要退出中断,在中断的结尾处会调用函数OSIntExit(), 这个函数里面会判断任务A是不是最高优先级的,如果不是,那就转去执行更高优先级的任务,被中断的任务A依然在就绪表中而已,什么时候任务A的优先级变为最高优先级后就有机会执行了;如果任务A是最高优先级,就直接返回执行任务A。

话说看书,仅仅任哲那本书还是不够的,还是需要邵贝贝老师的那本书才好,还有个5.2版本的,钟常慰同志给源码添加中文注释,每条代码都有注释,还有函数的详细描述,参数描述等等,建议你去看下,这个你百度可以搜到的

最后是实践出真知!好吧,其实我也是菜鸟级,也没实践过多少,只是自己大部分的源码都有敲过几遍,以加深理解,然后是根据手头上的单片机,试着一点点移植
回复 支持 反对

使用道具 举报

11

主题

38

帖子

0

精华

初级会员

Rank: 2

积分
105
金钱
105
注册时间
2013-4-3
在线时间
2 小时
 楼主| 发表于 2014-1-23 17:19:48 | 显示全部楼层
回复【7楼】bj2008wyou:
---------------------------------
恩,好的,感谢你的耐心解答。从你那里学到了很多,关键是可以冷静下来再去翻书看了。
回复 支持 反对

使用道具 举报

11

主题

38

帖子

0

精华

初级会员

Rank: 2

积分
105
金钱
105
注册时间
2013-4-3
在线时间
2 小时
 楼主| 发表于 2014-1-23 17:40:52 | 显示全部楼层
void OSTickISR(void)
{
    保存处理器寄存器的值;                          //保存  YourTask的环境       
    调用OSIntEnter()或是将OSIntNesting加1;
    调用OSTimeTick();

    调用OSIntExit();                                 //其中调用OSIntCtxSw()中断级任务调度,把MyTask的运行环境弹入CPU,运行Mytask
    恢复处理器寄存器的值;                          //恢复 YouTask的运行环境
    执行中断返回指令;
}
这样总结了一下今天的问题,不知道对不对?
回复 支持 反对

使用道具 举报

13

主题

91

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
235
金钱
235
注册时间
2012-11-17
在线时间
18 小时
发表于 2014-1-23 18:05:26 | 显示全部楼层
我纠正一点,补充一点

当正在执行YourTask,而且MyTask在等待延时期满,这时第500个节拍中断到来

void OSTickISR(void) 

    保存处理器寄存器的值;            //保存YourTask的环境,PC和PSW在中断发生时就自动帮你保存进SP指向的地方,也就是YourTask的堆栈       
    调用OSIntEnter()或是将OSIntNesting加1; 
    调用OSTimeTick();                 //这里刚好MyTask的等待节拍期满,Mytask进入就绪表

    调用OSIntExit();                    //如果是最后一层中断,如果有高优先级任务就进行任务切换,这里是把MyTask堆栈指针赋值给SP 
    恢复处理器寄存器的值;            //恢复Mytask的部分运行环境(因还缺少PC和PSW,所以我才说部分)
    执行中断返回指令;                  //该指令弹出PC和PSW,至此MyTask的运行环境已经准备完毕,然后接着Mytask的上次断点处继续往下执行                 
}
回复 支持 反对

使用道具 举报

11

主题

38

帖子

0

精华

初级会员

Rank: 2

积分
105
金钱
105
注册时间
2013-4-3
在线时间
2 小时
 楼主| 发表于 2014-1-23 18:57:14 | 显示全部楼层
回复【10楼】bj2008wyou:

我纠正一点,补充一点
当正在执行YourTask,而且MyTask在等待延时期满,这时第500个节拍中断到来
void OSTickISR(void) 

    保存处理器寄存器的值;            //保存YourTask的环境,PC和PSW在中断发生时就自动帮你保存进SP指向的地方,也就是YourTask的堆栈       
    调用OSIntEnter()或是将OSIntNesting加1; 
    调用OSTimeTick();                 //这里刚好MyTask的等待节拍期满,Mytask进入就绪表
    调用OSIntExit();                    //如果是最后一层中断,如果有高优先级任务就进行任务切换,这里是把MyTask堆栈指针赋值给SP 
    恢复处理器寄存器的值;            //恢复Youtask的部分运行环境(因还缺少PC和PSW,所以我才说部分)
    执行中断返回指令;                  //该指令弹出PC和PSW,至此YouTask的运行环境已经准备完毕,然后接着YouTask的上次断点处继续往下执行                 
}
回复 支持 反对

使用道具 举报

13

主题

91

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
235
金钱
235
注册时间
2012-11-17
在线时间
18 小时
发表于 2014-1-23 19:50:21 | 显示全部楼层
如果你仔细看书,或者要移植代码,而且很在意,你就会有一天回来把你改的那些改回来的,嘿嘿
回复 支持 反对

使用道具 举报

11

主题

38

帖子

0

精华

初级会员

Rank: 2

积分
105
金钱
105
注册时间
2013-4-3
在线时间
2 小时
 楼主| 发表于 2014-1-27 11:27:05 | 显示全部楼层
回复【12楼】bj2008wyou:
----------------------------
这两天回家了。嗯,根据你的提示,我又看了一下相关的内容。
把详细的各个部分的代码流程,放到一起,又修改了一下。

void OSTickISR(void)

{

1:保存处理器寄存器的值;            //保存YourTask的环境PCPSW在中断发生时就自动帮你保存进SP指向的地方,也就是YourTask的堆栈      

2:调用OSIntEnter()或是将OSIntNesting1;

3:调用OSTimeTick();                 //这里刚好MyTask的等待节拍期满,Mytask进入就绪表

4:调用OSIntExit(

4.1:获得最高级别就绪任务的prio

4.2:此最高优先级的任务是被中断的任务?

4.3:如果不是,获得TCB的指针//得到MyTaskTCB指针

4.4:执行中断级任务切换OSIntCtxSw(

4.4.1:OSTCBCur=OSTCBHighRdy;//任务控制块的切换

4.4.2:OSPrioCur=OSPrioHighRdy

4.4.3:SP=OSTCBHighRdy->OSTCBStkPtr;//使SP指向待运行任务堆栈

4.4.4://用出栈指令把R1,R2,R3弹入CPU的通用寄存器,用汇编实现。恢复MyTask

4.4.5:RETI;//中断返回,使PC指向待运行任务.自此开始运行MyTask

)

);

5//用出栈指令把R1,R2,R3弹入CPU的通用寄存器,用汇编实现。恢复YouTask

6RETI;//中断返回,使PC指向待运行任务。自此开始运行YouTask

                     

}

回复 支持 反对

使用道具 举报

13

主题

91

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
235
金钱
235
注册时间
2012-11-17
在线时间
18 小时
发表于 2014-2-9 19:33:35 | 显示全部楼层
我是觉得还是没全对,可以的话,建议你用个开发板,去写下最基本最简单的OS内核。用2个LED,每个代表一个任务,2个任务的LED闪烁时间不同,再加个空闲任务,总共3个任务。你能成功使得这2个LED都闪起来,你就会理解了。论坛上有《建立一个属于自己的AVR的RTOS》可以参照下。
回复 支持 反对

使用道具 举报

13

主题

91

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
235
金钱
235
注册时间
2012-11-17
在线时间
18 小时
发表于 2014-2-13 18:13:40 | 显示全部楼层
不好意思,后来再仔细看下,发现自己先前的说法有错误,具体如下:
void OSTickISR(void) 
    保存处理器寄存器的值;            //保存YourTask的环境,PC和PSW在中断发生时就自动帮你保存进SP指向的地方,也就是YourTask的堆栈       
    调用OSIntEnter()或是将OSIntNesting加1; 
    调用OSTimeTick();                 //这里刚好MyTask的等待节拍期满,Mytask进入就绪表
    调用OSIntExit();                    //如果是最后一层中断,如果有高优先级任务就进行任务切换,这里是把MyTask堆栈指针赋值给SP,然后执行中断返回完成中断级任务切换 
    恢复处理器寄存器的值;  
    执行中断返回指令;               
}

上面红色字体部分其实已经被包括在 OSIntExit() 里的最后几行语句,所以红色字体部分应该去掉。 执行完OSIntExit()后,后面的语句是没机会执行的,因为OSIntEixt()里面最后一句已经是执行中断返回 reti 了,这也就是为什么强调 OSintExit()一定要放在中断程序的最末尾!

至于LZ自己重新注释的,肯定地说,还是有错误的。






回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165536
金钱
165536
注册时间
2010-12-1
在线时间
2117 小时
发表于 2014-2-13 23:22:27 | 显示全部楼层
回复【15楼】bj2008wyou:
---------------------------------
高手啊.
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

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

本版积分规则



关闭

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

正点原子公众号

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

GMT+8, 2025-6-9 23:31

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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